From e0ab7bf6c16953e5cf688b7e4bfffe99e937c072 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 11 Dec 2021 19:10:02 +1000 Subject: [PATCH 001/712] =?UTF-8?q?gowin:=20BUGFIX.=20Place=20the=20ALU=20?= =?UTF-8?q?head=20in=20sli=D1=81e=200=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: YRabbit --- gowin/pack.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gowin/pack.cc b/gowin/pack.cc index 2616a913f3..4c6e28ca55 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -89,6 +89,9 @@ static void pack_alus(Context *ctx) } std::unique_ptr packed_head = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "_HEAD_ALULC"); + // Head is always SLICE0 + packed_head->constr_z = 0; + packed_head->constr_abs_z = true; if (ctx->verbose) { log_info("packed ALU head into %s. CIN net is %s\n", ctx->nameOf(packed_head.get()), ctx->nameOf(cin_netId)); From df061b1a9c844f9f2d14b658edbaba190b8b8fe1 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 11 Dec 2021 19:07:30 +0000 Subject: [PATCH 002/712] mistral: Add 'tools' dir to include path Signed-off-by: gatecat --- .github/workflows/mistral_ci.yml | 2 +- gui/mistral/family.cmake | 2 +- mistral/family.cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index 969005435a..afbb2ffd96 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: ea9691a720cc5695eab2fc580172be5c9e592760 + MISTRAL_REVISION: ecd413421b3b559fc63db13da30275fcd5cd3881 run: | source ./.github/ci/build_mistral.sh get_dependencies diff --git a/gui/mistral/family.cmake b/gui/mistral/family.cmake index c78ff6c1e4..072fa5cdc3 100644 --- a/gui/mistral/family.cmake +++ b/gui/mistral/family.cmake @@ -1 +1 @@ -target_include_directories(gui_mistral PRIVATE ${MISTRAL_ROOT}/libmistral ${CMAKE_BINARY_DIR}/libmistral) +target_include_directories(gui_mistral PRIVATE ${MISTRAL_ROOT}/libmistral ${CMAKE_BINARY_DIR}/libmistral ${CMAKE_BINARY_DIR}/tools) diff --git a/mistral/family.cmake b/mistral/family.cmake index d2da396ae9..dcbab4ff10 100644 --- a/mistral/family.cmake +++ b/mistral/family.cmake @@ -8,6 +8,6 @@ add_subdirectory(${MISTRAL_ROOT}/libmistral ${CMAKE_CURRENT_BINARY_DIR}/libmistr find_package(LibLZMA REQUIRED) foreach(family_target ${family_targets}) - target_include_directories(${family_target} PRIVATE ${MISTRAL_ROOT}/libmistral ${CMAKE_CURRENT_BINARY_DIR}/libmistral ${LIBLZMA_INCLUDE_DIRS}) + target_include_directories(${family_target} PRIVATE ${MISTRAL_ROOT}/libmistral ${CMAKE_CURRENT_BINARY_DIR}/tools ${CMAKE_CURRENT_BINARY_DIR}/libmistral ${LIBLZMA_INCLUDE_DIRS}) target_link_libraries(${family_target} PRIVATE mistral ${LIBLZMA_LIBRARIES}) endforeach() From 3c8af04ca587aa8056bbc583e1ecd0fae49e6276 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 11 Dec 2021 19:25:59 +0000 Subject: [PATCH 003/712] router2: Error instead of hang in case of reservation conflicts Signed-off-by: gatecat --- common/router2.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/router2.cc b/common/router2.cc index b93e7b35bc..051aa7f8d4 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -464,6 +464,9 @@ struct Router2 if (ctx->debug) log(" %s\n", ctx->nameOfWire(cursor)); did_something |= (wd.reserved_net != net->udata); + if (wd.reserved_net != -1 && wd.reserved_net != net->udata) + log_error("attempting to reserve wire '%s' for nets '%s' and '%s'\n", ctx->nameOfWire(cursor), + ctx->nameOf(nets_by_udata.at(wd.reserved_net)), ctx->nameOf(net)); wd.reserved_net = net->udata; if (cursor == src) break; From 35feb7ebbaa23e159d1ea5ca15da61e78aadaf57 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 12 Dec 2021 13:02:39 +0000 Subject: [PATCH 004/712] clangformat Signed-off-by: gatecat --- nexus/main.cc | 3 ++- nexus/pack.cc | 26 +++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/nexus/main.cc b/nexus/main.cc index 667b6d80dd..9fec8d5e80 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -52,7 +52,8 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("pdc", po::value(), "physical constraints file"); specific.add_options()("no-post-place-opt", "disable post-place repacking (debugging use only)"); specific.add_options()("no-pack-lutff", "disable packing (clustering) LUTs and FFs together"); - specific.add_options()("carry-lutff-ratio", po::value(), "ratio of FFs to be added to carry-chain LUT clusters"); + specific.add_options()("carry-lutff-ratio", po::value(), + "ratio of FFs to be added to carry-chain LUT clusters"); return specific; } diff --git a/nexus/pack.cc b/nexus/pack.cc index 0c771e8250..c3d9cba286 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2304,7 +2304,8 @@ struct NexusPacker } } - FFControlSet gather_ff_settings(CellInfo* cell) { + FFControlSet gather_ff_settings(CellInfo *cell) + { NPNR_ASSERT(cell->type == id_OXIDE_FF); FFControlSet ctrlset; @@ -2321,7 +2322,8 @@ struct NexusPacker return ctrlset; } - void pack_lutffs () { + void pack_lutffs() + { log_info("Inferring LUT+FF pairs...\n"); float carry_ratio = 1.0f; @@ -2336,7 +2338,7 @@ struct NexusPacker dict cluster_ffinfo; size_t num_comb = 0; - size_t num_ff = 0; + size_t num_ff = 0; size_t num_pair = 0; size_t num_glue = 0; @@ -2361,13 +2363,11 @@ struct NexusPacker } // Check if the driver is a LUT and the direct connection is from F - CellInfo* lut = di->driver.cell; + CellInfo *lut = di->driver.cell; if (lut->type != id_OXIDE_COMB) { continue; } - if (di->driver.port != id_F && - di->driver.port != id_OFX) - { + if (di->driver.port != id_F && di->driver.port != id_OFX) { continue; } @@ -2422,7 +2422,7 @@ struct NexusPacker // No order not to make too large carry clusters pack only the // given fraction of FFs there. - if(str_or_default(lut->params, id_MODE, "LOGIC") == "CCU2") { + if (str_or_default(lut->params, id_MODE, "LOGIC") == "CCU2") { float r = (float)(ctx->rng() % 1000) * 1e-3f; if (r > carry_ratio) { continue; @@ -2430,7 +2430,7 @@ struct NexusPacker } // Get the cluster root - CellInfo* root = ctx->cells.at(lut->cluster).get(); + CellInfo *root = ctx->cells.at(lut->cluster).get(); // Constrain the FF relative to the LUT ff->cluster = root->cluster; @@ -2462,12 +2462,8 @@ struct NexusPacker } // Print statistics - log_info(" Created %zu LUT+FF pairs and extended %zu clusters using total %zu FFs and %zu LUTs\n", - num_pair, - num_glue, - num_ff, - num_comb - ); + log_info(" Created %zu LUT+FF pairs and extended %zu clusters using total %zu FFs and %zu LUTs\n", num_pair, + num_glue, num_ff, num_comb); } explicit NexusPacker(Context *ctx) : ctx(ctx) {} From 78905c3ecf0cc283a79f064bf7e50aa6cdab2743 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 11 Dec 2021 19:38:14 +0000 Subject: [PATCH 005/712] mistral: DATAIN and DATAOUT of GPIO have swapped Signed-off-by: gatecat --- mistral/io.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mistral/io.cc b/mistral/io.cc index 3a72b001df..dab3672e28 100644 --- a/mistral/io.cc +++ b/mistral/io.cc @@ -31,9 +31,9 @@ void Arch::create_gpio(int x, int y) BelId bel = add_bel(x, y, id(stringf("IO[%d]", z)), id_MISTRAL_IO); add_bel_pin(bel, id_PAD, PORT_INOUT, pad); // FIXME: is the port index of zero always correct? - add_bel_pin(bel, id_I, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAIN, 0)); + add_bel_pin(bel, id_I, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)); add_bel_pin(bel, id_OE, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::OEIN, 0)); - add_bel_pin(bel, id_O, PORT_OUT, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)); + add_bel_pin(bel, id_O, PORT_OUT, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAIN, 0)); bel_data(bel).block_index = z; } } From 61597e14a7fe488fcfc223d3109da1d30a2ec71b Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 12 Dec 2021 13:55:06 +0000 Subject: [PATCH 006/712] mistral: Bump CI version Signed-off-by: gatecat --- .github/workflows/mistral_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index afbb2ffd96..7cf71621aa 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: ecd413421b3b559fc63db13da30275fcd5cd3881 + MISTRAL_REVISION: 0edeca112dda9bd463125feb869ddb7511d1acd9 run: | source ./.github/ci/build_mistral.sh get_dependencies From 80dd442412982af1901dcdf707ddc68379c20e0c Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 10 Dec 2021 15:46:15 +0800 Subject: [PATCH 007/712] ecp5: Use a vector rather than dict This improves router1 performance vs the default dict Using it for wire2net, pip2net, wire_fanout --- ecp5/arch.cc | 21 +++++++++++ ecp5/arch.h | 97 ++++++++++++++++++++++++++++++++++++++++++------- ecp5/archdefs.h | 2 + 3 files changed, 106 insertions(+), 14 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 34bdfa1b7f..ec64fb8284 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -123,6 +123,27 @@ Arch::Arch(ArchArgs args) : args(args) y_ids.push_back(y_id); id_to_y[y_id] = i; } + + wire_tile_vecidx.resize(chip_info->num_tiles, -1); + int n_wires = 0; + for (auto e : getWires()) { + if (e.index == 0) { + wire_tile_vecidx.at(e.location.y * chip_info->width + e.location.x) = n_wires; + } + n_wires++; + } + wire2net.resize(n_wires, nullptr); + wire_fanout.resize(n_wires, 0); + + pip_tile_vecidx.resize(chip_info->num_tiles, -1); + int n_pips = 0; + for (auto e : getPips()) { + if (e.index == 0) { + pip_tile_vecidx.at(e.location.y * chip_info->width + e.location.x) = n_pips; + } + n_pips++; + } + pip2net.resize(n_pips, nullptr); } // ----------------------------------------------------------------------- diff --git a/ecp5/arch.h b/ecp5/arch.h index 1648c96282..585ae01dd3 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -447,7 +447,17 @@ struct Arch : BaseArch mutable dict pip_by_name; std::vector bel_to_cell; - dict wire_fanout; + + // faster replacements for base_pip2net, base_wire2net + // indexed by get_pip_vecidx() + std::vector pip2net; + // indexed by get_wire_vecidx() + std::vector wire2net; + std::vector wire_fanout; + // We record the index=0 offset into pip2net for each tile, allowing us to + // calculate any PipId's offset from pip.index and pip.location + std::vector pip_tile_vecidx; + std::vector wire_tile_vecidx; // fast access to X and Y IdStrings for building object names std::vector x_ids, y_ids; @@ -614,21 +624,46 @@ struct Arch : BaseArch uint32_t getWireChecksum(WireId wire) const override { return wire.index; } + uint32_t get_wire_vecidx(const WireId & e) const { + uint32_t tile = e.location.y * chip_info->width + e.location.x; + int32_t base = wire_tile_vecidx.at(tile); + NPNR_ASSERT(base != -1); + int32_t i = base + e.index; + return i; + } + + void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) override + { + NPNR_ASSERT(wire != WireId()); + auto &w2n_entry = wire2net.at(get_wire_vecidx(wire)); + NPNR_ASSERT(w2n_entry == nullptr); + net->wires[wire].pip = PipId(); + net->wires[wire].strength = strength; + w2n_entry = net; + this->refreshUiWire(wire); + } void unbindWire(WireId wire) override { NPNR_ASSERT(wire != WireId()); - NPNR_ASSERT(base_wire2net[wire] != nullptr); + auto &w2n_entry = wire2net.at(get_wire_vecidx(wire)); + NPNR_ASSERT(w2n_entry != nullptr); - auto &net_wires = base_wire2net[wire]->wires; + auto &net_wires = w2n_entry->wires; auto it = net_wires.find(wire); NPNR_ASSERT(it != net_wires.end()); + auto pip = it->second.pip; - // As well as the default rules; need to handle fanout counting if (pip != PipId()) { - wire_fanout[getPipSrcWire(pip)]--; + pip2net.at(get_pip_vecidx(pip)) = nullptr; + wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]--; } - BaseArch::unbindWire(wire); + + net_wires.erase(it); + w2n_entry = nullptr; + this->refreshUiWire(wire); } + virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; } + NetInfo *getBoundWireNet(WireId wire) const override { return wire2net.at(get_wire_vecidx(wire)); } DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0); } @@ -667,17 +702,54 @@ struct Arch : BaseArch uint32_t getPipChecksum(PipId pip) const override { return pip.index; } + uint32_t get_pip_vecidx(const PipId & e) const { + uint32_t tile = e.location.y * chip_info->width + e.location.x; + int32_t base = pip_tile_vecidx.at(tile); + NPNR_ASSERT(base != -1); + int32_t i = base + e.index; + return i; + } + void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) override { - wire_fanout[getPipSrcWire(pip)]++; - BaseArch::bindPip(pip, net, strength); + NPNR_ASSERT(pip != PipId()); + wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]++; + + auto &p2n_entry = pip2net.at(get_pip_vecidx(pip)); + NPNR_ASSERT(p2n_entry == nullptr); + p2n_entry = net; + + WireId dst = this->getPipDstWire(pip); + auto &w2n_entry = wire2net.at(get_wire_vecidx(dst)); + NPNR_ASSERT(w2n_entry == nullptr); + w2n_entry = net; + net->wires[dst].pip = pip; + net->wires[dst].strength = strength; } void unbindPip(PipId pip) override { - wire_fanout[getPipSrcWire(pip)]--; - BaseArch::unbindPip(pip); + NPNR_ASSERT(pip != PipId()); + wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]--; + + auto &p2n_entry = pip2net.at(get_pip_vecidx(pip)); + NPNR_ASSERT(p2n_entry != nullptr); + WireId dst = this->getPipDstWire(pip); + + auto &w2n_entry = wire2net.at(get_wire_vecidx(dst)); + NPNR_ASSERT(w2n_entry != nullptr); + w2n_entry = nullptr; + + p2n_entry->wires.erase(dst); + p2n_entry = nullptr; + } + bool checkPipAvail(PipId pip) const override { return getBoundPipNet(pip) == nullptr; } + bool checkPipAvailForNet(PipId pip, NetInfo *net) const override + { + NetInfo *bound_net = getBoundPipNet(pip); + return bound_net == nullptr || bound_net == net; } + NetInfo *getBoundPipNet(PipId pip) const override { return pip2net.at(get_pip_vecidx(pip)); } AllPipRange getPips() const override { @@ -713,10 +785,7 @@ struct Arch : BaseArch DelayQuad getPipDelay(PipId pip) const override { NPNR_ASSERT(pip != PipId()); - int fanout = 0; - auto fnd_fanout = wire_fanout.find(getPipSrcWire(pip)); - if (fnd_fanout != wire_fanout.end()) - fanout = fnd_fanout->second; + int fanout = wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]; delay_t min_dly = speed_grade->pip_classes[loc_info(pip)->pip_data[pip.index].timing_class].min_base_delay + fanout * speed_grade->pip_classes[loc_info(pip)->pip_data[pip.index].timing_class].min_fanout_adder; diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index 5921954463..80e7810c18 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -156,6 +156,8 @@ struct ArchNetInfo typedef IdString ClusterId; +struct NetInfo; + struct ArchCellInfo : BaseClusterInfo { struct From fc5b34254f47ef76150ec9f9edc85cda20681656 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 10 Dec 2021 22:10:33 +0800 Subject: [PATCH 008/712] ecp5: Keep "visited" local Otherwise it keeps growing boundless and slows down small arcs --- common/router1.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index c8585b0b7c..0ff2bedd76 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -108,7 +108,6 @@ struct Router1 dict> arc_to_wires; pool queued_arcs; - dict visited; std::priority_queue, QueuedWire::Greater> queue; dict wireScores; @@ -503,7 +502,7 @@ struct Router1 std::priority_queue, QueuedWire::Greater> new_queue; queue.swap(new_queue); } - visited.clear(); + dict visited; // A* main loop From 0dafcc44ffcbe747babdadbb33d4dc140b5ea87c Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 12 Dec 2021 18:48:39 +0000 Subject: [PATCH 009/712] router2: Improve reservation debug logging Signed-off-by: gatecat --- common/router2.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/router2.cc b/common/router2.cc index 051aa7f8d4..d713cce288 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -453,12 +453,14 @@ struct Router2 { bool did_something = false; WireId src = ctx->getNetinfoSourceWire(net); - for (auto sink : ctx->getNetinfoSinkWires(net, net->users.at(i))) { + auto &usr = net->users.at(i); + for (auto sink : ctx->getNetinfoSinkWires(net, usr)) { pool rsv; WireId cursor = sink; bool done = false; if (ctx->debug) - log("reserving wires for arc %d of net %s\n", int(i), ctx->nameOf(net)); + log("reserving wires for arc %d (%s.%s) of net %s\n", int(i), ctx->nameOf(usr.cell), + ctx->nameOf(usr.port), ctx->nameOf(net)); while (!done) { auto &wd = wire_data(cursor); if (ctx->debug) From a933f82845e489af7f5e209d2d43ff79b4c521ca Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 12 Dec 2021 18:49:37 +0000 Subject: [PATCH 010/712] clangformat Signed-off-by: gatecat --- ecp5/arch.h | 10 ++++++---- gowin/pack.cc | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ecp5/arch.h b/ecp5/arch.h index 585ae01dd3..a570637954 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -450,9 +450,9 @@ struct Arch : BaseArch // faster replacements for base_pip2net, base_wire2net // indexed by get_pip_vecidx() - std::vector pip2net; + std::vector pip2net; // indexed by get_wire_vecidx() - std::vector wire2net; + std::vector wire2net; std::vector wire_fanout; // We record the index=0 offset into pip2net for each tile, allowing us to // calculate any PipId's offset from pip.index and pip.location @@ -624,7 +624,8 @@ struct Arch : BaseArch uint32_t getWireChecksum(WireId wire) const override { return wire.index; } - uint32_t get_wire_vecidx(const WireId & e) const { + uint32_t get_wire_vecidx(const WireId &e) const + { uint32_t tile = e.location.y * chip_info->width + e.location.x; int32_t base = wire_tile_vecidx.at(tile); NPNR_ASSERT(base != -1); @@ -702,7 +703,8 @@ struct Arch : BaseArch uint32_t getPipChecksum(PipId pip) const override { return pip.index; } - uint32_t get_pip_vecidx(const PipId & e) const { + uint32_t get_pip_vecidx(const PipId &e) const + { uint32_t tile = e.location.y * chip_info->width + e.location.x; int32_t base = pip_tile_vecidx.at(tile); NPNR_ASSERT(base != -1); diff --git a/gowin/pack.cc b/gowin/pack.cc index 4c6e28ca55..cb63f1c95a 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -89,9 +89,9 @@ static void pack_alus(Context *ctx) } std::unique_ptr packed_head = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "_HEAD_ALULC"); - // Head is always SLICE0 - packed_head->constr_z = 0; - packed_head->constr_abs_z = true; + // Head is always SLICE0 + packed_head->constr_z = 0; + packed_head->constr_abs_z = true; if (ctx->verbose) { log_info("packed ALU head into %s. CIN net is %s\n", ctx->nameOf(packed_head.get()), ctx->nameOf(cin_netId)); From 90b0e90bbe2fb923154096a8be3660b5e3f4cbb0 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 12 Dec 2021 16:52:01 +0800 Subject: [PATCH 011/712] ecp5: Reduce some chipdb fields sizes This reduces the final binary size by ~7 MB for 85k --- ecp5/arch.h | 11 +++++------ ecp5/trellis_import.py | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ecp5/arch.h b/ecp5/arch.h index a570637954..e7bf64fe81 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -54,11 +54,10 @@ NPNR_PACKED_STRUCT(struct BelPortPOD { NPNR_PACKED_STRUCT(struct PipInfoPOD { LocationPOD rel_src_loc, rel_dst_loc; - int32_t src_idx, dst_idx; - int32_t timing_class; - int16_t tile_type; + int16_t src_idx, dst_idx; + int16_t timing_class; + int8_t tile_type; int8_t pip_type; - int8_t padding_0; }); NPNR_PACKED_STRUCT(struct PipLocatorPOD { @@ -68,8 +67,8 @@ NPNR_PACKED_STRUCT(struct PipLocatorPOD { NPNR_PACKED_STRUCT(struct WireInfoPOD { RelPtr name; - int32_t type; - int32_t tile_wire; + int16_t type; + int16_t tile_wire; RelSlice pips_uphill, pips_downhill; RelSlice bel_pins; }); diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 945f6e9303..2e76fb7453 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -196,12 +196,15 @@ def s(self, s, comment): print("str |%s| %s" % (s, comment)) def u8(self, v, comment): + assert -128 <= int(v) <= 127 if comment is None: print("u8 %d" % (v,)) else: print("u8 %d %s" % (v, comment)) def u16(self, v, comment): + # is actually used as signed 16 bit + assert -32768 <= int(v) <= 32767 if comment is None: print("u16 %d" % (v,)) else: @@ -413,17 +416,16 @@ def get_wire_name(arc_loctype, rel, idx): for arc in loctype.arcs: write_loc(arc.srcWire.rel, "src") write_loc(arc.sinkWire.rel, "dst") - bba.u32(arc.srcWire.id, "src_idx") - bba.u32(arc.sinkWire.id, "dst_idx") + bba.u16(arc.srcWire.id, "src_idx") + bba.u16(arc.sinkWire.id, "dst_idx") src_name = get_wire_name(idx, arc.srcWire.rel, arc.srcWire.id) snk_name = get_wire_name(idx, arc.sinkWire.rel, arc.sinkWire.id) - bba.u32(get_pip_class(src_name, snk_name), "timing_class") - bba.u16(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type") + bba.u16(get_pip_class(src_name, snk_name), "timing_class") + bba.u8(get_tiletype_index(ddrg.to_str(arc.tiletype)), "tile_type") cls = arc.cls if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name: cls = 2 bba.u8(cls, "pip_type") - bba.u8(0, "padding") if len(loctype.wires) > 0: for wire_idx in range(len(loctype.wires)): wire = loctype.wires[wire_idx] @@ -447,11 +449,11 @@ def get_wire_name(arc_loctype, rel, idx): for wire_idx in range(len(loctype.wires)): wire = loctype.wires[wire_idx] bba.s(ddrg.to_str(wire.name), "name") - bba.u32(constids[wire_type(ddrg.to_str(wire.name))], "type") + bba.u16(constids[wire_type(ddrg.to_str(wire.name))], "type") if ("TILE_WIRE_" + ddrg.to_str(wire.name)) in gfx_wire_ids: - bba.u32(gfx_wire_ids["TILE_WIRE_" + ddrg.to_str(wire.name)], "tile_wire") + bba.u16(gfx_wire_ids["TILE_WIRE_" + ddrg.to_str(wire.name)], "tile_wire") else: - bba.u32(0, "tile_wire") + bba.u16(0, "tile_wire") bba.r_slice("loc%d_wire%d_uppips" % (idx, wire_idx) if len(wire.arcsUphill) > 0 else None, len(wire.arcsUphill), "pips_uphill") bba.r_slice("loc%d_wire%d_downpips" % (idx, wire_idx) if len(wire.arcsDownhill) > 0 else None, len(wire.arcsDownhill), "pips_downhill") bba.r_slice("loc%d_wire%d_belpins" % (idx, wire_idx) if len(wire.belPins) > 0 else None, len(wire.belPins), "bel_pins") From f36188f2e18783ea692f8b6132712a0d1da6b6b7 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 13 Dec 2021 16:04:45 +0000 Subject: [PATCH 012/712] ecp5: LUT permutation support Signed-off-by: gatecat --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- ecp5/arch.cc | 4 +++ ecp5/arch.h | 50 +++++++++++++++++++++++++++-- ecp5/archdefs.h | 1 + ecp5/bitstream.cc | 58 ++++++++++++++++++++++++++++++++-- ecp5/main.cc | 3 ++ ecp5/pack.cc | 4 ++- ecp5/trellis_import.py | 4 ++- 8 files changed, 118 insertions(+), 8 deletions(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index 2e39058d09..4f229d2d38 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -48,7 +48,7 @@ RUN set -e -x ;\ cd /usr/local/src ;\ git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\ cd prjtrellis ;\ - git reset --hard 210a0a72757d57b278ac7397ae6b14729f149b10 ;\ + git reset --hard 7239331d5463321d4864164f320beef67310f1e5 ;\ cd libtrellis ;\ cmake . ;\ make -j $(nproc) ;\ diff --git a/ecp5/arch.cc b/ecp5/arch.cc index ec64fb8284..95a2768285 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -144,6 +144,8 @@ Arch::Arch(ArchArgs args) : args(args) n_pips++; } pip2net.resize(n_pips, nullptr); + + lutperm_allowed.resize(chip_info->width * chip_info->height * 4); } // ----------------------------------------------------------------------- @@ -625,6 +627,8 @@ bool Arch::route() { std::string router = str_or_default(settings, id("router"), defaultRouter); + disable_router_lutperm = getCtx()->setting("arch.disable_router_lutperm", false); + setup_wire_locations(); route_ecp5_globals(getCtx()); assignArchInfo(); diff --git a/ecp5/arch.h b/ecp5/arch.h index e7bf64fe81..51a919bb41 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -58,8 +58,15 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD { int16_t timing_class; int8_t tile_type; int8_t pip_type; + int16_t lutperm_flags; + int16_t padding; }); +inline bool is_lutperm_pip(int16_t flags) { return flags & 0x4000; } +inline uint8_t lutperm_lut(int16_t flags) { return (flags >> 4) & 0x7; } +inline uint8_t lutperm_out(int16_t flags) { return (flags >> 2) & 0x3; } +inline uint8_t lutperm_in(int16_t flags) { return flags & 0x3; } + NPNR_PACKED_STRUCT(struct PipLocatorPOD { LocationPOD rel_loc; int32_t index; @@ -446,6 +453,14 @@ struct Arch : BaseArch mutable dict pip_by_name; std::vector bel_to_cell; + enum class LutPermRule + { + NONE, + CARRY, + ALL, + }; + std::vector lutperm_allowed; + bool disable_router_lutperm = false; // faster replacements for base_pip2net, base_wire2net // indexed by get_pip_vecidx() @@ -509,6 +524,12 @@ struct Arch : BaseArch return (bel.location.y * chip_info->width + bel.location.x) * max_loc_bels + bel.index; } + int get_slice_index(int x, int y, int slice) const + { + NPNR_ASSERT(slice >= 0 && slice < 4); + return (y * chip_info->width + x) * 4 + slice; + } + void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override { NPNR_ASSERT(bel != BelId()); @@ -517,6 +538,11 @@ struct Arch : BaseArch bel_to_cell[idx] = cell; cell->bel = bel; cell->belStrength = strength; + if (getBelType(bel) == id_TRELLIS_SLICE) { + lutperm_allowed.at(get_slice_index(bel.location.x, bel.location.y, getBelLocation(bel).z)) = + (cell->sliceInfo.is_memory ? LutPermRule::NONE + : (cell->sliceInfo.is_carry ? LutPermRule::CARRY : LutPermRule::ALL)); + } refreshUiBel(bel); } @@ -744,11 +770,31 @@ struct Arch : BaseArch p2n_entry->wires.erase(dst); p2n_entry = nullptr; } - bool checkPipAvail(PipId pip) const override { return getBoundPipNet(pip) == nullptr; } + bool is_pip_blocked(PipId pip) const + { + auto &pip_data = loc_info(pip)->pip_data[pip.index]; + int lp = pip_data.lutperm_flags; + if (is_lutperm_pip(lp)) { + if (disable_router_lutperm) + return true; + auto rule = lutperm_allowed.at(get_slice_index(pip.location.x, pip.location.y, lutperm_lut(lp) / 2)); + if (rule == LutPermRule::NONE) { + // Permutation not allowed + return true; + } else if (rule == LutPermRule::CARRY) { + // Can swap A/B and C/D only + int i = lutperm_out(lp), j = lutperm_in(lp); + if ((i / 2) != (j / 2)) + return true; + } + } + return false; + } + bool checkPipAvail(PipId pip) const override { return (getBoundPipNet(pip) == nullptr) && !is_pip_blocked(pip); } bool checkPipAvailForNet(PipId pip, NetInfo *net) const override { NetInfo *bound_net = getBoundPipNet(pip); - return bound_net == nullptr || bound_net == net; + return (bound_net == nullptr || bound_net == net) && !is_pip_blocked(pip); } NetInfo *getBoundPipNet(PipId pip) const override { return pip2net.at(get_pip_vecidx(pip)); } diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index 80e7810c18..dd260a3e87 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -165,6 +165,7 @@ struct ArchCellInfo : BaseClusterInfo bool using_dff; bool has_l6mux; bool is_carry; + bool is_memory; IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; int sd0, sd1; } sliceInfo; diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 338c4f201a..de6c711ece 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -522,6 +522,55 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) cc.tiles[tile].add_arc(sink, source); } +static unsigned permute_lut(Context *ctx, CellInfo *cell, pool &used_phys_pins, int k, unsigned orig_init) +{ + std::array, 4> phys_to_log; + const std::array ports{k ? id_A1 : id_A0, k ? id_B1 : id_B0, k ? id_C1 : id_C0, k ? id_D1 : id_D0}; + for (unsigned i = 0; i < 4; i++) { + WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]); + for (PipId pip : ctx->getPipsUphill(pin_wire)) { + if (!ctx->getBoundPipNet(pip)) + continue; + unsigned lp = ctx->loc_info(pip)->pip_data[pip.index].lutperm_flags; + if (!is_lutperm_pip(lp)) { // non-permuting + phys_to_log[i].push_back(i); + } else { // permuting + unsigned from_pin = lutperm_in(lp); + unsigned to_pin = lutperm_out(lp); + NPNR_ASSERT(to_pin == i); + phys_to_log[from_pin].push_back(i); + } + } + } + for (unsigned i = 0; i < 4; i++) + if (!phys_to_log.at(i).empty()) + used_phys_pins.insert(ports.at(i)); + if (cell->sliceInfo.is_carry) { + // Insert dummy entries to ensure we keep the split between the two halves of a CCU2 + for (unsigned i = 0; i < 4; i++) { + if (!phys_to_log.at(i).empty()) + continue; + for (unsigned j = 2 * (i / 2); j < 2 * ((i / 2) + 1); j++) { + if (!ctx->getBoundWireNet(ctx->getBelPinWire(cell->bel, ports[j]))) + phys_to_log.at(i).push_back(j); + } + } + } + unsigned permuted_init = 0; + for (unsigned i = 0; i < 16; i++) { + unsigned log_idx = 0; + for (unsigned j = 0; j < 4; j++) { + if ((i >> j) & 0x1) { + for (auto log_pin : phys_to_log[j]) + log_idx |= (1 << log_pin); + } + } + if ((orig_init >> log_idx) & 0x1) + permuted_init |= (1 << i); + } + return permuted_init; +} + static std::vector parse_config_str(const Property &p, int length) { std::vector word; @@ -787,12 +836,15 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } BelId bel = ci->bel; if (ci->type == ctx->id("TRELLIS_SLICE")) { + pool used_phys_pins; std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get(); int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); - cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16)); - cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16)); + cc.tiles[tname].add_word(slice + ".K0.INIT", + int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16)); + cc.tiles[tname].add_word(slice + ".K1.INIT", + int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16)); cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC")); cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, ctx->id("REG0_SD"), "0")); @@ -854,7 +906,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex // Tie unused inputs high for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) { - if (ci->ports.find(input) == ci->ports.end() || ci->ports.at(input).net == nullptr) { + if (!used_phys_pins.count(input)) { cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1"); } } diff --git a/ecp5/main.cc b/ecp5/main.cc index f3861149d4..1864548bac 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -85,6 +85,7 @@ po::options_description ECP5CommandHandler::getArchOptions() specific.add_options()( "out-of-context", "disable IO buffer insertion and global promotion/routing, for building pre-routed blocks (experimental)"); + specific.add_options()("disable-router-lutperm", "don't allow the router to permute LUT inputs"); return specific; } @@ -255,6 +256,8 @@ std::unique_ptr ECP5CommandHandler::createContext(dictsettings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed); if (vm.count("out-of-context")) ctx->settings[ctx->id("arch.ooc")] = 1; + if (vm.count("disable-router-lutperm")) + ctx->settings[ctx->id("arch.disable_router_lutperm")] = 1; return ctx; } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index cbf882a88a..85d92336c2 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -3253,7 +3253,9 @@ void Arch::assignArchInfo() ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK")); ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR")); ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); - ci->sliceInfo.is_carry = str_or_default(ci->params, id("MODE"), "LOGIC") == "CCU2"; + std::string mode = str_or_default(ci->params, id("MODE"), "LOGIC"); + ci->sliceInfo.is_carry = (mode == "CCU2"); + ci->sliceInfo.is_memory = (mode == "DPRAM" || mode == "RAMW"); ci->sliceInfo.sd0 = std::stoi(str_or_default(ci->params, id("REG0_SD"), "0")); ci->sliceInfo.sd1 = std::stoi(str_or_default(ci->params, id("REG1_SD"), "0")); ci->sliceInfo.has_l6mux = false; diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index 2e76fb7453..a586db7b90 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -426,6 +426,8 @@ def get_wire_name(arc_loctype, rel, idx): if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name: cls = 2 bba.u8(cls, "pip_type") + bba.u16(arc.lutperm_flags, "lutperm_flags") + bba.u16(0, "padding") if len(loctype.wires) > 0: for wire_idx in range(len(loctype.wires)): wire = loctype.wires[wire_idx] @@ -623,7 +625,7 @@ def main(): # print("Initialising chip...") chip = pytrellis.Chip(dev_names[args.device]) # print("Building routing graph...") - ddrg = pytrellis.make_dedup_chipdb(chip) + ddrg = pytrellis.make_dedup_chipdb(chip, include_lutperm_pips=True) max_row = chip.get_max_row() max_col = chip.get_max_col() process_timing_data() From fdf26e698f751c92445a0323d7892869598ca9ce Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 14 Dec 2021 14:09:27 +1000 Subject: [PATCH 013/712] gowin: Fix spelling of messages Signed-off-by: YRabbit --- gowin/arch.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 6641df72ff..99e0ce6115 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -705,7 +705,7 @@ Arch::Arch(ArchArgs args) : args(args) } } if (package_name == IdString()) { - log_error("Unsuported partnumber '%s'.\n", args.partnumber.c_str()); + log_error("Unsupported partnumber '%s'.\n", args.partnumber.c_str()); } // setup timing info @@ -719,7 +719,7 @@ Arch::Arch(ArchArgs args) : args(args) } } if (speed == nullptr) { - log_error("Unsuported speed grade '%s'.\n", speed_id.c_str(this)); + log_error("Unsupported speed grade '%s'.\n", speed_id.c_str(this)); } const VariantPOD *variant = nullptr; @@ -732,7 +732,7 @@ Arch::Arch(ArchArgs args) : args(args) } } if (variant == nullptr) { - log_error("Unsuported device grade '%s'.\n", device_id.c_str(this)); + log_error("Unsupported device grade '%s'.\n", device_id.c_str(this)); } package = nullptr; @@ -750,7 +750,7 @@ Arch::Arch(ArchArgs args) : args(args) } if (package == nullptr) { - log_error("Unsuported package '%s'.\n", package_name.c_str(this)); + log_error("Unsupported package '%s'.\n", package_name.c_str(this)); } // From a120ae1fa7be5bac98e77755a6220df59e443b16 Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 14 Dec 2021 18:47:35 +0000 Subject: [PATCH 014/712] python: Bind getBelLocation/getPipLocation Signed-off-by: gatecat --- common/arch_pybindings_shared.h | 4 ++++ common/pybindings.cc | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h index f44aa70e60..b3dc0506d0 100644 --- a/common/arch_pybindings_shared.h +++ b/common/arch_pybindings_shared.h @@ -48,6 +48,8 @@ fn_wrapper_2a_v, conv_from_str>::def_wrap(ctx_cls, "getBelType"); +fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelLocation"); fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "checkBelAvail"); fn_wrapper_1a, @@ -92,6 +94,8 @@ fn_wrapper_0a, conv_from_str>::def_wrap(ctx_cls, "getPipChecksum"); +fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipLocation"); fn_wrapper_3a_v, addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindPip"); fn_wrapper_1a_v>::def_wrap( diff --git a/common/pybindings.cc b/common/pybindings.cc index f9ee9eb76d..eef460ce11 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -83,6 +83,8 @@ template <> struct string_converter } // namespace PythonConversion +std::string loc_repr_py(Loc loc) { return stringf("Loc(%d, %d, %d)", loc.x, loc.y, loc.z); } + PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) { py::register_exception_translator([](std::exception_ptr p) { @@ -175,7 +177,8 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) .def(py::init()) .def_readwrite("x", &Loc::x) .def_readwrite("y", &Loc::y) - .def_readwrite("z", &Loc::z); + .def_readwrite("z", &Loc::z) + .def("__repr__", loc_repr_py); auto ci_cls = py::class_>(m, "CellInfo"); readwrite_wrapper, From a946ed0206cda29ce1bfe7f5a09f1b5e3d21794d Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 14 Dec 2021 19:27:20 +0000 Subject: [PATCH 015/712] ice40: Pack LUT at start of carry chain if there is 1 candidate Signed-off-by: gatecat --- ice40/pack.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ice40/pack.cc b/ice40/pack.cc index 0db7899212..921e73e08e 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -203,6 +203,8 @@ static void pack_carries(Context *ctx) CellInfo *carry_lc = nullptr; if (carry_ci_lc && carry_lcs.find(carry_ci_lc->name) != carry_lcs.end()) { carry_lc = carry_ci_lc; + } else if (ci_const && carry_lcs.size() == 1) { + carry_lc = ctx->cells.at(*(carry_lcs.begin())).get(); } else { // No LC to pack into matching I0/I1, insert a new one std::unique_ptr created_lc = From 120ed0c42d0d7a70003667d4e8dcc7b9ac86afef Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 15 Dec 2021 07:56:34 +1000 Subject: [PATCH 016/712] gowin: Recognize models correctly For example, clearly distinguish between GW1N-4 GW1NR-4 GW1NS-4 GW1NSR-4 GW1NSR-4 Signed-off-by: YRabbit --- gowin/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gowin/main.cc b/gowin/main.cc index c6697ecc86..cbd62ed92c 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -65,7 +65,7 @@ std::unique_ptr GowinCommandHandler::createContext(dict Date: Fri, 29 Oct 2021 18:00:05 -0400 Subject: [PATCH 017/712] machxo2: Correct which PIO wires get adjusted when writing text bitstream. Add verbose logging for adjustments. --- machxo2/bitstream.cc | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/machxo2/bitstream.cc b/machxo2/bitstream.cc index ed67975a22..913d7b58a7 100644 --- a/machxo2/bitstream.cc +++ b/machxo2/bitstream.cc @@ -19,6 +19,7 @@ */ #include +#include #include "bitstream.h" #include "config.h" @@ -59,13 +60,20 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) // Handle MachXO2's wonderful naming quirks for wires in left/right tiles, whose // relative coords push them outside the bounds of the chip. + // Indents are based on wires proximity/purpose. auto is_pio_wire = [](std::string name) { return (name.find("DI") != std::string::npos || name.find("JDI") != std::string::npos || - name.find("PADD") != std::string::npos || name.find("INDD") != std::string::npos || - name.find("IOLDO") != std::string::npos || name.find("IOLTO") != std::string::npos || - name.find("JCE") != std::string::npos || name.find("JCLK") != std::string::npos || - name.find("JLSR") != std::string::npos || name.find("JONEG") != std::string::npos || - name.find("JOPOS") != std::string::npos || name.find("JTS") != std::string::npos || + name.find("PADD") != std::string::npos || name.find("INDD") != std::string::npos || + name.find("IOLDO") != std::string::npos || name.find("IOLTO") != std::string::npos || + // JCE0-3, JCLK0-3, JLSR0-3 connect to PIO wires named JCEA-D, JCLKA-D, JLSRA-D. + name.find("JCEA") != std::string::npos || name.find("JCEB") != std::string::npos || + name.find("JCEC") != std::string::npos || name.find("JCED") != std::string::npos || + name.find("JCLKA") != std::string::npos || name.find("JCLKB") != std::string::npos || + name.find("JCLKC") != std::string::npos || name.find("JCLKD") != std::string::npos || + name.find("JLSRA") != std::string::npos || name.find("JLSRB") != std::string::npos || + name.find("JLSRC") != std::string::npos || name.find("JLSRD") != std::string::npos || + name.find("JONEG") != std::string::npos || name.find("JOPOS") != std::string::npos || + name.find("JTS") != std::string::npos || name.find("JIN") != std::string::npos || name.find("JIP") != std::string::npos || // Connections to global mux name.find("JINCK") != std::string::npos); @@ -92,10 +100,19 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) if (loc == wire.location) { // TODO: JINCK is not currently handled by this. if (is_pio_wire(basename)) { - if (wire.location.x == 0) - return "W1_" + basename; - else if (wire.location.x == max_col) - return "E1_" + basename; + if (wire.location.x == 0) { + std::string pio_name = "W1_" + basename; + if (ctx->verbose) + log_info("PIO wire %s was adjusted by W1 to form Trellis name %s.\n", \ + ctx->nameOfWire(wire), pio_name.c_str()); + return pio_name; + } else if (wire.location.x == max_col) { + std::string pio_name = "E1_" + basename; + if (ctx->verbose) + log_info("PIO wire %s was adjusted by E1 to form Trellis name %s.\n", \ + ctx->nameOfWire(wire), pio_name.c_str()); + return pio_name; + } } return basename; } From 365a871908f3b945fb19cfefb642fbde87dbf584 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Wed, 3 Nov 2021 13:53:57 -0400 Subject: [PATCH 018/712] machxo2: Add packing logic to forbid designs lacking FACADE_IO top-level ports. --- machxo2/pack.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 66d2d4110a..231cc99d58 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -229,6 +229,44 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) static bool is_facade_iob(const Context *ctx, const CellInfo *cell) { return cell->type == id_FACADE_IO; } +static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, NetInfo *&top) { + NPNR_ASSERT(is_nextpnr_iob(ctx, iob)); + + if(iob->type == ctx->id("$nextpnr_ibuf")) { + NetInfo *o = iob->ports.at(id_O).net; + top = o; + + CellInfo *fio = net_only_drives(ctx, o, is_facade_iob, id_PAD, true); + return fio != nullptr; + } else if(iob->type == ctx->id("$nextpnr_obuf")) { + NetInfo *i = iob->ports.at(id_I).net; + top = i; + + // If connected to a FACADE_IO PAD, the net attached to an I port of an + // $nextpnr_obuf will not have a driver, only users; an inout port + // like PAD cannot be a driver in nextpnr. So net_driven_by won't + // return anything. We exclude the IOB as one of the two users because + // we already know that the net drives the $nextpnr_obuf. + CellInfo *fio = net_only_drives(ctx, i, is_facade_iob, id_PAD, true, iob); + return fio != nullptr; + } else if(iob->type == ctx->id("$nextpnr_iobuf")) { + NetInfo *o = iob->ports.at(id_O).net; + top = o; + + // When split_io is enabled in a frontend (it is for JSON), the I and O + // ports of a $nextpnr_iobuf are split; the I port connects to the + // driver of the original net before IOB insertion, and the O port + // connects everything else. Because FACADE_IO PADs cannot be a driver + // in nextpnr, the we can safely ignore the I port of an $nextpnr_iobuf + // for any JSON input we're interested in accepting. + CellInfo *fio_o = net_only_drives(ctx, o, is_facade_iob, id_PAD, true); + return fio_o != nullptr; + } + + // Unreachable! + NPNR_ASSERT(false); +} + // Pack IO buffers- Right now, all this does is remove $nextpnr_[io]buf cells. // User is expected to manually instantiate FACADE_IO with BEL/IO_TYPE // attributes. @@ -241,6 +279,14 @@ static void pack_io(Context *ctx) for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_nextpnr_iob(ctx, ci)) { + NetInfo *top; + + if(!nextpnr_iob_connects_only_facade_iob(ctx, ci, top)) + log_error("Top level net '%s' is not connected to a FACADE_IO PAD port.\n", top->name.c_str(ctx)); + + if (ctx->verbose) + log_info("Removing top-level IOBUF '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); + for (auto &p : ci->ports) disconnect_port(ctx, ci, p.first); packed_cells.insert(ci->name); From be3788fa3004cfee1058293d5da6ba758f913e7e Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 5 Nov 2021 12:49:24 -0400 Subject: [PATCH 019/712] machxo2: Remove -noiopad option when generating miters for post-pnr verification. --- machxo2/examples/mitertest.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/machxo2/examples/mitertest.sh b/machxo2/examples/mitertest.sh index cfae28b788..feafc0ddab 100644 --- a/machxo2/examples/mitertest.sh +++ b/machxo2/examples/mitertest.sh @@ -61,6 +61,7 @@ do_smt() { miter -equiv -make_assert gold gate ${2}${1}_miter hierarchy -top ${2}${1}_miter; proc; opt_clean + flatten t:*FACADE_IO* write_verilog ${2}${1}_miter.v write_smt2 ${2}${1}_miter.smt2" @@ -71,7 +72,7 @@ do_smt() { set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v - synth_machxo2 -noiopad -json ${1}.json" + synth_machxo2 -json ${1}.json" ${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json From 4d757922572e6009777c6d533d410cdb2257c363 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 5 Nov 2021 13:26:05 -0400 Subject: [PATCH 020/712] machxo2: Remove no-iobs option. It was always enabled and should remain an implementation detail. --- machxo2/examples/demo-vhdl.sh | 2 +- machxo2/examples/demo.sh | 2 +- machxo2/examples/mitertest.sh | 2 +- machxo2/examples/simple.sh | 2 +- machxo2/examples/simtest.sh | 2 +- machxo2/main.cc | 3 --- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/machxo2/examples/demo-vhdl.sh b/machxo2/examples/demo-vhdl.sh index 4bdab54a8a..ed1f7d8044 100644 --- a/machxo2/examples/demo-vhdl.sh +++ b/machxo2/examples/demo-vhdl.sh @@ -19,6 +19,6 @@ set -ex ${YOSYS:-yosys} -p "ghdl --std=08 prims.vhd ${1}.vhd -e; attrmap -tocase LOC synth_machxo2 -json ${1}-vhdl.json" -${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --no-iobs --json $1-vhdl.json --textcfg $1-vhdl.txt +${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --json $1-vhdl.json --textcfg $1-vhdl.txt ecppack --compress $DB_ARG $1-vhdl.txt $1-vhdl.bit tinyproga -b $1-vhdl.bit diff --git a/machxo2/examples/demo.sh b/machxo2/examples/demo.sh index 00cb0cd0fd..634fbb4dfd 100644 --- a/machxo2/examples/demo.sh +++ b/machxo2/examples/demo.sh @@ -17,6 +17,6 @@ fi set -ex ${YOSYS:-yosys} -p "synth_machxo2 -json $1.json" $1.v -${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --no-iobs --json $1.json --textcfg $1.txt +${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --json $1.json --textcfg $1.txt ecppack --compress $DB_ARG $1.txt $1.bit tinyproga -b $1.bit diff --git a/machxo2/examples/mitertest.sh b/machxo2/examples/mitertest.sh index feafc0ddab..b7ec2695d0 100644 --- a/machxo2/examples/mitertest.sh +++ b/machxo2/examples/mitertest.sh @@ -73,7 +73,7 @@ set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v synth_machxo2 -json ${1}.json" -${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json clean -purge diff --git a/machxo2/examples/simple.sh b/machxo2/examples/simple.sh index 1da6093393..69706b9c3f 100644 --- a/machxo2/examples/simple.sh +++ b/machxo2/examples/simple.sh @@ -26,7 +26,7 @@ set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v synth_machxo2 -json ${1}.json show -format png -prefix ${1}" -${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json clean -purge diff --git a/machxo2/examples/simtest.sh b/machxo2/examples/simtest.sh index 2c7f6f3003..0adf175163 100644 --- a/machxo2/examples/simtest.sh +++ b/machxo2/examples/simtest.sh @@ -30,7 +30,7 @@ set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v synth_machxo2 -json ${1}.json" -${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json clean -purge diff --git a/machxo2/main.cc b/machxo2/main.cc index 53b765fbbd..e29e117b45 100644 --- a/machxo2/main.cc +++ b/machxo2/main.cc @@ -69,7 +69,6 @@ po::options_description MachXO2CommandHandler::getArchOptions() // specific.add_options()("lpf", po::value>(), "LPF pin constraint file(s)"); - specific.add_options()("no-iobs", "disable automatic IO buffer insertion (unimplemented- always enabled)"); return specific; } @@ -108,8 +107,6 @@ std::unique_ptr MachXO2CommandHandler::createContext(dict(new Context(chipArgs)); - if (vm.count("no-iobs")) - ctx->settings[ctx->id("disable_iobs")] = Property::State::S1; return ctx; } From 78ce9971ff9c0c5ed1b38e5a3423f325c9a1d6e5 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 5 Nov 2021 13:29:01 -0400 Subject: [PATCH 021/712] README.md: Add machxo2 arch to list of (experimental) supported devices. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c1b28f15ac..14128b87ec 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Currently nextpnr supports: * Lattice Nexus devices supported by [Project Oxide](https://github.com/gatecat/prjoxide) * Gowin LittleBee devices supported by [Project Apicula](https://github.com/YosysHQ/apicula) * *(experimental)* Cyclone V devices supported by [Mistral](https://github.com/Ravenslofty/mistral) + * *(experimental)* Lattice MachXO2 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis) * *(experimental)* a "generic" back-end for user-defined architectures There is some work in progress towards [support for Xilinx devices](https://github.com/gatecat/nextpnr-xilinx/) but it is not upstream and not intended for end users at the present time. We hope to see more FPGA families supported in the future. We would love your help in developing this awesome new project! From 064b6d808e9b91c93258a53ac1a18b3b84431545 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Thu, 16 Dec 2021 17:09:29 -0500 Subject: [PATCH 022/712] clangformat. --- machxo2/bitstream.cc | 10 ++++++---- machxo2/pack.cc | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/machxo2/bitstream.cc b/machxo2/bitstream.cc index 913d7b58a7..f624d91b61 100644 --- a/machxo2/bitstream.cc +++ b/machxo2/bitstream.cc @@ -62,6 +62,7 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) // relative coords push them outside the bounds of the chip. // Indents are based on wires proximity/purpose. auto is_pio_wire = [](std::string name) { + // clang-format off return (name.find("DI") != std::string::npos || name.find("JDI") != std::string::npos || name.find("PADD") != std::string::npos || name.find("INDD") != std::string::npos || name.find("IOLDO") != std::string::npos || name.find("IOLTO") != std::string::npos || @@ -77,6 +78,7 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) name.find("JIN") != std::string::npos || name.find("JIP") != std::string::npos || // Connections to global mux name.find("JINCK") != std::string::npos); + // clang-format on }; if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_" || prefix7 == "BRANCH_") @@ -103,14 +105,14 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) if (wire.location.x == 0) { std::string pio_name = "W1_" + basename; if (ctx->verbose) - log_info("PIO wire %s was adjusted by W1 to form Trellis name %s.\n", \ - ctx->nameOfWire(wire), pio_name.c_str()); + log_info("PIO wire %s was adjusted by W1 to form Trellis name %s.\n", ctx->nameOfWire(wire), + pio_name.c_str()); return pio_name; } else if (wire.location.x == max_col) { std::string pio_name = "E1_" + basename; if (ctx->verbose) - log_info("PIO wire %s was adjusted by E1 to form Trellis name %s.\n", \ - ctx->nameOfWire(wire), pio_name.c_str()); + log_info("PIO wire %s was adjusted by E1 to form Trellis name %s.\n", ctx->nameOfWire(wire), + pio_name.c_str()); return pio_name; } } diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 231cc99d58..c53229ba01 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -229,16 +229,17 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) static bool is_facade_iob(const Context *ctx, const CellInfo *cell) { return cell->type == id_FACADE_IO; } -static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, NetInfo *&top) { +static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, NetInfo *&top) +{ NPNR_ASSERT(is_nextpnr_iob(ctx, iob)); - if(iob->type == ctx->id("$nextpnr_ibuf")) { + if (iob->type == ctx->id("$nextpnr_ibuf")) { NetInfo *o = iob->ports.at(id_O).net; top = o; CellInfo *fio = net_only_drives(ctx, o, is_facade_iob, id_PAD, true); return fio != nullptr; - } else if(iob->type == ctx->id("$nextpnr_obuf")) { + } else if (iob->type == ctx->id("$nextpnr_obuf")) { NetInfo *i = iob->ports.at(id_I).net; top = i; @@ -249,7 +250,7 @@ static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, Ne // we already know that the net drives the $nextpnr_obuf. CellInfo *fio = net_only_drives(ctx, i, is_facade_iob, id_PAD, true, iob); return fio != nullptr; - } else if(iob->type == ctx->id("$nextpnr_iobuf")) { + } else if (iob->type == ctx->id("$nextpnr_iobuf")) { NetInfo *o = iob->ports.at(id_O).net; top = o; @@ -281,7 +282,7 @@ static void pack_io(Context *ctx) if (is_nextpnr_iob(ctx, ci)) { NetInfo *top; - if(!nextpnr_iob_connects_only_facade_iob(ctx, ci, top)) + if (!nextpnr_iob_connects_only_facade_iob(ctx, ci, top)) log_error("Top level net '%s' is not connected to a FACADE_IO PAD port.\n", top->name.c_str(ctx)); if (ctx->verbose) From 4451a562efaab259c46d0ce2308fc4f1f1963708 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 17 Dec 2021 14:51:00 +0000 Subject: [PATCH 023/712] frontend: Consider net aliases when uniquifying name Signed-off-by: gatecat --- frontend/frontend_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 6d2ee6f627..ed9354b63f 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -229,7 +229,7 @@ template struct GenericFrontend } name = ctx->id(comb); incr++; - } while (is_net ? ctx->nets.count(name) : ctx->cells.count(name)); + } while (is_net ? (ctx->nets.count(name) || ctx->net_aliases.count(name)) : ctx->cells.count(name)); return name; } From a30686014456cfc665b2bd3243dbef956891860d Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 17 Dec 2021 15:06:19 +0000 Subject: [PATCH 024/712] nexus: router1 speedup based on #867 Signed-off-by: gatecat --- nexus/arch.cc | 3 +++ nexus/arch.h | 72 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index ee7f6304c4..74a0647863 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -119,6 +119,9 @@ Arch::Arch(ArchArgs args) : args(args) ts.bels_by_z[bel.z].tile = i; ts.bels_by_z[bel.z].index = j; } + auto &ts = tileStatus.at(i); + ts.boundwires.resize(loc.wires.size()); + ts.boundpips.resize(loc.pips.size()); } for (int i = 0; i < chip_info->width; i++) { diff --git a/nexus/arch.h b/nexus/arch.h index 3e718e7809..edebec1ba0 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -911,6 +911,7 @@ struct Arch : BaseArch { std::vector boundcells; std::vector bels_by_z; + std::vector boundwires, boundpips; LogicTileStatus *lts = nullptr; ~TileStatus() { delete lts; } }; @@ -1014,7 +1015,7 @@ struct Arch : BaseArch return false; if (is_pseudo_pip_disabled(pip)) return false; - return BaseArch::checkPipAvail(pip); + return getBoundPipNet(pip) == nullptr; } bool checkPipAvailForNet(PipId pip, NetInfo *net) const override @@ -1023,7 +1024,8 @@ struct Arch : BaseArch return false; if (is_pseudo_pip_disabled(pip)) return false; - return BaseArch::checkPipAvailForNet(pip, net); + NetInfo *bound = getBoundPipNet(pip); + return (bound == nullptr) || (bound == net); } CellInfo *getBoundBelCell(BelId bel) const override @@ -1136,6 +1138,38 @@ struct Arch : BaseArch return range; } + void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) override + { + NPNR_ASSERT(wire != WireId()); + auto &w2n_entry = tileStatus.at(wire.tile).boundwires.at(wire.index); + NPNR_ASSERT(w2n_entry == nullptr); + net->wires[wire].pip = PipId(); + net->wires[wire].strength = strength; + w2n_entry = net; + this->refreshUiWire(wire); + } + void unbindWire(WireId wire) override + { + NPNR_ASSERT(wire != WireId()); + auto &w2n_entry = tileStatus.at(wire.tile).boundwires.at(wire.index); + NPNR_ASSERT(w2n_entry != nullptr); + + auto &net_wires = w2n_entry->wires; + auto it = net_wires.find(wire); + NPNR_ASSERT(it != net_wires.end()); + + auto pip = it->second.pip; + if (pip != PipId()) { + tileStatus.at(pip.tile).boundpips.at(pip.index) = nullptr; + } + + net_wires.erase(it); + w2n_entry = nullptr; + this->refreshUiWire(wire); + } + virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; } + NetInfo *getBoundWireNet(WireId wire) const override { return tileStatus.at(wire.tile).boundwires.at(wire.index); } + // ------------------------------------------------- PipId getPipByName(IdStringList name) const override; @@ -1220,6 +1254,40 @@ struct Arch : BaseArch return range; } + void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) override + { + NPNR_ASSERT(pip != PipId()); + + auto &p2n_entry = tileStatus.at(pip.tile).boundpips.at(pip.index); + NPNR_ASSERT(p2n_entry == nullptr); + p2n_entry = net; + + WireId dst = this->getPipDstWire(pip); + auto &w2n_entry = tileStatus.at(dst.tile).boundwires.at(dst.index); + NPNR_ASSERT(w2n_entry == nullptr); + w2n_entry = net; + net->wires[dst].pip = pip; + net->wires[dst].strength = strength; + } + + void unbindPip(PipId pip) override + { + NPNR_ASSERT(pip != PipId()); + + auto &p2n_entry = tileStatus.at(pip.tile).boundpips.at(pip.index); + NPNR_ASSERT(p2n_entry != nullptr); + WireId dst = this->getPipDstWire(pip); + + auto &w2n_entry = tileStatus.at(dst.tile).boundwires.at(dst.index); + NPNR_ASSERT(w2n_entry != nullptr); + w2n_entry = nullptr; + + p2n_entry->wires.erase(dst); + p2n_entry = nullptr; + } + + NetInfo *getBoundPipNet(PipId pip) const override { return tileStatus.at(pip.tile).boundpips.at(pip.index); } + // ------------------------------------------------- delay_t estimateDelay(WireId src, WireId dst) const override; From 53ce8f3736e7e4535c4fb4b323c01cbea95314a9 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 18 Sep 2021 10:00:05 +0100 Subject: [PATCH 025/712] router1: Improve timing heuristic Signed-off-by: gatecat --- common/router1.cc | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 0ff2bedd76..6977a79927 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -117,14 +117,21 @@ struct Router1 int arcs_without_ripup = 0; bool ripup_flag; - Router1(Context *ctx, const Router1Cfg &cfg) : ctx(ctx), cfg(cfg) {} + TimingAnalyser tmg; + + Router1(Context *ctx, const Router1Cfg &cfg) : ctx(ctx), cfg(cfg), tmg(ctx) + { + tmg.setup(); + tmg.run(); + } void arc_queue_insert(const arc_key &arc, WireId src_wire, WireId dst_wire) { if (queued_arcs.count(arc)) return; - delay_t pri = ctx->estimateDelay(src_wire, dst_wire) - arc.net_info->users[arc.user_idx].budget; + delay_t pri = ctx->estimateDelay(src_wire, dst_wire) * + (100 * tmg.get_criticality(CellPortKey(arc.net_info->users.at(arc.user_idx)))); arc_entry entry; entry.arc = arc; @@ -459,6 +466,8 @@ struct Router1 auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], arc.phys_idx); ripup_flag = false; + float crit = tmg.get_criticality(CellPortKey(net_info->users.at(user_idx))); + if (ctx->debug) { log("Routing arc %d on net %s (%d arcs total):\n", user_idx, ctx->nameOf(net_info), int(net_info->users.size())); @@ -536,6 +545,7 @@ struct Router1 delay_t next_delay = qw.delay + ctx->getPipDelay(pip).maxDelay(); delay_t next_penalty = qw.penalty; delay_t next_bonus = qw.bonus; + delay_t penalty_delta = 0; WireId next_wire = ctx->getPipDstWire(pip); next_delay += ctx->getWireDelay(next_wire).maxDelay(); @@ -544,7 +554,7 @@ struct Router1 NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr; if (net_info->wires.count(next_wire) && net_info->wires.at(next_wire).pip == pip) { - next_bonus += cfg.reuseBonus; + next_bonus += cfg.reuseBonus * (1.0 - crit); } else { if (!ctx->checkWireAvail(next_wire)) { if (!ripup) @@ -609,34 +619,36 @@ struct Router1 if (conflictWireWire != WireId()) { auto scores_it = wireScores.find(conflictWireWire); if (scores_it != wireScores.end()) - next_penalty += scores_it->second * cfg.wireRipupPenalty; - next_penalty += cfg.wireRipupPenalty; + penalty_delta += scores_it->second * cfg.wireRipupPenalty; + penalty_delta += cfg.wireRipupPenalty; } if (conflictPipWire != WireId()) { auto scores_it = wireScores.find(conflictPipWire); if (scores_it != wireScores.end()) - next_penalty += scores_it->second * cfg.wireRipupPenalty; - next_penalty += cfg.wireRipupPenalty; + penalty_delta += scores_it->second * cfg.wireRipupPenalty; + penalty_delta += cfg.wireRipupPenalty; } if (conflictWireNet != nullptr) { auto scores_it = netScores.find(conflictWireNet); if (scores_it != netScores.end()) - next_penalty += scores_it->second * cfg.netRipupPenalty; - next_penalty += cfg.netRipupPenalty; - next_penalty += conflictWireNet->wires.size() * cfg.wireRipupPenalty; + penalty_delta += scores_it->second * cfg.netRipupPenalty; + penalty_delta += cfg.netRipupPenalty; + penalty_delta += conflictWireNet->wires.size() * cfg.wireRipupPenalty; } if (conflictPipNet != nullptr) { auto scores_it = netScores.find(conflictPipNet); if (scores_it != netScores.end()) - next_penalty += scores_it->second * cfg.netRipupPenalty; - next_penalty += cfg.netRipupPenalty; - next_penalty += conflictPipNet->wires.size() * cfg.wireRipupPenalty; + penalty_delta += scores_it->second * cfg.netRipupPenalty; + penalty_delta += cfg.netRipupPenalty; + penalty_delta += conflictPipNet->wires.size() * cfg.wireRipupPenalty; } } + next_penalty += penalty_delta * std::max(0.05, (1.0 - crit)); + delay_t next_score = next_delay + next_penalty; NPNR_ASSERT(next_score >= 0); From 9b2d6c5a67f0774bc41f0bc6cdb6a71bbcc388e8 Mon Sep 17 00:00:00 2001 From: uis Date: Sat, 18 Dec 2021 22:44:08 +0300 Subject: [PATCH 026/712] Clean gowin modification regex --- gowin/main.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/gowin/main.cc b/gowin/main.cc index cbd62ed92c..66de26aa34 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -54,7 +54,7 @@ po::options_description GowinCommandHandler::getArchOptions() std::unique_ptr GowinCommandHandler::createContext(dict &values) { - std::regex devicere = std::regex("GW1N([A-Z]*)-(LV|UV|UX)([0-9])(C?).*"); + std::regex devicere = std::regex("GW1N(S?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*"); std::smatch match; std::string device = vm["device"].as(); if (!std::regex_match(device, match, devicere)) { @@ -65,11 +65,7 @@ std::unique_ptr GowinCommandHandler::createContext(dict(new Context(chipArgs)); From f670de7b52027ace6eb9f382ee6025f79d84202d Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 18 Dec 2021 20:58:44 +0000 Subject: [PATCH 027/712] router1: Experimental timing-driven ripup support Signed-off-by: gatecat --- common/command.cc | 8 ++-- common/router1.cc | 97 ++++++++++++++++++++++++++++++++++++++++++++++- common/router2.cc | 4 +- 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/common/command.cc b/common/command.cc index 5a13fb55ce..d4279a581f 100644 --- a/common/command.cc +++ b/common/command.cc @@ -176,7 +176,9 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("router2-heatmap", po::value(), "prefix for router2 resource congestion heatmaps"); - general.add_options()("router2-tmg-ripup", "enable experimental timing-driven ripup in router2"); + general.add_options()("tmg-ripup", "enable experimental timing-driven ripup in router"); + general.add_options()("router2-tmg-ripup", + "enable experimental timing-driven ripup in router (deprecated; use --tmg-ripup instead)"); general.add_options()("report", po::value(), "write timing and utilization report in JSON format to file"); @@ -298,8 +300,8 @@ void CommandHandler::setupContext(Context *ctx) ctx->settings[ctx->id("placerHeap/timingWeight")] = std::to_string(vm["placer-heap-timingweight"].as()); if (vm.count("router2-heatmap")) ctx->settings[ctx->id("router2/heatmap")] = vm["router2-heatmap"].as(); - if (vm.count("router2-tmg-ripup")) - ctx->settings[ctx->id("router2/tmg_ripup")] = true; + if (vm.count("tmg-ripup") || vm.count("router2-tmg-ripup")) + ctx->settings[ctx->id("router/tmg_ripup")] = true; // Setting default values if (ctx->settings.find(ctx->id("target_freq")) == ctx->settings.end()) diff --git a/common/router1.cc b/common/router1.cc index 6977a79927..f387aee1c5 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -119,8 +119,11 @@ struct Router1 TimingAnalyser tmg; + bool timing_driven = true; + Router1(Context *ctx, const Router1Cfg &cfg) : ctx(ctx), cfg(cfg), tmg(ctx) { + timing_driven = ctx->setting("timing_driven"); tmg.setup(); tmg.run(); } @@ -647,7 +650,7 @@ struct Router1 } } - next_penalty += penalty_delta * std::max(0.05, (1.0 - crit)); + next_penalty += penalty_delta * (timing_driven ? std::max(0.05, (1.0 - crit)) : 1); delay_t next_score = next_delay + next_penalty; NPNR_ASSERT(next_score >= 0); @@ -790,6 +793,53 @@ struct Router1 return true; } + + delay_t find_slack_thresh() + { + // If more than 5% of arcs have negative slack; use the 5% threshold as a ripup criteria + int arc_count = 0; + int failed_count = 0; + delay_t default_thresh = ctx->getDelayEpsilon(); + + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (skip_net(ni)) + continue; + for (size_t i = 0; i < ni->users.size(); i++) { + auto &usr = ni->users.at(i); + ++arc_count; + delay_t slack = tmg.get_setup_slack(CellPortKey(usr)); + if (slack == std::numeric_limits::min()) + continue; + if (slack < default_thresh) + ++failed_count; + } + } + + if (arc_count < 50 || (failed_count < (0.05 * arc_count))) { + return default_thresh; + } + + std::vector slacks; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (skip_net(ni)) + continue; + for (size_t i = 0; i < ni->users.size(); i++) { + auto &usr = ni->users.at(i); + delay_t slack = tmg.get_setup_slack(CellPortKey(usr)); + if (slack == std::numeric_limits::min()) + continue; + slacks.push_back(slack); + } + } + std::sort(slacks.begin(), slacks.end()); + delay_t thresh = slacks.at(int(slacks.size() * 0.05)); + log_warning("%.f%% of arcs have failing slack; using %.2fns as ripup threshold. Consider a reduced Fmax " + "constraint.\n", + (100.0 * failed_count) / arc_count, ctx->getDelayNS(thresh)); + return thresh; + } }; } // namespace @@ -831,6 +881,9 @@ bool router1(Context *ctx, const Router1Cfg &cfg) int iter_cnt = 0; int last_arcs_with_ripup = 0; int last_arcs_without_ripup = 0; + int timing_fail_count = 0; + bool timing_ripup = ctx->setting("router/tmg_ripup", false); + delay_t ripup_slack = 0; log_info(" | (re-)routed arcs | delta | remaining| time spent |\n"); log_info(" IterCnt | w/ripup wo/ripup | w/r wo/r | arcs| batch(sec) total(sec)|\n"); @@ -866,6 +919,48 @@ bool router1(Context *ctx, const Router1Cfg &cfg) #endif return false; } + // Timing driven ripup + if (timing_ripup && router.arc_queue.empty() && timing_fail_count < 50) { + ++timing_fail_count; + router.tmg.run(); + delay_t wns = 0, tns = 0; + if (timing_fail_count == 1) + ripup_slack = router.find_slack_thresh(); + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (router.skip_net(ni)) + continue; + bool is_locked = false; + for (auto &wire : ni->wires) { + if (wire.second.strength > STRENGTH_STRONG) + is_locked = true; + } + if (is_locked) + continue; + for (size_t i = 0; i < ni->users.size(); i++) { + auto &usr = ni->users.at(i); + delay_t slack = router.tmg.get_setup_slack(CellPortKey(usr)); + if (slack == std::numeric_limits::min()) + continue; + if (slack < 0) { + wns = std::min(wns, slack); + tns += slack; + } + if (slack <= ripup_slack) { + for (WireId w : ctx->getNetinfoSinkWires(ni, usr)) { + if (ctx->checkWireAvail(w)) + continue; + router.ripup_wire(w); + } + } + } + } + log_info(" %d arcs ripped up due to negative slack WNS=%.02fns TNS=%.02fns.\n", + int(router.arc_queue.size()), ctx->getDelayNS(wns), ctx->getDelayNS(tns)); + iter_cnt = 0; + router.wireScores.clear(); + router.netScores.clear(); + } } auto rend = std::chrono::high_resolution_clock::now(); log_info("%10d | %8d %10d | %4d %5d | %9d| %10.02f %10.02f|\n", iter_cnt, router.arcs_with_ripup, diff --git a/common/router2.cc b/common/router2.cc index d713cce288..c76e1f6153 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -1373,8 +1373,8 @@ struct Router2 route_queue.push_back(i); timing_driven = ctx->setting("timing_driven"); - if (ctx->settings.count(ctx->id("router2/tmg_ripup"))) - timing_driven_ripup = timing_driven && ctx->setting("router2/tmg_ripup"); + if (ctx->settings.count(ctx->id("router/tmg_ripup"))) + timing_driven_ripup = timing_driven && ctx->setting("router/tmg_ripup"); else timing_driven_ripup = false; log_info("Running main router loop...\n"); From ddb084e9a8a0cba10536951236cde824526e8071 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 19 Dec 2021 16:41:34 +0000 Subject: [PATCH 028/712] archapi: Use arbitrary rather than actual placement in predictDelay This makes predictDelay be based on an arbitrary belpin pair rather than a arc of a net based on cell placement. This way 'what-if' decisions can be evaluated without actually changing placement; potentially useful for parallel placement. A new helper predictArcDelay behaves like the old predictDelay to minimise the impact on existing passes; only arches need be updated. Signed-off-by: gatecat --- common/arch_api.h | 2 +- common/context.cc | 23 +++++++++++++++++++++-- common/context.h | 2 ++ common/nextpnr_namespaces.h | 2 ++ common/place_common.cc | 2 +- common/placer1.cc | 4 ++-- common/timing.cc | 2 +- common/timing_opt.cc | 8 ++++---- docs/archapi.md | 2 +- docs/coding.md | 2 +- ecp5/arch.cc | 21 ++++++++------------- ecp5/arch.h | 2 +- fpga_interchange/arch.cc | 8 +++++--- fpga_interchange/arch.h | 2 +- generic/arch.cc | 9 +++++---- generic/arch.h | 2 +- gowin/arch.cc | 9 +++++---- gowin/arch.h | 2 +- ice40/arch.h | 2 +- ice40/delay.cc | 10 +++++----- machxo2/arch.cc | 13 +++++++------ machxo2/arch.h | 2 +- mistral/arch.h | 2 +- mistral/delay.cc | 12 +++++------- nexus/arch.cc | 12 +++++------- nexus/arch.h | 2 +- 26 files changed, 89 insertions(+), 70 deletions(-) diff --git a/common/arch_api.h b/common/arch_api.h index e49d26c1a8..14a30652ad 100644 --- a/common/arch_api.h +++ b/common/arch_api.h @@ -110,7 +110,7 @@ template struct ArchAPI : BaseCtx virtual typename R::GroupPipsRangeT getGroupPips(GroupId group) const = 0; virtual typename R::GroupGroupsRangeT getGroupGroups(GroupId group) const = 0; // Delay Methods - virtual delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const = 0; + virtual delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const = 0; virtual delay_t getDelayEpsilon() const = 0; virtual delay_t getRipupDelayPenalty() const = 0; virtual float getDelayNS(delay_t v) const = 0; diff --git a/common/context.cc b/common/context.cc index 6bba5cbe69..faddf82595 100644 --- a/common/context.cc +++ b/common/context.cc @@ -90,6 +90,25 @@ WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, return WireId(); } +delay_t Context::predictArcDelay(const NetInfo *net_info, const PortRef &sink) const +{ + if (net_info->driver.cell == nullptr || net_info->driver.cell->bel == BelId() || sink.cell->bel == BelId()) + return 0; + IdString driver_pin, sink_pin; + // Pick the first pin for a prediction; assume all will be similar enouhg + for (auto pin : getBelPinsForCellPin(net_info->driver.cell, net_info->driver.port)) { + driver_pin = pin; + break; + } + for (auto pin : getBelPinsForCellPin(sink.cell, sink.port)) { + sink_pin = pin; + break; + } + if (driver_pin == IdString() || sink_pin == IdString()) + return 0; + return predictDelay(net_info->driver.cell->bel, driver_pin, sink.cell->bel, sink_pin); +} + delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const { #ifdef ARCH_ECP5 @@ -98,7 +117,7 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us #endif if (net_info->wires.empty()) - return predictDelay(net_info, user_info); + return predictArcDelay(net_info, user_info); WireId src_wire = getNetinfoSourceWire(net_info); if (src_wire == WireId()) @@ -128,7 +147,7 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us if (cursor == src_wire) max_delay = std::max(max_delay, delay + getWireDelay(src_wire).maxDelay()); // routed else - max_delay = std::max(max_delay, predictDelay(net_info, user_info)); // unrouted + max_delay = std::max(max_delay, predictArcDelay(net_info, user_info)); // unrouted } return max_delay; } diff --git a/common/context.h b/common/context.h index 6adbbdb54f..cb8fd25707 100644 --- a/common/context.h +++ b/common/context.h @@ -51,6 +51,8 @@ struct Context : Arch, DeterministicRNG // -------------------------------------------------------------- + delay_t predictArcDelay(const NetInfo *net_info, const PortRef &sink) const; + WireId getNetinfoSourceWire(const NetInfo *net_info) const; SSOArray getNetinfoSinkWires(const NetInfo *net_info, const PortRef &sink) const; size_t getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const; diff --git a/common/nextpnr_namespaces.h b/common/nextpnr_namespaces.h index 6fb0aa776d..b758d7c555 100644 --- a/common/nextpnr_namespaces.h +++ b/common/nextpnr_namespaces.h @@ -33,6 +33,8 @@ #define USING_NEXTPNR_NAMESPACE #endif +#define NPNR_UNUSED(x) ((void)x) + #if defined(__GNUC__) || defined(__clang__) #define NPNR_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) #define NPNR_NORETURN __attribute__((noreturn)) diff --git a/common/place_common.cc b/common/place_common.cc index bcfa3633a4..e03fca553f 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -50,7 +50,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type if (load_cell->bel == BelId()) continue; if (timing_driven) { - delay_t net_delay = ctx->predictDelay(net, load); + delay_t net_delay = ctx->predictArcDelay(net, load); auto slack = load.budget - net_delay; if (slack < 0) negative_slack += slack; diff --git a/common/placer1.cc b/common/placer1.cc index 4db1c9519d..6de035b444 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -866,11 +866,11 @@ class SAPlacer if (ctx->getPortTimingClass(net->driver.cell, net->driver.port, cc) == TMG_IGNORE) return 0; if (cfg.budgetBased) { - double delay = ctx->getDelayNS(ctx->predictDelay(net, net->users.at(user))); + double delay = ctx->getDelayNS(ctx->predictArcDelay(net, net->users.at(user))); return std::min(10.0, std::exp(delay - ctx->getDelayNS(net->users.at(user).budget) / 10)); } else { float crit = tmg.get_criticality(CellPortKey(net->users.at(user))); - double delay = ctx->getDelayNS(ctx->predictDelay(net, net->users.at(user))); + double delay = ctx->getDelayNS(ctx->predictArcDelay(net, net->users.at(user))); return delay * std::pow(crit, crit_exp); } } diff --git a/common/timing.cc b/common/timing.cc index e305d82d1c..f30d4fc531 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -1378,7 +1378,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p auto driver_wire = ctx->getNetinfoSourceWire(net); auto sink_wire = ctx->getNetinfoSinkWire(net, sink_ref, 0); log_info(" prediction: %f ns estimate: %f ns\n", - ctx->getDelayNS(ctx->predictDelay(net, sink_ref)), + ctx->getDelayNS(ctx->predictArcDelay(net, sink_ref)), ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire))); auto cursor = sink_wire; delay_t delay; diff --git a/common/timing_opt.cc b/common/timing_opt.cc index 6dd93d6783..a73a70cf0f 100644 --- a/common/timing_opt.cc +++ b/common/timing_opt.cc @@ -99,7 +99,7 @@ class TimingOptimiser continue; for (auto user : net->users) { if (user.cell == cell && user.port == port.first) { - if (ctx->predictDelay(net, user) > + if (ctx->predictArcDelay(net, user) > 1.1 * max_net_delay.at(std::make_pair(cell->name, port.first))) return false; } @@ -111,7 +111,7 @@ class TimingOptimiser BelId dstBel = user.cell->bel; if (dstBel == BelId()) continue; - if (ctx->predictDelay(net, user) > + if (ctx->predictArcDelay(net, user) > 1.1 * max_net_delay.at(std::make_pair(user.cell->name, user.port))) { return false; @@ -413,7 +413,7 @@ class TimingOptimiser for (size_t j = 0; j < pn->users.size(); j++) { auto &usr = pn->users.at(j); if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) { - original_delay += ctx->predictDelay(pn, usr); + original_delay += ctx->predictArcDelay(pn, usr); break; } } @@ -497,7 +497,7 @@ class TimingOptimiser for (size_t j = 0; j < pn->users.size(); j++) { auto &usr = pn->users.at(j); if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) { - total_delay += ctx->predictDelay(pn, usr); + total_delay += ctx->predictArcDelay(pn, usr); break; } } diff --git a/docs/archapi.md b/docs/archapi.md index f2571f084b..5034d7d12f 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -517,7 +517,7 @@ result, and for that estimate it is considered more acceptable to return a slightly too high result and it is considered less acceptable to return a too low result (thus "low upper bound"). -### delay\_t predictDelay(const NetInfo \*net\_info, const PortRef &sink) const +### delay\_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const Return a reasonably good estimate for the total `maxDelay()` delay for the given arc. This should return a low upper bound for the fastest route for that arc. diff --git a/docs/coding.md b/docs/coding.md index dc09bb3f18..db2ec079b6 100644 --- a/docs/coding.md +++ b/docs/coding.md @@ -80,7 +80,7 @@ As nextpnr allows arbitrary constraints on bels for more advanced packer-free fl There are several routes for timing information in the placer: - sink `PortRef`s have a `budget` value annotated by calling `assign_budget` which is an estimate of the maximum delay that an arc may have - sink ports can have a criticality (value between 0 and 1 where 1 is the critical path) associated with them by using `get_criticalities` and a `NetCriticalityMap` - - `predictDelay` returns an estimated delay for a sink port based on placement information + - `predictDelay` and its derivative `predictArcDelay` returns an estimated delay for a sink port based on placement information ### Bel Buckets diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 95a2768285..2e453f2a90 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -543,26 +543,21 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const return bb; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - const auto &driver = net_info->driver; - if ((driver.port == id_FCO && sink.port == id_FCI) || sink.port == id_FXA || sink.port == id_FXB) + if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin == id_FXA || dst_pin == id_FXB) return 0; - auto driver_loc = getBelLocation(driver.cell->bel); - auto sink_loc = getBelLocation(sink.cell->bel); + auto driver_loc = getBelLocation(src_bel); + auto sink_loc = getBelLocation(dst_bel); // Encourage use of direct interconnect if (driver_loc.x == sink_loc.x && driver_loc.y == sink_loc.y) { - if ((sink.port == id_A0 || sink.port == id_A1) && (driver.port == id_F1) && - (driver_loc.z == 2 || driver_loc.z == 3)) + if ((dst_pin == id_A0 || dst_pin == id_A1) && (src_pin == id_F1) && (driver_loc.z == 2 || driver_loc.z == 3)) return 0; - if ((sink.port == id_B0 || sink.port == id_B1) && (driver.port == id_F1) && - (driver_loc.z == 0 || driver_loc.z == 1)) + if ((dst_pin == id_B0 || dst_pin == id_B1) && (src_pin == id_F1) && (driver_loc.z == 0 || driver_loc.z == 1)) return 0; - if ((sink.port == id_C0 || sink.port == id_C1) && (driver.port == id_F0) && - (driver_loc.z == 2 || driver_loc.z == 3)) + if ((dst_pin == id_C0 || dst_pin == id_C1) && (src_pin == id_F0) && (driver_loc.z == 2 || driver_loc.z == 3)) return 0; - if ((sink.port == id_D0 || sink.port == id_D1) && (driver.port == id_F0) && - (driver_loc.z == 0 || driver_loc.z == 1)) + if ((dst_pin == id_D0 || dst_pin == id_D1) && (src_pin == id_F0) && (driver_loc.z == 0 || driver_loc.z == 1)) return 0; } diff --git a/ecp5/arch.h b/ecp5/arch.h index 51a919bb41..c1bed2b361 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -913,7 +913,7 @@ struct Arch : BaseArch delay_t estimateDelay(WireId src, WireId dst) const override; ArcBounds getRouteBoundingBox(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 20; } delay_t getRipupDelayPenalty() const override; float getDelayNS(delay_t v) const override { return v * 0.001; } diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index a39f49e67e..917af85e92 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -1000,14 +1000,16 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const #endif } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { // FIXME: Implement when adding timing-driven place and route. + NPNR_UNUSED(src_pin); + NPNR_UNUSED(dst_pin); int src_x, src_y; - get_tile_x_y(net_info->driver.cell->bel.tile, &src_x, &src_y); + get_tile_x_y(src_bel.tile, &src_x, &src_y); int dst_x, dst_y; - get_tile_x_y(sink.cell->bel.tile, &dst_x, &dst_y); + get_tile_x_y(dst_bel.tile, &dst_x, &dst_y); delay_t base = 30 * std::min(std::abs(dst_x - src_x), 18) + 10 * std::max(std::abs(dst_x - src_x) - 18, 0) + 60 * std::min(std::abs(dst_y - src_y), 6) + 20 * std::max(std::abs(dst_y - src_y) - 6, 0) + 300; diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 482bf9117d..8bb2e2d149 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -700,7 +700,7 @@ struct Arch : ArchAPI // ------------------------------------------------- delay_t estimateDelay(WireId src, WireId dst) const final; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const final; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const final; ArcBounds getRouteBoundingBox(WireId src, WireId dst) const final; delay_t getDelayEpsilon() const final { return 20; } delay_t getRipupDelayPenalty() const final { return 120; } diff --git a/generic/arch.cc b/generic/arch.cc index ebc1ef2694..0695ec9880 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -509,11 +509,12 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const return (dx + dy) * args.delayScale + args.delayOffset; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - const auto &driver = net_info->driver; - auto driver_loc = getBelLocation(driver.cell->bel); - auto sink_loc = getBelLocation(sink.cell->bel); + NPNR_UNUSED(src_pin); + NPNR_UNUSED(dst_pin); + auto driver_loc = getBelLocation(src_bel); + auto sink_loc = getBelLocation(dst_bel); int dx = abs(sink_loc.x - driver_loc.x); int dy = abs(sink_loc.y - driver_loc.y); diff --git a/generic/arch.h b/generic/arch.h index 2344d8b2d1..7f574f31c5 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -287,7 +287,7 @@ struct Arch : ArchAPI const std::vector &getGroupGroups(GroupId group) const override; delay_t estimateDelay(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 0.001; } delay_t getRipupDelayPenalty() const override { return 0.015; } float getDelayNS(delay_t v) const override { return v; } diff --git a/gowin/arch.cc b/gowin/arch.cc index 99e0ce6115..801540cc42 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1171,11 +1171,12 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const return (dx + dy) * args.delayScale + args.delayOffset; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - const auto &driver = net_info->driver; - auto driver_loc = getBelLocation(driver.cell->bel); - auto sink_loc = getBelLocation(sink.cell->bel); + NPNR_UNUSED(src_pin); + NPNR_UNUSED(dst_pin); + auto driver_loc = getBelLocation(src_bel); + auto sink_loc = getBelLocation(dst_bel); int dx = abs(sink_loc.x - driver_loc.x); int dy = abs(sink_loc.y - driver_loc.y); diff --git a/gowin/arch.h b/gowin/arch.h index aa751a4fe9..931a0aa1e5 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -419,7 +419,7 @@ struct Arch : BaseArch const std::vector &getGroupGroups(GroupId group) const override; delay_t estimateDelay(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 0.01; } delay_t getRipupDelayPenalty() const override { return 0.4; } float getDelayNS(delay_t v) const override { return v; } diff --git a/ice40/arch.h b/ice40/arch.h index 5162285c36..3563baad2a 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -784,7 +784,7 @@ struct Arch : BaseArch // ------------------------------------------------- delay_t estimateDelay(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 20; } delay_t getRipupDelayPenalty() const override { return 200; } float getDelayNS(delay_t v) const override { return v * 0.001; } diff --git a/ice40/delay.cc b/ice40/delay.cc index 740057f1a9..a00cc2596c 100644 --- a/ice40/delay.cc +++ b/ice40/delay.cc @@ -188,13 +188,13 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const return v; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - const auto &driver = net_info->driver; - auto driver_loc = getBelLocation(driver.cell->bel); - auto sink_loc = getBelLocation(sink.cell->bel); + NPNR_UNUSED(dst_pin); + auto driver_loc = getBelLocation(src_bel); + auto sink_loc = getBelLocation(dst_bel); - if (driver.port == id_COUT) { + if (src_pin == id_COUT) { if (driver_loc.y == sink_loc.y) return 0; return 250; diff --git a/machxo2/arch.cc b/machxo2/arch.cc index a201adf1af..6c0e48ced5 100644 --- a/machxo2/arch.cc +++ b/machxo2/arch.cc @@ -387,16 +387,17 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const return (abs(dst.location.x - src.location.x) + abs(dst.location.y - src.location.y)) * (0.01 + 0.01); } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - BelId src = net_info->driver.cell->bel; - BelId dst = sink.cell->bel; + NPNR_UNUSED(src_pin); + NPNR_UNUSED(dst_pin); - NPNR_ASSERT(src != BelId()); - NPNR_ASSERT(dst != BelId()); + NPNR_ASSERT(src_bel != BelId()); + NPNR_ASSERT(dst_bel != BelId()); // TODO: Same deal applies here as with estimateDelay. - return (abs(dst.location.x - src.location.x) + abs(dst.location.y - src.location.y)) * (0.01 + 0.01); + return (abs(dst_bel.location.x - src_bel.location.x) + abs(dst_bel.location.y - src_bel.location.y)) * + (0.01 + 0.01); } ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const diff --git a/machxo2/arch.h b/machxo2/arch.h index f5ccb4f618..29d8c3ffcc 100644 --- a/machxo2/arch.h +++ b/machxo2/arch.h @@ -626,7 +626,7 @@ struct Arch : BaseArch // Delay delay_t estimateDelay(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 0.001; } delay_t getRipupDelayPenalty() const override { return 0.015; } float getDelayNS(delay_t v) const override { return v; } diff --git a/mistral/arch.h b/mistral/arch.h index 34e5584638..471b5251ee 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -418,7 +418,7 @@ struct Arch : BaseArch // ------------------------------------------------- delay_t estimateDelay(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 10; }; delay_t getRipupDelayPenalty() const override { return 100; }; float getDelayNS(delay_t v) const override { return float(v) / 1000.0f; }; diff --git a/mistral/delay.cc b/mistral/delay.cc index bfaeb06589..98ef1be62a 100644 --- a/mistral/delay.cc +++ b/mistral/delay.cc @@ -239,14 +239,12 @@ DelayQuad Arch::getPipDelay(PipId pip) const return DelayQuad{308}; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - if (net_info->driver.cell == nullptr || net_info->driver.cell->bel == BelId()) - return 100; - if (sink.cell->bel == BelId()) - return 100; - Loc src_loc = getBelLocation(net_info->driver.cell->bel); - Loc dst_loc = getBelLocation(sink.cell->bel); + NPNR_UNUSED(src_pin); + NPNR_UNUSED(dst_pin); + Loc src_loc = getBelLocation(src_bel); + Loc dst_loc = getBelLocation(dst_bel); return std::abs(dst_loc.y - src_loc.y) * 100 + std::abs(dst_loc.x - src_loc.x) * 100 + 100; } diff --git a/nexus/arch.cc b/nexus/arch.cc index 74a0647863..a7751425e3 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -603,16 +603,14 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const int dist_y = std::abs(src_y - dst_y); return 75 * dist_x + 75 * dist_y + 250; } -delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - if (net_info->driver.cell == nullptr || net_info->driver.cell->bel == BelId() || sink.cell->bel == BelId()) + NPNR_UNUSED(src_pin); + if (dst_pin == id_FCI) return 0; - if (sink.port == id_FCI) - return 0; - int src_x = net_info->driver.cell->bel.tile % chip_info->width, - src_y = net_info->driver.cell->bel.tile / chip_info->width; + int src_x = src_bel.tile % chip_info->width, src_y = src_bel.tile / chip_info->width; - int dst_x = sink.cell->bel.tile % chip_info->width, dst_y = sink.cell->bel.tile / chip_info->width; + int dst_x = dst_bel.tile % chip_info->width, dst_y = dst_bel.tile / chip_info->width; int dist_x = std::abs(src_x - dst_x); int dist_y = std::abs(src_y - dst_y); return 100 * dist_x + 100 * dist_y + 250; diff --git a/nexus/arch.h b/nexus/arch.h index edebec1ba0..0bd1b62cd7 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1291,7 +1291,7 @@ struct Arch : BaseArch // ------------------------------------------------- delay_t estimateDelay(WireId src, WireId dst) const override; - delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 20; } delay_t getRipupDelayPenalty() const override; delay_t getWireRipupDelayPenalty(WireId wire) const; From 5a76b3cb4d506c4920fc3d6b9aff935cbaa2987e Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 20 Dec 2021 15:48:38 +1000 Subject: [PATCH 029/712] gowin: Add simplified IO cells processing Some models have I/O cells that are IOBUFs, and other types (IBUFs and OBUFs) are obtained by feeding 1 or 0 to the OEN input. This is done with general-purpose routing so it's best to do it here to avoid conflicts. For this purpose, in the new bases, these special cells are of type IOBS (IOB Simplified). The proposed changes are compatible with bases of previous versions of Apycula and do not require changing .CST constraint files. Signed-off-by: YRabbit --- gowin/arch.cc | 33 +++++++++++++++++++++++++++++++++ gowin/cells.cc | 12 ++++++++++-- gowin/constids.inc | 13 +++++++++++++ gowin/pack.cc | 23 +++++++++++++++++++++-- 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 801540cc42..2f61685a33 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -870,6 +870,39 @@ Arch::Arch(ArchArgs args) : args(args) snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelInput(belname, id_OEN, id(buf)); break; + // Simplified IO + case ID_IOBJS: + z++; /* fall-through*/ + case ID_IOBIS: + z++; /* fall-through*/ + case ID_IOBHS: + z++; /* fall-through*/ + case ID_IOBGS: + z++; /* fall-through*/ + case ID_IOBFS: + z++; /* fall-through*/ + case ID_IOBES: + z++; /* fall-through*/ + case ID_IOBDS: + z++; /* fall-through*/ + case ID_IOBCS: + z++; /* fall-through*/ + case ID_IOBBS: + z++; /* fall-through*/ + case ID_IOBAS: + snprintf(buf, 32, "R%dC%d_IOB%c", row + 1, col + 1, 'A' + z); + belname = id(buf); + addBel(belname, id_IOBS, Loc(col, row, z), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_O)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_O, id(buf)); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_I)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_I, id(buf)); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OE)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_OEN, id(buf)); + break; default: break; diff --git a/gowin/cells.cc b/gowin/cells.cc index 57f3ab9cc2..dce3f45654 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -64,14 +64,14 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: add_port(ctx, new_cell.get(), id_I1, PORT_IN); add_port(ctx, new_cell.get(), id_SEL, PORT_IN); add_port(ctx, new_cell.get(), id_OF, PORT_OUT); - } else if (type == id_IOB) { + } else if (type == id_IOB || type == id_IOBS) { new_cell->params[id_INPUT_USED] = 0; new_cell->params[id_OUTPUT_USED] = 0; new_cell->params[id_ENABLE_USED] = 0; add_port(ctx, new_cell.get(), id_PAD, PORT_INOUT); add_port(ctx, new_cell.get(), id_I, PORT_IN); - add_port(ctx, new_cell.get(), id_EN, PORT_IN); + add_port(ctx, new_cell.get(), id_OEN, PORT_IN); add_port(ctx, new_cell.get(), id_O, PORT_OUT); } else { log_error("unable to create generic cell of type %s\n", type.c_str(ctx)); @@ -140,9 +140,17 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &todelete_cells) { if (nxio->type == id_IBUF) { + if (iob->type == id_IOBS) { + // VCC -> OEN + connect_port(ctx, ctx->nets[ctx->id("$PACKER_VCC_NET")].get(), iob, id_OEN); + } iob->params[id_INPUT_USED] = 1; replace_port(nxio, id_O, iob, id_O); } else if (nxio->type == id_OBUF) { + if (iob->type == id_IOBS) { + // VSS -> OEN + connect_port(ctx, ctx->nets[ctx->id("$PACKER_GND_NET")].get(), iob, id_OEN); + } iob->params[id_OUTPUT_USED] = 1; replace_port(nxio, id_I, iob, id_I); } else if (nxio->type == id_TBUF) { diff --git a/gowin/constids.inc b/gowin/constids.inc index 7de754fcc5..6a730d5d5e 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -358,6 +358,19 @@ X(IOBH) X(IOBI) X(IOBJ) +// simplified iobs +X(IOBS) +X(IOBAS) +X(IOBBS) +X(IOBCS) +X(IOBDS) +X(IOBES) +X(IOBFS) +X(IOBGS) +X(IOBHS) +X(IOBIS) +X(IOBJS) + // Wide LUTs X(MUX2_LUT5) X(MUX2_LUT6) diff --git a/gowin/pack.cc b/gowin/pack.cc index cb63f1c95a..553eeb4e53 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -629,7 +629,7 @@ static void pack_constants(Context *ctx) std::vector dead_nets; - bool gnd_used = false; + bool gnd_used = true; // XXX May be needed for simplified IO for (auto &net : ctx->nets) { NetInfo *ni = net.second.get(); @@ -718,8 +718,27 @@ static void pack_io(Context *ctx) } packed_cells.insert(iob->name); } + // what type to create + IdString new_cell_type = id_IOB; + std::string constr_bel_name = std::string(""); + // check whether the given IO is limited to simplified IO cells + auto constr_bel = ci->attrs.find(id_BEL); + if (constr_bel != ci->attrs.end()) { + constr_bel_name = constr_bel->second.as_string(); + } + constr_bel = iob->attrs.find(id_BEL); + if (constr_bel != iob->attrs.end()) { + constr_bel_name = constr_bel->second.as_string(); + } + if (!constr_bel_name.empty()) { + BelId constr_bel = ctx->getBelByNameStr(constr_bel_name); + if (constr_bel != BelId()) { + new_cell_type = ctx->bels[constr_bel].type; + } + } + // Create a IOB buffer - std::unique_ptr ice_cell = create_generic_cell(ctx, id_IOB, ci->name.str(ctx) + "$iob"); + std::unique_ptr ice_cell = create_generic_cell(ctx, new_cell_type, ci->name.str(ctx) + "$iob"); gwio_to_iob(ctx, ci, ice_cell.get(), packed_cells); new_cells.push_back(std::move(ice_cell)); auto gwiob = new_cells.back().get(); From 500fa6f4423ac016559f6140c8141da376d2833b Mon Sep 17 00:00:00 2001 From: Karol Gugala Date: Mon, 20 Dec 2021 14:33:29 +0100 Subject: [PATCH 030/712] nexus: handle SLEWRATE in pdc --- nexus/constids.inc | 1 + nexus/fasm.cc | 4 ++-- nexus/pdc.cc | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/nexus/constids.inc b/nexus/constids.inc index 48b0ca2b2f..ca6bed5a23 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -109,6 +109,7 @@ X(CIB_T) X(CIB_LR) X(IO_TYPE) +X(SLEWRATE) X(OSCA) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index bb0a794173..4aaecdf47d 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -433,7 +433,7 @@ struct NexusFasmWriter write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS33").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); write_ioattr(cell, "GLITCHFILTER", "OFF"); - write_ioattr(cell, "SLEWRATE", "MED"); + write_ioattr(cell, "SLEWRATE", str_or_default(cell->attrs, id_SLEWRATE, "MED").c_str()); write_cell_muxes(cell); pop(); } @@ -458,7 +458,7 @@ struct NexusFasmWriter const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); - write_ioattr(cell, "SLEWRATE", "MED"); + write_ioattr(cell, "SLEWRATE", str_or_default(cell->attrs, id_SLEWRATE, "MED").c_str()); pop(); write_cell_muxes(cell); pop(); diff --git a/nexus/pdc.cc b/nexus/pdc.cc index 67bab3c9ec..af62215829 100644 --- a/nexus/pdc.cc +++ b/nexus/pdc.cc @@ -21,6 +21,7 @@ #include "log.h" #include "nextpnr.h" +#include #include NEXTPNR_NAMESPACE_BEGIN @@ -423,6 +424,12 @@ struct PDCParser if (eqp == std::string::npos) log_error("expected key-value pair separated by '=' (line %d)", lineno); std::string k = kv.substr(0, eqp), v = kv.substr(eqp + 1); + if (k == "SLEWRATE") { + std::vector slewrate_allowed = {"SLOW", "MED", "FAST", "NA"}; + if (std::find(std::begin(slewrate_allowed), std::end(slewrate_allowed), v) == + std::end(slewrate_allowed)) + log_error("unexpected SLEWRATE configuration %s (line %d)\n", v.c_str(), lineno); + } args[ctx->id(k)] = v; } } else { From 2c43ac992f841eb57ac3003acc2f3a5736ce4591 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 22 Dec 2021 13:17:55 +0000 Subject: [PATCH 031/712] mistral: Update to latest enum name Signed-off-by: gatecat --- .github/workflows/mistral_ci.yml | 2 +- mistral/bitstream.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index 7cf71621aa..64300d78f4 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: 0edeca112dda9bd463125feb869ddb7511d1acd9 + MISTRAL_REVISION: e039b595529ab573d9cb01c64ef927f9d81d63ce run: | source ./.github/ci/build_mistral.sh get_dependencies diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index e8c4dba7ae..eed508b38b 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -123,7 +123,7 @@ struct MistralBitgen break; }; case CycloneV::FPLL: { - if (pt == CycloneV::EXTSWITCH || (pt == CycloneV::CLKEN && pi < 2)) + if (pt == CycloneV::EXTSWITCH0 || (pt == CycloneV::CLKEN && pi < 2)) cv->inv_set(pn2r.second, true); break; }; From b53a921e426efdfbc6e0d13c19bcb94439431a70 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Fri, 17 Dec 2021 12:34:21 +0100 Subject: [PATCH 032/712] Add support for GW1NS-4 series devices --- gowin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gowin/CMakeLists.txt b/gowin/CMakeLists.txt index a356a84b5f..5ec321288c 100644 --- a/gowin/CMakeLists.txt +++ b/gowin/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) project(chipdb-gowin NONE) -set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9 GW1NS-2) +set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9 GW1NS-2 GW1NS-4) set(GOWIN_DEVICES ${ALL_GOWIN_DEVICES} CACHE STRING "Include support for these Gowin devices (available: ${ALL_GOWIN_DEVICES})") message(STATUS "Enabled Gowin devices: ${GOWIN_DEVICES}") From fa1ef15e47f7ee0b36a77f94ea26f92e2a8a689d Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Fri, 17 Dec 2021 12:35:37 +0100 Subject: [PATCH 033/712] build on release of apycula with gw1ns-4 support --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index 4f229d2d38..bf81a4a663 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -65,4 +65,4 @@ RUN set -e -x ;\ PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide RUN set -e -x ;\ - pip3 install apycula==0.0.1a12 + pip3 install apycula==0.2a1 From e6b787954216ec220698bf7a6264f19bb6102d73 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sun, 26 Dec 2021 12:05:35 +1000 Subject: [PATCH 034/712] gowin: Initializing the grid dimensions gridDimX and gridDimY are not initialized explicitly, which leads to effects when the design is reloaded, say, from the GUI. Signed-off-by: YRabbit --- gowin/arch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gowin/arch.h b/gowin/arch.h index 931a0aa1e5..9f969f54f0 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -298,7 +298,7 @@ struct Arch : BaseArch dict> decal_graphics; - int gridDimX, gridDimY; + int gridDimX = 0, gridDimY = 0; std::vector> tileBelDimZ; std::vector> tilePipDimZ; From 5b6961edd0ff3ad79aa659696105f93c3a246ca0 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Sun, 26 Dec 2021 17:52:00 +0100 Subject: [PATCH 035/712] update release that actually includes GW1NS-4 chipdb --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index bf81a4a663..934bbbcbe0 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -65,4 +65,4 @@ RUN set -e -x ;\ PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide RUN set -e -x ;\ - pip3 install apycula==0.2a1 + pip3 install apycula==0.2a2 From c272d28e575bde2675a6ae7090a0d5f0f4c9c88f Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 29 Dec 2021 21:43:33 +0000 Subject: [PATCH 036/712] docs: Fix typo Signed-off-by: gatecat --- docs/archapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/archapi.md b/docs/archapi.md index 5034d7d12f..f798254fad 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -280,7 +280,7 @@ Return a (preferably unique) number that represents this wire. This is used in d ### void bindWire(WireId wire, NetInfo \*net, PlaceStrength strength) -Bind a wire to a net. This method must be used when binding a wire that is driven by a bel pin. Use `binPip()` +Bind a wire to a net. This method must be used when binding a wire that is driven by a bel pin. Use `bindPip()` when binding a wire that is driven by a pip. This method must also update `net->wires`. From 59874188a6800fbaa03ec21e3578160e963c2eb5 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 30 Dec 2021 09:08:02 +0000 Subject: [PATCH 037/712] generic: Refactor for faster performance This won't affect Python-built arches significantly; but will be useful for the future 'viaduct' functionality where generic routing graphs can be built on the C++ side; too. Signed-off-by: gatecat --- common/pywrappers.h | 17 +- docs/generic.md | 35 +++-- generic/arch.cc | 274 ++++++++++++++++----------------- generic/arch.h | 78 ++++++---- generic/arch_pybindings.cc | 64 ++++---- generic/arch_pybindings.h | 67 ++++++++ generic/archdefs.h | 39 ++++- generic/examples/write_fasm.py | 2 +- 8 files changed, 348 insertions(+), 228 deletions(-) diff --git a/common/pywrappers.h b/common/pywrappers.h index 66dec6fb11..60ef65be7c 100644 --- a/common/pywrappers.h +++ b/common/pywrappers.h @@ -257,7 +257,7 @@ template struct f { Context *ctx = get_ctx(cls); Class &base = get_base(cls); - return (base.*fn)(arg1_conv()(ctx, arg1)); + (base.*fn)(arg1_conv()(ctx, arg1)); } template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } @@ -280,7 +280,7 @@ template (cls); Class &base = get_base(cls); - return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2)); + (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2)); } template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } @@ -304,7 +304,7 @@ struct fn_wrapper_3a_v { Context *ctx = get_ctx(cls); Class &base = get_base(cls); - return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3)); + (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3)); } template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } @@ -331,8 +331,7 @@ struct fn_wrapper_4a_v { Context *ctx = get_ctx(cls); Class &base = get_base(cls); - return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3), - arg4_conv()(ctx, arg4)); + (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3), arg4_conv()(ctx, arg4)); } template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } @@ -360,8 +359,8 @@ struct fn_wrapper_5a_v { Context *ctx = get_ctx(cls); Class &base = get_base(cls); - return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3), - arg4_conv()(ctx, arg4), arg5_conv()(ctx, arg5)); + (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3), arg4_conv()(ctx, arg4), + arg5_conv()(ctx, arg5)); } template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } @@ -390,8 +389,8 @@ struct fn_wrapper_6a_v { Context *ctx = get_ctx(cls); Class &base = get_base(cls); - return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3), - arg4_conv()(ctx, arg4), arg5_conv()(ctx, arg5), arg6_conv()(ctx, arg6)); + (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3), arg4_conv()(ctx, arg4), + arg5_conv()(ctx, arg5), arg6_conv()(ctx, arg6)); } template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } diff --git a/docs/generic.md b/docs/generic.md index 0562bed314..96db872e84 100644 --- a/docs/generic.md +++ b/docs/generic.md @@ -12,35 +12,42 @@ will be worked on in the future. ## Python API -All identifiers (`IdString`) are automatically converted to -and from a Python string, so no manual conversion is required. +All identifiers (`IdString`, `IdStringList`, `WireId`, `PipId`, and `BelId`) are +automatically converted to and from a Python string, so no manual conversion is +required. + +`IdStringList`s will be most efficient if strings can be split according to a +separator (currently fixed to `/`), as only the components need be stored +in-memory. For example; instead of needing to store an entire pip name +`X33/Y45/V4A_TO_A6` which scales badly for large numbers of pips; the strings +`X33`, `Y45` and `V4A_TO_A6` are stored. Argument names are included in the Python bindings, so named arguments may be used. -### void addWire(IdString name, IdString type, int x, int y); +### void addWire(IdStringList name, IdString type, int x, int y); Adds a wire with a name, type (for user purposes only, ignored by all nextpnr code other than the UI) to the FPGA description. x and y give a nominal location of the wire for delay estimation purposes. Delay estimates are important for router performance (as the router uses an A* type algorithm), even if timing is not of importance. -### addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, float delay, Loc loc); +### addPip(IdStringList name, IdString type, WireId srcWire, WireId dstWire, float delay, Loc loc); Adds a pip (programmable connection between two named wires). Pip delays that correspond to delay estimates are important for router performance (as the router uses an A* type algorithm), even if timing is otherwise not of importance. Loc is constructed using `Loc(x, y, z)`. 'z' for pips is only important if region constraints (e.g. for partial reconfiguration regions) are used. -### void addBel(IdString name, IdString type, Loc loc, bool gb, bool hidden); +### void addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden); Adds a bel to the FPGA description. Bel type should match the type of cells in the netlist that are placed at this bel (see below for information on special bel types supported by the packer). Loc is constructed using `Loc(x, y, z)` and must be unique. If `hidden` is true, then the bel will not be included in utilisation reports (e.g. for routing/internal use bels). -### void addBelInput(IdString bel, IdString name, IdString wire); -### void addBelOutput(IdString bel, IdString name, IdString wire); -### void addBelInout(IdString bel, IdString name, IdString wire); +### void addBelInput(BelId bel, IdString name, WireId wire); +### void addBelOutput(BelId bel, IdString name, WireId wire); +### void addBelInout(BelId bel, IdString name, WireId wire); Adds an input, output or inout pin to a bel, with an associated wire. Note that both `bel` and `wire` must have been created before calling this function. -### void addGroupBel(IdString group, IdString bel); -### void addGroupWire(IdString group, IdString wire); -### void addGroupPip(IdString group, IdString pip); +### void addGroupBel(IdString group, BelId bel); +### void addGroupWire(IdString group, WireId wire); +### void addGroupPip(IdString group, PipId pip); ### void addGroupGroup(IdString group, IdString grp); Add a bel, wire, pip or subgroup to a group, which will be created if it doesn't already exist. Groups are purely for visual presentation purposes in the user interface and are not used by any place-and-route algorithms. @@ -56,9 +63,9 @@ Add a graphic element to a _decal_, a reusable drawing that may be used to repre Sets the decal ID and offset for a wire, bel, pip or group in the UI. -### void setWireAttr(IdString wire, IdString key, const std::string &value); -### void setPipAttr(IdString pip, IdString key, const std::string &value); -### void setBelAttr(IdString bel, IdString key, const std::string &value); +### void setWireAttr(WireId wire, IdString key, const std::string &value); +### void setPipAttr(PipId pip, IdString key, const std::string &value); +### void setBelAttr(BelId bel, IdString key, const std::string &value); Sets an attribute on a wire, pip or bel. Attributes are displayed in the tree view in the UI, but have no bearing on place-and-route itself. diff --git a/generic/arch.cc b/generic/arch.cc index 0695ec9880..d899bd0b68 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -28,46 +28,27 @@ NEXTPNR_NAMESPACE_BEGIN -WireInfo &Arch::wire_info(IdStringList wire) +WireId Arch::addWire(IdStringList name, IdString type, int x, int y) { - auto w = wires.find(wire); - if (w == wires.end()) - NPNR_ASSERT_FALSE_STR("no wire named " + wire.str(getCtx())); - return w->second; -} - -PipInfo &Arch::pip_info(IdStringList pip) -{ - auto p = pips.find(pip); - if (p == pips.end()) - NPNR_ASSERT_FALSE_STR("no pip named " + pip.str(getCtx())); - return p->second; -} - -BelInfo &Arch::bel_info(IdStringList bel) -{ - auto b = bels.find(bel); - if (b == bels.end()) - NPNR_ASSERT_FALSE_STR("no bel named " + bel.str(getCtx())); - return b->second; -} - -void Arch::addWire(IdStringList name, IdString type, int x, int y) -{ - NPNR_ASSERT(wires.count(name) == 0); - WireInfo &wi = wires[name]; + NPNR_ASSERT(wire_by_name.count(name) == 0); + WireId wire(wires.size()); + wire_by_name[name] = wire; + wires.emplace_back(); + WireInfo &wi = wires.back(); wi.name = name; wi.type = type; wi.x = x; wi.y = y; - - wire_ids.push_back(name); + return wire; } -void Arch::addPip(IdStringList name, IdString type, IdStringList srcWire, IdStringList dstWire, delay_t delay, Loc loc) +PipId Arch::addPip(IdStringList name, IdString type, WireId srcWire, WireId dstWire, delay_t delay, Loc loc) { - NPNR_ASSERT(pips.count(name) == 0); - PipInfo &pi = pips[name]; + NPNR_ASSERT(pip_by_name.count(name) == 0); + PipId pip(pips.size()); + pip_by_name[name] = pip; + pips.emplace_back(); + PipInfo &pi = pips.back(); pi.name = name; pi.type = type; pi.srcWire = srcWire; @@ -75,9 +56,8 @@ void Arch::addPip(IdStringList name, IdString type, IdStringList srcWire, IdStri pi.delay = delay; pi.loc = loc; - wire_info(srcWire).downhill.push_back(name); - wire_info(dstWire).uphill.push_back(name); - pip_ids.push_back(name); + wire_info(srcWire).downhill.push_back(pip); + wire_info(dstWire).uphill.push_back(pip); if (int(tilePipDimZ.size()) <= loc.x) tilePipDimZ.resize(loc.x + 1); @@ -88,13 +68,17 @@ void Arch::addPip(IdStringList name, IdString type, IdStringList srcWire, IdStri gridDimX = std::max(gridDimX, loc.x + 1); gridDimY = std::max(gridDimY, loc.x + 1); tilePipDimZ[loc.x][loc.y] = std::max(tilePipDimZ[loc.x][loc.y], loc.z + 1); + return pip; } -void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden) +BelId Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden) { - NPNR_ASSERT(bels.count(name) == 0); + NPNR_ASSERT(bel_by_name.count(name) == 0); NPNR_ASSERT(bel_by_loc.count(loc) == 0); - BelInfo &bi = bels[name]; + BelId bel(bels.size()); + bel_by_name[name] = bel; + bels.emplace_back(); + BelInfo &bi = bels.back(); bi.name = name; bi.type = type; bi.x = loc.x; @@ -103,8 +87,7 @@ void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidde bi.gb = gb; bi.hidden = hidden; - bel_ids.push_back(name); - bel_by_loc[loc] = name; + bel_by_loc[loc] = bel; if (int(bels_by_tile.size()) <= loc.x) bels_by_tile.resize(loc.x + 1); @@ -112,7 +95,7 @@ void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidde if (int(bels_by_tile[loc.x].size()) <= loc.y) bels_by_tile[loc.x].resize(loc.y + 1); - bels_by_tile[loc.x][loc.y].push_back(name); + bels_by_tile[loc.x][loc.y].push_back(bel); if (int(tileBelDimZ.size()) <= loc.x) tileBelDimZ.resize(loc.x + 1); @@ -123,49 +106,50 @@ void Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidde gridDimX = std::max(gridDimX, loc.x + 1); gridDimY = std::max(gridDimY, loc.x + 1); tileBelDimZ[loc.x][loc.y] = std::max(tileBelDimZ[loc.x][loc.y], loc.z + 1); + return bel; } -void Arch::addBelInput(IdStringList bel, IdString name, IdStringList wire) +void Arch::addBelInput(BelId bel, IdString name, WireId wire) { - NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); - PinInfo &pi = bel_info(bel).pins[name]; + auto &bi = bel_info(bel); + NPNR_ASSERT(bi.pins.count(name) == 0); + PinInfo &pi = bi.pins[name]; pi.name = name; pi.wire = wire; pi.type = PORT_IN; - wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name}); wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } -void Arch::addBelOutput(IdStringList bel, IdString name, IdStringList wire) +void Arch::addBelOutput(BelId bel, IdString name, WireId wire) { - NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); - PinInfo &pi = bel_info(bel).pins[name]; + auto &bi = bel_info(bel); + NPNR_ASSERT(bi.pins.count(name) == 0); + PinInfo &pi = bi.pins[name]; pi.name = name; pi.wire = wire; pi.type = PORT_OUT; - wire_info(wire).uphill_bel_pin = BelPin{bel, name}; wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } -void Arch::addBelInout(IdStringList bel, IdString name, IdStringList wire) +void Arch::addBelInout(BelId bel, IdString name, WireId wire) { - NPNR_ASSERT(bel_info(bel).pins.count(name) == 0); - PinInfo &pi = bel_info(bel).pins[name]; + auto &bi = bel_info(bel); + NPNR_ASSERT(bi.pins.count(name) == 0); + PinInfo &pi = bi.pins[name]; pi.name = name; pi.wire = wire; pi.type = PORT_INOUT; - wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name}); wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } -void Arch::addGroupBel(IdStringList group, IdStringList bel) { groups[group].bels.push_back(bel); } +void Arch::addGroupBel(IdStringList group, BelId bel) { groups[group].bels.push_back(bel); } -void Arch::addGroupWire(IdStringList group, IdStringList wire) { groups[group].wires.push_back(wire); } +void Arch::addGroupWire(IdStringList group, WireId wire) { groups[group].wires.push_back(wire); } -void Arch::addGroupPip(IdStringList group, IdStringList pip) { groups[group].pips.push_back(pip); } +void Arch::addGroupPip(IdStringList group, PipId pip) { groups[group].pips.push_back(pip); } void Arch::addGroupGroup(IdStringList group, IdStringList grp) { groups[group].groups.push_back(grp); } @@ -177,19 +161,19 @@ void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic) void Arch::setWireDecal(WireId wire, DecalXY decalxy) { - wire_info(wire).decalxy = decalxy; + wires.at(wire.index).decalxy = decalxy; refreshUiWire(wire); } void Arch::setPipDecal(PipId pip, DecalXY decalxy) { - pip_info(pip).decalxy = decalxy; + pips.at(pip.index).decalxy = decalxy; refreshUiPip(pip); } void Arch::setBelDecal(BelId bel, DecalXY decalxy) { - bel_info(bel).decalxy = decalxy; + bels.at(bel.index).decalxy = decalxy; refreshUiBel(bel); } @@ -199,14 +183,11 @@ void Arch::setGroupDecal(GroupId group, DecalXY decalxy) refreshUiGroup(group); } -void Arch::setWireAttr(IdStringList wire, IdString key, const std::string &value) -{ - wire_info(wire).attrs[key] = value; -} +void Arch::setWireAttr(WireId wire, IdString key, const std::string &value) { wire_info(wire).attrs[key] = value; } -void Arch::setPipAttr(IdStringList pip, IdString key, const std::string &value) { pip_info(pip).attrs[key] = value; } +void Arch::setPipAttr(PipId pip, IdString key, const std::string &value) { pip_info(pip).attrs[key] = value; } -void Arch::setBelAttr(IdStringList bel, IdString key, const std::string &value) { bel_info(bel).attrs[key] = value; } +void Arch::setBelAttr(BelId bel, IdString key, const std::string &value) { bel_info(bel).attrs[key] = value; } void Arch::setLutK(int K) { args.K = K; } @@ -268,16 +249,19 @@ void IdString::initialize_arch(const BaseCtx *ctx) {} BelId Arch::getBelByName(IdStringList name) const { - if (bels.count(name)) - return name; - return BelId(); + if (name.size() == 0) + return BelId(); + auto fnd = bel_by_name.find(name); + if (fnd == bel_by_name.end()) + NPNR_ASSERT_FALSE_STR("no bel named " + name.str(getCtx())); + return fnd->second; } -IdStringList Arch::getBelName(BelId bel) const { return bel; } +IdStringList Arch::getBelName(BelId bel) const { return bel_info(bel).name; } Loc Arch::getBelLocation(BelId bel) const { - auto &info = bels.at(bel); + auto &info = bel_info(bel); return Loc(info.x, info.y, info.z); } @@ -291,7 +275,7 @@ BelId Arch::getBelByLocation(Loc loc) const const std::vector &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); } -bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; } +bool Arch::getBelGlobalBuf(BelId bel) const { return bel_info(bel).gb; } uint32_t Arch::getBelChecksum(BelId bel) const { @@ -301,7 +285,7 @@ uint32_t Arch::getBelChecksum(BelId bel) const void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) { - bels.at(bel).bound_cell = cell; + bel_info(bel).bound_cell = cell; cell->bel = bel; cell->belStrength = strength; refreshUiBel(bel); @@ -309,40 +293,41 @@ void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) void Arch::unbindBel(BelId bel) { - bels.at(bel).bound_cell->bel = BelId(); - bels.at(bel).bound_cell->belStrength = STRENGTH_NONE; - bels.at(bel).bound_cell = nullptr; + auto &bi = bel_info(bel); + bi.bound_cell->bel = BelId(); + bi.bound_cell->belStrength = STRENGTH_NONE; + bi.bound_cell = nullptr; refreshUiBel(bel); } -bool Arch::checkBelAvail(BelId bel) const { return bels.at(bel).bound_cell == nullptr; } +bool Arch::checkBelAvail(BelId bel) const { return bel_info(bel).bound_cell == nullptr; } -CellInfo *Arch::getBoundBelCell(BelId bel) const { return bels.at(bel).bound_cell; } +CellInfo *Arch::getBoundBelCell(BelId bel) const { return bel_info(bel).bound_cell; } -CellInfo *Arch::getConflictingBelCell(BelId bel) const { return bels.at(bel).bound_cell; } +CellInfo *Arch::getConflictingBelCell(BelId bel) const { return bel_info(bel).bound_cell; } -const std::vector &Arch::getBels() const { return bel_ids; } +linear_range Arch::getBels() const { return linear_range(bels.size()); } -IdString Arch::getBelType(BelId bel) const { return bels.at(bel).type; } +IdString Arch::getBelType(BelId bel) const { return bel_info(bel).type; } -bool Arch::getBelHidden(BelId bel) const { return bels.at(bel).hidden; } +bool Arch::getBelHidden(BelId bel) const { return bel_info(bel).hidden; } -const std::map &Arch::getBelAttrs(BelId bel) const { return bels.at(bel).attrs; } +const std::map &Arch::getBelAttrs(BelId bel) const { return bel_info(bel).attrs; } WireId Arch::getBelPinWire(BelId bel, IdString pin) const { - const auto &bdata = bels.at(bel); + const auto &bdata = bel_info(bel); if (!bdata.pins.count(pin)) log_error("bel '%s' has no pin '%s'\n", getCtx()->nameOfBel(bel), pin.c_str(this)); return bdata.pins.at(pin).wire; } -PortType Arch::getBelPinType(BelId bel, IdString pin) const { return bels.at(bel).pins.at(pin).type; } +PortType Arch::getBelPinType(BelId bel, IdString pin) const { return bel_info(bel).pins.at(pin).type; } std::vector Arch::getBelPins(BelId bel) const { std::vector ret; - for (auto &it : bels.at(bel).pins) + for (auto &it : bel_info(bel).pins) ret.push_back(it.first); return ret; } @@ -356,26 +341,25 @@ const std::vector &Arch::getBelPinsForCellPin(const CellInfo *cell_inf WireId Arch::getWireByName(IdStringList name) const { - if (wires.count(name)) - return name; - return WireId(); + if (name.size() == 0) + return WireId(); + auto fnd = wire_by_name.find(name); + if (fnd == wire_by_name.end()) + NPNR_ASSERT_FALSE_STR("no wire named " + name.str(getCtx())); + return fnd->second; } -IdStringList Arch::getWireName(WireId wire) const { return wire; } +IdStringList Arch::getWireName(WireId wire) const { return wire_info(wire).name; } -IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; } +IdString Arch::getWireType(WireId wire) const { return wire_info(wire).type; } -const std::map &Arch::getWireAttrs(WireId wire) const { return wires.at(wire).attrs; } +const std::map &Arch::getWireAttrs(WireId wire) const { return wire_info(wire).attrs; } -uint32_t Arch::getWireChecksum(WireId wire) const -{ - // FIXME - return 0; -} +uint32_t Arch::getWireChecksum(WireId wire) const { return wire.index; } void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength) { - wires.at(wire).bound_net = net; + wire_info(wire).bound_net = net; net->wires[wire].pip = PipId(); net->wires[wire].strength = strength; refreshUiWire(wire); @@ -383,55 +367,54 @@ void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength) void Arch::unbindWire(WireId wire) { - auto &net_wires = wires.at(wire).bound_net->wires; + auto &net_wires = wire_info(wire).bound_net->wires; auto pip = net_wires.at(wire).pip; if (pip != PipId()) { - pips.at(pip).bound_net = nullptr; + pip_info(pip).bound_net = nullptr; refreshUiPip(pip); } net_wires.erase(wire); - wires.at(wire).bound_net = nullptr; + wire_info(wire).bound_net = nullptr; refreshUiWire(wire); } -bool Arch::checkWireAvail(WireId wire) const { return wires.at(wire).bound_net == nullptr; } +bool Arch::checkWireAvail(WireId wire) const { return wire_info(wire).bound_net == nullptr; } -NetInfo *Arch::getBoundWireNet(WireId wire) const { return wires.at(wire).bound_net; } +NetInfo *Arch::getBoundWireNet(WireId wire) const { return wire_info(wire).bound_net; } -NetInfo *Arch::getConflictingWireNet(WireId wire) const { return wires.at(wire).bound_net; } +NetInfo *Arch::getConflictingWireNet(WireId wire) const { return wire_info(wire).bound_net; } -const std::vector &Arch::getWireBelPins(WireId wire) const { return wires.at(wire).bel_pins; } +const std::vector &Arch::getWireBelPins(WireId wire) const { return wire_info(wire).bel_pins; } -const std::vector &Arch::getWires() const { return wire_ids; } +linear_range Arch::getWires() const { return linear_range(wires.size()); } // --------------------------------------------------------------- PipId Arch::getPipByName(IdStringList name) const { - if (pips.count(name)) - return name; - return PipId(); + if (name.size() == 0) + return PipId(); + auto fnd = pip_by_name.find(name); + if (fnd == pip_by_name.end()) + NPNR_ASSERT_FALSE_STR("no pip named " + name.str(getCtx())); + return fnd->second; } -IdStringList Arch::getPipName(PipId pip) const { return pip; } +IdStringList Arch::getPipName(PipId pip) const { return pip_info(pip).name; } -IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; } +IdString Arch::getPipType(PipId pip) const { return pip_info(pip).type; } -const std::map &Arch::getPipAttrs(PipId pip) const { return pips.at(pip).attrs; } +const std::map &Arch::getPipAttrs(PipId pip) const { return pip_info(pip).attrs; } -uint32_t Arch::getPipChecksum(PipId wire) const -{ - // FIXME - return 0; -} +uint32_t Arch::getPipChecksum(PipId pip) const { return pip.index; } void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) { - WireId wire = pips.at(pip).dstWire; - pips.at(pip).bound_net = net; - wires.at(wire).bound_net = net; + WireId wire = pip_info(pip).dstWire; + pip_info(pip).bound_net = net; + wire_info(wire).bound_net = net; net->wires[wire].pip = pip; net->wires[wire].strength = strength; refreshUiPip(pip); @@ -440,41 +423,44 @@ void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) void Arch::unbindPip(PipId pip) { - WireId wire = pips.at(pip).dstWire; - wires.at(wire).bound_net->wires.erase(wire); - pips.at(pip).bound_net = nullptr; - wires.at(wire).bound_net = nullptr; + WireId wire = pip_info(pip).dstWire; + wire_info(wire).bound_net->wires.erase(wire); + pip_info(pip).bound_net = nullptr; + wire_info(wire).bound_net = nullptr; refreshUiPip(pip); refreshUiWire(wire); } -bool Arch::checkPipAvail(PipId pip) const { return pips.at(pip).bound_net == nullptr; } +bool Arch::checkPipAvail(PipId pip) const { return pip_info(pip).bound_net == nullptr; } bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const { - NetInfo *bound_net = pips.at(pip).bound_net; + NetInfo *bound_net = pip_info(pip).bound_net; return bound_net == nullptr || bound_net == net; } -NetInfo *Arch::getBoundPipNet(PipId pip) const { return pips.at(pip).bound_net; } +NetInfo *Arch::getBoundPipNet(PipId pip) const { return pip_info(pip).bound_net; } -NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pips.at(pip).bound_net; } +NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pip_info(pip).bound_net; } -WireId Arch::getConflictingPipWire(PipId pip) const { return pips.at(pip).bound_net ? pips.at(pip).dstWire : WireId(); } +WireId Arch::getConflictingPipWire(PipId pip) const +{ + return pip_info(pip).bound_net ? pip_info(pip).dstWire : WireId(); +} -const std::vector &Arch::getPips() const { return pip_ids; } +linear_range Arch::getPips() const { return linear_range(pips.size()); } -Loc Arch::getPipLocation(PipId pip) const { return pips.at(pip).loc; } +Loc Arch::getPipLocation(PipId pip) const { return pip_info(pip).loc; } -WireId Arch::getPipSrcWire(PipId pip) const { return pips.at(pip).srcWire; } +WireId Arch::getPipSrcWire(PipId pip) const { return pip_info(pip).srcWire; } -WireId Arch::getPipDstWire(PipId pip) const { return pips.at(pip).dstWire; } +WireId Arch::getPipDstWire(PipId pip) const { return pip_info(pip).dstWire; } -DelayQuad Arch::getPipDelay(PipId pip) const { return DelayQuad(pips.at(pip).delay); } +DelayQuad Arch::getPipDelay(PipId pip) const { return DelayQuad(pip_info(pip).delay); } -const std::vector &Arch::getPipsDownhill(WireId wire) const { return wires.at(wire).downhill; } +const std::vector &Arch::getPipsDownhill(WireId wire) const { return wire_info(wire).downhill; } -const std::vector &Arch::getPipsUphill(WireId wire) const { return wires.at(wire).uphill; } +const std::vector &Arch::getPipsUphill(WireId wire) const { return wire_info(wire).uphill; } // --------------------------------------------------------------- @@ -502,8 +488,8 @@ const std::vector &Arch::getGroupGroups(GroupId group) const { return g delay_t Arch::estimateDelay(WireId src, WireId dst) const { - const WireInfo &s = wires.at(src); - const WireInfo &d = wires.at(dst); + const WireInfo &s = wire_info(src); + const WireInfo &d = wire_info(dst); int dx = abs(s.x - d.x); int dy = abs(s.y - d.y); return (dx + dy) * args.delayScale + args.delayOffset; @@ -527,10 +513,10 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const { ArcBounds bb; - int src_x = wires.at(src).x; - int src_y = wires.at(src).y; - int dst_x = wires.at(dst).x; - int dst_y = wires.at(dst).y; + int src_x = wire_info(src).x; + int src_y = wire_info(src).y; + int dst_x = wire_info(dst).x; + int dst_y = wire_info(dst).y; bb.x0 = src_x; bb.y0 = src_y; @@ -611,11 +597,11 @@ const std::vector &Arch::getDecalGraphics(DecalId decal) const return decal_graphics.at(decal); } -DecalXY Arch::getBelDecal(BelId bel) const { return bels.at(bel).decalxy; } +DecalXY Arch::getBelDecal(BelId bel) const { return bel_info(bel).decalxy; } -DecalXY Arch::getWireDecal(WireId wire) const { return wires.at(wire).decalxy; } +DecalXY Arch::getWireDecal(WireId wire) const { return wire_info(wire).decalxy; } -DecalXY Arch::getPipDecal(PipId pip) const { return pips.at(pip).decalxy; } +DecalXY Arch::getPipDecal(PipId pip) const { return pip_info(pip).decalxy; } DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decalxy; } diff --git a/generic/arch.h b/generic/arch.h index 7f574f31c5..885089ca9d 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -60,8 +60,6 @@ struct WireInfo std::map attrs; NetInfo *bound_net; std::vector downhill, uphill; - BelPin uphill_bel_pin; - std::vector downhill_bel_pins; std::vector bel_pins; DecalXY decalxy; int x, y; @@ -111,23 +109,40 @@ struct CellTiming dict> clockingInfo; }; +template struct linear_range +{ + struct iterator + { + explicit iterator(int32_t index) : index(index){}; + int32_t index; + bool operator==(const iterator &other) const { return index == other.index; } + bool operator!=(const iterator &other) const { return index != other.index; } + void operator++() { ++index; } + TId operator*() const { return TId(index); } + }; + explicit linear_range(int32_t size) : size(size){}; + int32_t size; + iterator begin() const { return iterator(0); } + iterator end() const { return iterator(size); } +}; + struct ArchRanges { using ArchArgsT = ArchArgs; // Bels - using AllBelsRangeT = const std::vector &; + using AllBelsRangeT = linear_range; using TileBelsRangeT = const std::vector &; using BelAttrsRangeT = const std::map &; using BelPinsRangeT = std::vector; using CellBelPinRangeT = const std::vector &; // Wires - using AllWiresRangeT = const std::vector &; + using AllWiresRangeT = linear_range; using DownhillPipRangeT = const std::vector &; using UphillPipRangeT = const std::vector &; using WireBelPinRangeT = const std::vector &; using WireAttrsRangeT = const std::map &; // Pips - using AllPipsRangeT = const std::vector &; + using AllPipsRangeT = linear_range; using PipAttrsRangeT = const std::map &; // Groups using AllGroupsRangeT = std::vector; @@ -147,17 +162,22 @@ struct Arch : ArchAPI { std::string chipName; - dict wires; - dict pips; - dict bels; + std::vector wires; + std::vector pips; + std::vector bels; dict groups; - // These functions include useful errors if not found - WireInfo &wire_info(IdStringList wire); - PipInfo &pip_info(IdStringList wire); - BelInfo &bel_info(IdStringList wire); + WireInfo &wire_info(WireId wire) { return wires.at(wire.index); } + PipInfo &pip_info(PipId pip) { return pips.at(pip.index); } + BelInfo &bel_info(BelId bel) { return bels.at(bel.index); } + + const WireInfo &wire_info(WireId wire) const { return wires.at(wire.index); } + const PipInfo &pip_info(PipId pip) const { return pips.at(pip.index); } + const BelInfo &bel_info(BelId bel) const { return bels.at(bel.index); } - std::vector bel_ids, wire_ids, pip_ids; + dict wire_by_name; + dict pip_by_name; + dict bel_by_name; dict bel_by_loc; std::vector>> bels_by_tile; @@ -170,17 +190,17 @@ struct Arch : ArchAPI dict cellTiming; - void addWire(IdStringList name, IdString type, int x, int y); - void addPip(IdStringList name, IdString type, IdStringList srcWire, IdStringList dstWire, delay_t delay, Loc loc); + WireId addWire(IdStringList name, IdString type, int x, int y); + PipId addPip(IdStringList name, IdString type, WireId srcWire, WireId dstWire, delay_t delay, Loc loc); - void addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden); - void addBelInput(IdStringList bel, IdString name, IdStringList wire); - void addBelOutput(IdStringList bel, IdString name, IdStringList wire); - void addBelInout(IdStringList bel, IdString name, IdStringList wire); + BelId addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden); + void addBelInput(BelId bel, IdString name, WireId wire); + void addBelOutput(BelId bel, IdString name, WireId wire); + void addBelInout(BelId bel, IdString name, WireId wire); - void addGroupBel(IdStringList group, IdStringList bel); - void addGroupWire(IdStringList group, IdStringList wire); - void addGroupPip(IdStringList group, IdStringList pip); + void addGroupBel(IdStringList group, BelId bel); + void addGroupWire(IdStringList group, WireId wire); + void addGroupPip(IdStringList group, PipId pip); void addGroupGroup(IdStringList group, IdStringList grp); void addDecalGraphic(DecalId decal, const GraphicElement &graphic); @@ -189,9 +209,9 @@ struct Arch : ArchAPI void setBelDecal(BelId bel, DecalXY decalxy); void setGroupDecal(GroupId group, DecalXY decalxy); - void setWireAttr(IdStringList wire, IdString key, const std::string &value); - void setPipAttr(IdStringList pip, IdString key, const std::string &value); - void setBelAttr(IdStringList bel, IdString key, const std::string &value); + void setWireAttr(WireId wire, IdString key, const std::string &value); + void setPipAttr(PipId pip, IdString key, const std::string &value); + void setBelAttr(BelId bel, IdString key, const std::string &value); void setLutK(int K); void setDelayScaling(double scale, double offset); @@ -234,7 +254,7 @@ struct Arch : ArchAPI bool checkBelAvail(BelId bel) const override; CellInfo *getBoundBelCell(BelId bel) const override; CellInfo *getConflictingBelCell(BelId bel) const override; - const std::vector &getBels() const override; + linear_range getBels() const override; IdString getBelType(BelId bel) const override; bool getBelHidden(BelId bel) const override; const std::map &getBelAttrs(BelId bel) const override; @@ -255,7 +275,7 @@ struct Arch : ArchAPI WireId getConflictingWireWire(WireId wire) const override { return wire; } NetInfo *getConflictingWireNet(WireId wire) const override; DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0); } - const std::vector &getWires() const override; + linear_range getWires() const override; const std::vector &getWireBelPins(WireId wire) const override; PipId getPipByName(IdStringList name) const override; @@ -270,7 +290,7 @@ struct Arch : ArchAPI NetInfo *getBoundPipNet(PipId pip) const override; WireId getConflictingPipWire(PipId pip) const override; NetInfo *getConflictingPipNet(PipId pip) const override; - const std::vector &getPips() const override; + linear_range getPips() const override; Loc getPipLocation(PipId pip) const override; WireId getPipSrcWire(PipId pip) const override; WireId getPipDstWire(PipId pip) const override; @@ -307,7 +327,7 @@ struct Arch : ArchAPI { pool cell_types; for (auto bel : bels) { - cell_types.insert(bel.second.type); + cell_types.insert(bel.type); } return std::vector{cell_types.begin(), cell_types.end()}; diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index df59b4fe76..92c782529b 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -42,6 +42,10 @@ void arch_wrap_python(py::module &m) { using namespace PythonConversion; + typedef linear_range BelRange; + typedef linear_range WireRange; + typedef linear_range AllPipRange; + auto arch_cls = py::class_(m, "Arch").def(py::init()); auto dxy_cls = py::class_>(m, "DecalXY_"); @@ -74,8 +78,8 @@ void arch_wrap_python(py::module &m) conv_from_str>::def_wrap(ctx_cls, "getBoundBelCell"); fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); - fn_wrapper_0a &>>::def_wrap(ctx_cls, "getBels"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "getBels"); fn_wrapper_2a, conv_from_str, conv_from_str>::def_wrap(ctx_cls, "getBelPinWire"); @@ -96,11 +100,11 @@ void arch_wrap_python(py::module &m) fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingWireNet"); - fn_wrapper_0a &>>::def_wrap(ctx_cls, "getWires"); + fn_wrapper_0a>::def_wrap( + ctx_cls, "getWires"); - fn_wrapper_0a &>>::def_wrap(ctx_cls, "getPips"); + fn_wrapper_0a>::def_wrap( + ctx_cls, "getPips"); fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getPipChecksum"); fn_wrapper_3a_v, @@ -156,50 +160,50 @@ void arch_wrap_python(py::module &m) "name"_a, "type"_a, "x"_a, "y"_a); fn_wrapper_6a_v, - conv_from_str, conv_from_str, conv_from_str, - pass_through, pass_through>::def_wrap(ctx_cls, "addPip", "name"_a, "type"_a, - "srcWire"_a, "dstWire"_a, "delay"_a, "loc"_a); + conv_from_str, conv_from_str, conv_from_str, pass_through, + pass_through>::def_wrap(ctx_cls, "addPip", "name"_a, "type"_a, "srcWire"_a, "dstWire"_a, + "delay"_a, "loc"_a); fn_wrapper_5a_v, conv_from_str, pass_through, pass_through, pass_through>::def_wrap(ctx_cls, "addBel", "name"_a, "type"_a, "loc"_a, "gb"_a, "hidden"_a); - fn_wrapper_3a_v, - conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelInput", "bel"_a, - "name"_a, "wire"_a); - fn_wrapper_3a_v, - conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelOutput", "bel"_a, - "name"_a, "wire"_a); - fn_wrapper_3a_v, - conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelInout", "bel"_a, - "name"_a, "wire"_a); + fn_wrapper_3a_v, + conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelInput", "bel"_a, "name"_a, + "wire"_a); + fn_wrapper_3a_v, + conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelOutput", "bel"_a, + "name"_a, "wire"_a); + fn_wrapper_3a_v, + conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelInout", "bel"_a, "name"_a, + "wire"_a); fn_wrapper_2a_v, - conv_from_str>::def_wrap(ctx_cls, "addGroupBel", "group"_a, "bel"_a); + conv_from_str>::def_wrap(ctx_cls, "addGroupBel", "group"_a, "bel"_a); fn_wrapper_2a_v, - conv_from_str>::def_wrap(ctx_cls, "addGroupWire", "group"_a, "wire"_a); + conv_from_str>::def_wrap(ctx_cls, "addGroupWire", "group"_a, "wire"_a); fn_wrapper_2a_v, - conv_from_str>::def_wrap(ctx_cls, "addGroupPip", "group"_a, "pip"_a); - fn_wrapper_2a_v, + conv_from_str>::def_wrap(ctx_cls, "addGroupPip", "group"_a, "pip"_a); + fn_wrapper_2a_v, conv_from_str>::def_wrap(ctx_cls, "addGroupGroup", "group"_a, "grp"_a); fn_wrapper_2a_v, pass_through>::def_wrap(ctx_cls, "addDecalGraphic", (py::arg("decal"), "graphic")); - fn_wrapper_2a_v, + fn_wrapper_2a_v, unwrap_context>::def_wrap(ctx_cls, "setWireDecal", "wire"_a, "decalxy"_a); - fn_wrapper_2a_v, + fn_wrapper_2a_v, unwrap_context>::def_wrap(ctx_cls, "setPipDecal", "pip"_a, "decalxy"_a); - fn_wrapper_2a_v, + fn_wrapper_2a_v, unwrap_context>::def_wrap(ctx_cls, "setBelDecal", "bel"_a, "decalxy"_a); fn_wrapper_2a_v, unwrap_context>::def_wrap(ctx_cls, "setGroupDecal", "group"_a, "decalxy"_a); - fn_wrapper_3a_v, + fn_wrapper_3a_v, conv_from_str, pass_through>::def_wrap(ctx_cls, "setWireAttr", "wire"_a, "key"_a, "value"_a); - fn_wrapper_3a_v, + fn_wrapper_3a_v, conv_from_str, pass_through>::def_wrap(ctx_cls, "setBelAttr", "bel"_a, "key"_a, "value"_a); - fn_wrapper_3a_v, + fn_wrapper_3a_v, conv_from_str, pass_through>::def_wrap(ctx_cls, "setPipAttr", "pip"_a, "key"_a, "value"_a); @@ -254,6 +258,10 @@ void arch_wrap_python(py::module &m) pass_through, conv_from_str, conv_from_str>::def_wrap(ctx_cls, "isValidBelForCellType"); + WRAP_RANGE(m, Bel, conv_to_str); + WRAP_RANGE(m, Wire, conv_to_str); + WRAP_RANGE(m, AllPip, conv_to_str); + WRAP_MAP_UPTR(m, CellMap, "IdCellMap"); WRAP_MAP_UPTR(m, NetMap, "IdNetMap"); WRAP_MAP(m, HierarchyMap, wrap_context, "HierarchyMap"); diff --git a/generic/arch_pybindings.h b/generic/arch_pybindings.h index 9a5735408c..72d688dc61 100644 --- a/generic/arch_pybindings.h +++ b/generic/arch_pybindings.h @@ -26,6 +26,73 @@ NEXTPNR_NAMESPACE_BEGIN +namespace PythonConversion { + +template <> struct string_converter +{ + BelId from_str(Context *ctx, std::string name) { return ctx->getBelByNameStr(name); } + + std::string to_str(Context *ctx, BelId id) + { + if (id == BelId()) + throw bad_wrap(); + return ctx->getBelName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + WireId from_str(Context *ctx, std::string name) { return ctx->getWireByNameStr(name); } + + std::string to_str(Context *ctx, WireId id) + { + if (id == WireId()) + throw bad_wrap(); + return ctx->getWireName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + WireId from_str(Context *ctx, std::string name) { return ctx->getWireByNameStr(name); } + + std::string to_str(Context *ctx, WireId id) + { + if (id == WireId()) + throw bad_wrap(); + return ctx->getWireName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + PipId from_str(Context *ctx, std::string name) { return ctx->getPipByNameStr(name); } + + std::string to_str(Context *ctx, PipId id) + { + if (id == PipId()) + throw bad_wrap(); + return ctx->getPipName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + BelPin from_str(Context *ctx, std::string name) + { + NPNR_ASSERT_FALSE("string_converter::from_str not implemented"); + } + + std::string to_str(Context *ctx, BelPin pin) + { + if (pin.bel == BelId()) + throw bad_wrap(); + return ctx->getBelName(pin.bel).str(ctx) + "/" + pin.pin.str(ctx); + } +}; + +} // namespace PythonConversion + NEXTPNR_NAMESPACE_END #endif #endif diff --git a/generic/archdefs.h b/generic/archdefs.h index c46fba93c0..2d46c0a2d1 100644 --- a/generic/archdefs.h +++ b/generic/archdefs.h @@ -27,9 +27,42 @@ NEXTPNR_NAMESPACE_BEGIN typedef float delay_t; -typedef IdStringList BelId; -typedef IdStringList WireId; -typedef IdStringList PipId; +struct BelId +{ + BelId() : index(-1){}; + explicit BelId(int32_t index) : index(index){}; + int32_t index = -1; + + bool operator==(const BelId &other) const { return index == other.index; } + bool operator!=(const BelId &other) const { return index != other.index; } + bool operator<(const BelId &other) const { return index < other.index; } + unsigned int hash() const { return index; } +}; + +struct WireId +{ + WireId() : index(-1){}; + explicit WireId(int32_t index) : index(index){}; + int32_t index = -1; + + bool operator==(const WireId &other) const { return index == other.index; } + bool operator!=(const WireId &other) const { return index != other.index; } + bool operator<(const WireId &other) const { return index < other.index; } + unsigned int hash() const { return index; } +}; + +struct PipId +{ + PipId() : index(-1){}; + explicit PipId(int32_t index) : index(index){}; + int32_t index = -1; + + bool operator==(const PipId &other) const { return index == other.index; } + bool operator!=(const PipId &other) const { return index != other.index; } + bool operator<(const PipId &other) const { return index < other.index; } + unsigned int hash() const { return index; } +}; + typedef IdStringList GroupId; typedef IdStringList DecalId; typedef IdString BelBucketId; diff --git a/generic/examples/write_fasm.py b/generic/examples/write_fasm.py index ede8f16b51..057e779cad 100644 --- a/generic/examples/write_fasm.py +++ b/generic/examples/write_fasm.py @@ -29,7 +29,7 @@ def write_fasm(ctx, paramCfg, f): for nname, net in sorted(ctx.nets, key=lambda x: str(x[1].name)): print("# Net %s" % nname, file=f) for wire, pip in sorted(net.wires, key=lambda x: str(x[1])): - if pip.pip != "": + if pip.pip is not None: print("%s" % pip.pip, file=f) print("", file=f) for cname, cell in sorted(ctx.cells, key=lambda x: str(x[1].name)): From 69a4e3e544f3b0cff296a01c51d325d23e265fb1 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 30 Dec 2021 21:32:04 +0000 Subject: [PATCH 038/712] SSOArray: Implement move and assignment operators Signed-off-by: gatecat --- common/sso_array.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/common/sso_array.h b/common/sso_array.h index 1fae6c57be..80e7d1c1fd 100644 --- a/common/sso_array.h +++ b/common/sso_array.h @@ -70,6 +70,26 @@ template class SSOArray std::copy(other.begin(), other.end(), begin()); } + SSOArray(SSOArray &&other) : m_size(other.size()) + { + if (is_heap()) + data_heap = other.data_heap; + else + std::copy(other.begin(), other.end(), begin()); + other.m_size = 0; + } + SSOArray &operator=(const SSOArray &other) + { + if (&other == this) + return *this; + if (is_heap()) + delete[] data_heap; + m_size = other.m_size; + alloc(); + std::copy(other.begin(), other.end(), begin()); + return *this; + } + template SSOArray(const Tother &other) : m_size(other.size()) { alloc(); From 045ce3f148a90ae4dc8751f856d39a985384d0cc Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 3 Jan 2022 17:48:31 +1000 Subject: [PATCH 039/712] gowin: Fix last MUX8 In fact, there is also an input/output column. Signed-off-by: YRabbit --- gowin/arch.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 2f61685a33..8c654b2eb9 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -612,7 +612,7 @@ void Arch::addMuxBels(const DatabasePOD *db, int row, int col) int gcol = col + 1; // no MUX2_LUT8 in the last column - if (j == 7 && col == getGridDimX() - 1) { + if (j == 7 && col == getGridDimX() - 2) { continue; } From e88bd34c02272ecdad6295123941d9040fa4f043 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 30 Dec 2021 13:18:34 +0000 Subject: [PATCH 040/712] Viaduct API for a hybrid between generic and full-custom arch Signed-off-by: gatecat --- generic/arch.cc | 60 +++- generic/arch.h | 33 +- generic/archdefs.h | 5 +- generic/family.cmake | 7 + generic/main.cc | 15 +- generic/pack.cc | 14 +- generic/viaduct/example/.gitignore | 1 + generic/viaduct/example/constids.inc | 14 + generic/viaduct/example/example.cc | 306 ++++++++++++++++++ generic/viaduct/example/example_map.v | 12 + generic/viaduct/example/example_prims.v | 35 ++ .../viaduct/example/synth_viaduct_example.tcl | 24 ++ generic/viaduct/example/viaduct_example.sh | 6 + generic/viaduct_api.cc | 118 +++++++ generic/viaduct_api.h | 114 +++++++ generic/viaduct_constids.h | 74 +++++ generic/viaduct_helpers.cc | 163 ++++++++++ generic/viaduct_helpers.h | 82 +++++ 18 files changed, 1055 insertions(+), 28 deletions(-) create mode 100644 generic/viaduct/example/.gitignore create mode 100644 generic/viaduct/example/constids.inc create mode 100644 generic/viaduct/example/example.cc create mode 100644 generic/viaduct/example/example_map.v create mode 100644 generic/viaduct/example/example_prims.v create mode 100644 generic/viaduct/example/synth_viaduct_example.tcl create mode 100755 generic/viaduct/example/viaduct_example.sh create mode 100644 generic/viaduct_api.cc create mode 100644 generic/viaduct_api.h create mode 100644 generic/viaduct_constids.h create mode 100644 generic/viaduct_helpers.cc create mode 100644 generic/viaduct_helpers.h diff --git a/generic/arch.cc b/generic/arch.cc index d899bd0b68..ad054efd6d 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -25,6 +25,7 @@ #include "router1.h" #include "router2.h" #include "util.h" +#include "viaduct_api.h" NEXTPNR_NAMESPACE_BEGIN @@ -285,6 +286,8 @@ uint32_t Arch::getBelChecksum(BelId bel) const void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) { + if (uarch) + uarch->notifyBelChange(bel, cell); bel_info(bel).bound_cell = cell; cell->bel = bel; cell->belStrength = strength; @@ -293,6 +296,8 @@ void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) void Arch::unbindBel(BelId bel) { + if (uarch) + uarch->notifyBelChange(bel, nullptr); auto &bi = bel_info(bel); bi.bound_cell->bel = BelId(); bi.bound_cell->belStrength = STRENGTH_NONE; @@ -300,7 +305,10 @@ void Arch::unbindBel(BelId bel) refreshUiBel(bel); } -bool Arch::checkBelAvail(BelId bel) const { return bel_info(bel).bound_cell == nullptr; } +bool Arch::checkBelAvail(BelId bel) const +{ + return (!uarch || uarch->checkBelAvail(bel)) && (bel_info(bel).bound_cell == nullptr); +} CellInfo *Arch::getBoundBelCell(BelId bel) const { return bel_info(bel).bound_cell; } @@ -359,6 +367,8 @@ uint32_t Arch::getWireChecksum(WireId wire) const { return wire.index; } void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength) { + if (uarch) + uarch->notifyWireChange(wire, net); wire_info(wire).bound_net = net; net->wires[wire].pip = PipId(); net->wires[wire].strength = strength; @@ -371,16 +381,22 @@ void Arch::unbindWire(WireId wire) auto pip = net_wires.at(wire).pip; if (pip != PipId()) { + if (uarch) + uarch->notifyPipChange(pip, nullptr); pip_info(pip).bound_net = nullptr; refreshUiPip(pip); } + uarch->notifyWireChange(wire, nullptr); net_wires.erase(wire); wire_info(wire).bound_net = nullptr; refreshUiWire(wire); } -bool Arch::checkWireAvail(WireId wire) const { return wire_info(wire).bound_net == nullptr; } +bool Arch::checkWireAvail(WireId wire) const +{ + return (!uarch || uarch->checkWireAvail(wire)) && (wire_info(wire).bound_net == nullptr); +} NetInfo *Arch::getBoundWireNet(WireId wire) const { return wire_info(wire).bound_net; } @@ -413,6 +429,10 @@ uint32_t Arch::getPipChecksum(PipId pip) const { return pip.index; } void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) { WireId wire = pip_info(pip).dstWire; + if (uarch) { + uarch->notifyPipChange(pip, net); + uarch->notifyWireChange(wire, net); + } pip_info(pip).bound_net = net; wire_info(wire).bound_net = net; net->wires[wire].pip = pip; @@ -424,6 +444,10 @@ void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) void Arch::unbindPip(PipId pip) { WireId wire = pip_info(pip).dstWire; + if (uarch) { + uarch->notifyPipChange(pip, nullptr); + uarch->notifyWireChange(wire, nullptr); + } wire_info(wire).bound_net->wires.erase(wire); pip_info(pip).bound_net = nullptr; wire_info(wire).bound_net = nullptr; @@ -431,10 +455,15 @@ void Arch::unbindPip(PipId pip) refreshUiWire(wire); } -bool Arch::checkPipAvail(PipId pip) const { return pip_info(pip).bound_net == nullptr; } +bool Arch::checkPipAvail(PipId pip) const +{ + return (!uarch || uarch->checkPipAvail(pip)) && (pip_info(pip).bound_net == nullptr); +} bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const { + if (uarch && !uarch->checkPipAvailForNet(pip, net)) + return false; NetInfo *bound_net = pip_info(pip).bound_net; return bound_net == nullptr || bound_net == net; } @@ -488,6 +517,8 @@ const std::vector &Arch::getGroupGroups(GroupId group) const { return g delay_t Arch::estimateDelay(WireId src, WireId dst) const { + if (uarch) + return uarch->estimateDelay(src, dst); const WireInfo &s = wire_info(src); const WireInfo &d = wire_info(dst); int dx = abs(s.x - d.x); @@ -497,8 +528,8 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - NPNR_UNUSED(src_pin); - NPNR_UNUSED(dst_pin); + if (uarch) + return uarch->predictDelay(src_bel, src_pin, dst_bel, dst_pin); auto driver_loc = getBelLocation(src_bel); auto sink_loc = getBelLocation(dst_bel); @@ -511,6 +542,8 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const { + if (uarch) + return uarch->getRouteBoundingBox(src, dst); ArcBounds bb; int src_x = wire_info(src).x; @@ -537,6 +570,8 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bool Arch::place() { + if (uarch) + uarch->prePlace(); std::string placer = str_or_default(settings, id("placer"), defaultPlacer); if (placer == "heap") { bool have_iobuf_or_constr = false; @@ -548,7 +583,7 @@ bool Arch::place() } } bool retVal; - if (!have_iobuf_or_constr) { + if (!have_iobuf_or_constr && !uarch) { log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to " "SA.\n"); retVal = placer1(getCtx(), Placer1Cfg(getCtx())); @@ -557,11 +592,15 @@ bool Arch::place() cfg.ioBufTypes.insert(id("GENERIC_IOB")); retVal = placer_heap(getCtx(), cfg); } + if (uarch) + uarch->postPlace(); getCtx()->settings[getCtx()->id("place")] = 1; archInfoToAttributes(); return retVal; } else if (placer == "sa") { bool retVal = placer1(getCtx(), Placer1Cfg(getCtx())); + if (uarch) + uarch->postPlace(); getCtx()->settings[getCtx()->id("place")] = 1; archInfoToAttributes(); return retVal; @@ -572,6 +611,8 @@ bool Arch::place() bool Arch::route() { + if (uarch) + uarch->preRoute(); std::string router = str_or_default(settings, id("router"), defaultRouter); bool result; if (router == "router1") { @@ -582,6 +623,8 @@ bool Arch::route() } else { log_error("iCE40 architecture does not support router '%s'\n", router.c_str()); } + if (uarch) + uarch->postRoute(); getCtx()->settings[getCtx()->id("route")] = 1; archInfoToAttributes(); return result; @@ -644,6 +687,8 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port bool Arch::isBelLocationValid(BelId bel) const { + if (uarch) + return uarch->isBelLocationValid(bel); std::vector cells; Loc loc = getBelLocation(bel); for (auto tbel : getBelsByTile(loc.x, loc.y)) { @@ -671,6 +716,7 @@ const std::vector Arch::availableRouters = {"router1", "router2"}; void Arch::assignArchInfo() { + int index = 0; for (auto &cell : getCtx()->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id("GENERIC_SLICE")) { @@ -684,6 +730,8 @@ void Arch::assignArchInfo() for (auto &p : ci->ports) if (!ci->bel_pins.count(p.first)) ci->bel_pins.emplace(p.first, std::vector{p.first}); + ci->flat_index = index; + ++index; } } diff --git a/generic/arch.h b/generic/arch.h index 885089ca9d..e96853f1de 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -23,10 +23,12 @@ #include #include "arch_api.h" +#include "base_arch.h" #include "idstring.h" #include "idstringlist.h" #include "nextpnr_namespaces.h" #include "nextpnr_types.h" +#include "viaduct_api.h" NEXTPNR_NAMESPACE_BEGIN @@ -126,7 +128,7 @@ template struct linear_range iterator end() const { return iterator(size); } }; -struct ArchRanges +struct ArchRanges : BaseArchRanges { using ArchArgsT = ArchArgs; // Bels @@ -158,9 +160,10 @@ struct ArchRanges using BucketBelRangeT = std::vector; }; -struct Arch : ArchAPI +struct Arch : BaseArch { std::string chipName; + std::unique_ptr uarch{}; std::vector wires; std::vector pips; @@ -325,6 +328,8 @@ struct Arch : ArchAPI std::vector getCellTypes() const override { + if (uarch) + return uarch->getCellTypes(); pool cell_types; for (auto bel : bels) { cell_types.insert(bel.type); @@ -339,9 +344,15 @@ struct Arch : ArchAPI BelBucketId getBelBucketByName(IdString bucket) const override { return bucket; } - BelBucketId getBelBucketForBel(BelId bel) const override { return getBelType(bel); } + BelBucketId getBelBucketForBel(BelId bel) const override + { + return uarch ? uarch->getBelBucketForBel(bel) : getBelType(bel); + } - BelBucketId getBelBucketForCellType(IdString cell_type) const override { return cell_type; } + BelBucketId getBelBucketForCellType(IdString cell_type) const override + { + return uarch ? uarch->getBelBucketForCellType(cell_type) : cell_type; + } std::vector getBelsInBucket(BelBucketId bucket) const override { @@ -366,19 +377,11 @@ struct Arch : ArchAPI // Get the TimingClockingInfo of a port TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override; - bool isValidBelForCellType(IdString cell_type, BelId bel) const override { return cell_type == getBelType(bel); } - bool isBelLocationValid(BelId bel) const override; - - // TODO - CellInfo *getClusterRootCell(ClusterId cluster) const override { NPNR_ASSERT_FALSE("unimplemented"); } - ArcBounds getClusterBounds(ClusterId cluster) const override { NPNR_ASSERT_FALSE("unimplemented"); } - Loc getClusterOffset(const CellInfo *cell) const override { NPNR_ASSERT_FALSE("unimplemented"); } - bool isClusterStrict(const CellInfo *cell) const override { NPNR_ASSERT_FALSE("unimplemented"); } - bool getClusterPlacement(ClusterId cluster, BelId root_bel, - std::vector> &placement) const override + bool isValidBelForCellType(IdString cell_type, BelId bel) const override { - NPNR_ASSERT_FALSE("unimplemented"); + return uarch ? uarch->isValidBelForCellType(cell_type, bel) : cell_type == getBelType(bel); } + bool isBelLocationValid(BelId bel) const override; static const std::string defaultPlacer; static const std::vector availablePlacers; diff --git a/generic/archdefs.h b/generic/archdefs.h index 2d46c0a2d1..4e91ffd3ab 100644 --- a/generic/archdefs.h +++ b/generic/archdefs.h @@ -20,6 +20,7 @@ #ifndef GENERIC_ARCHDEFS_H #define GENERIC_ARCHDEFS_H +#include "base_clusterinfo.h" #include "hashlib.h" #include "idstringlist.h" @@ -74,7 +75,7 @@ struct ArchNetInfo struct NetInfo; -struct ArchCellInfo +struct ArchCellInfo : BaseClusterInfo { // Custom grouping set via "PACK_GROUP" attribute. All cells with the same group // value may share a tile (-1 = don't care, default if not set) @@ -83,6 +84,8 @@ struct ArchCellInfo bool is_slice; // Only packing rule for slice type primitives is a single clock per tile const NetInfo *slice_clk; + // A flat index for cells; so viaduct uarches can have their own fast flat arrays of per-cell validity-related data + int flat_index; // Cell to bel pin mapping dict> bel_pins; }; diff --git a/generic/family.cmake b/generic/family.cmake index e69de29bb2..cd4e3801e4 100644 --- a/generic/family.cmake +++ b/generic/family.cmake @@ -0,0 +1,7 @@ +set(VIADUCT_UARCHES "example") +foreach(uarch ${VIADUCT_UARCHES}) + aux_source_directory(${family}/viaduct/${uarch} UARCH_FILES) + foreach(target ${family_targets}) + target_sources(${target} PRIVATE ${UARCH_FILES}) + endforeach() +endforeach(uarch) diff --git a/generic/main.cc b/generic/main.cc index 387df6c6ed..d08ae3819a 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -44,8 +44,10 @@ GenericCommandHandler::GenericCommandHandler(int argc, char **argv) : CommandHan po::options_description GenericCommandHandler::getArchOptions() { + std::string all_uarches = ViaductArch::list(); + std::string uarch_help = stringf("viaduct micro-arch to use (available: %s)", all_uarches.c_str()); po::options_description specific("Architecture specific options"); - specific.add_options()("generic", "set device type to generic"); + specific.add_options()("uarch", po::value(), uarch_help.c_str()); specific.add_options()("no-iobs", "disable automatic IO buffer insertion"); return specific; } @@ -63,6 +65,17 @@ std::unique_ptr GenericCommandHandler::createContext(dict(new Context(chipArgs)); if (vm.count("no-iobs")) ctx->settings[ctx->id("disable_iobs")] = Property::State::S1; + if (vm.count("uarch")) { + std::string uarch_name = vm["uarch"].as(); + dict args; // TODO + auto uarch = ViaductArch::create(uarch_name, args); + if (!uarch) { + std::string all_uarches = ViaductArch::list(); + log_error("Unknown viaduct uarch '%s'; available options: '%s'\n", uarch_name.c_str(), all_uarches.c_str()); + } + ctx->uarch = std::move(uarch); + ctx->uarch->init(ctx.get()); + } return ctx; } diff --git a/generic/pack.cc b/generic/pack.cc index 32dae5538a..291a528dae 100644 --- a/generic/pack.cc +++ b/generic/pack.cc @@ -276,12 +276,16 @@ bool Arch::pack() Context *ctx = getCtx(); try { log_break(); - pack_constants(ctx); - pack_io(ctx); - pack_lut_lutffs(ctx); - pack_nonlut_ffs(ctx); - ctx->settings[ctx->id("pack")] = 1; + if (uarch) { + uarch->pack(); + } else { + pack_constants(ctx); + pack_io(ctx); + pack_lut_lutffs(ctx); + pack_nonlut_ffs(ctx); + } ctx->assignArchInfo(); + ctx->settings[ctx->id("pack")] = 1; log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; } catch (log_execution_error_exception) { diff --git a/generic/viaduct/example/.gitignore b/generic/viaduct/example/.gitignore new file mode 100644 index 0000000000..a6c57f5fb2 --- /dev/null +++ b/generic/viaduct/example/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/generic/viaduct/example/constids.inc b/generic/viaduct/example/constids.inc new file mode 100644 index 0000000000..b40d5be8a0 --- /dev/null +++ b/generic/viaduct/example/constids.inc @@ -0,0 +1,14 @@ +X(LUT4) +X(DFF) +X(CLK) +X(D) +X(F) +X(Q) +X(INBUF) +X(OUTBUF) +X(I) +X(EN) +X(O) +X(IOB) +X(PAD) +X(INIT) diff --git a/generic/viaduct/example/example.cc b/generic/viaduct/example/example.cc new file mode 100644 index 0000000000..3d1c201cbf --- /dev/null +++ b/generic/viaduct/example/example.cc @@ -0,0 +1,306 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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" +#include "viaduct_api.h" +#include "viaduct_helpers.h" + +#define GEN_INIT_CONSTIDS +#define VIADUCT_CONSTIDS "viaduct/example/constids.inc" +#include "viaduct_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct ExampleImpl : ViaductAPI +{ + ~ExampleImpl(){}; + void init(Context *ctx) override + { + init_uarch_constids(ctx); + ViaductAPI::init(ctx); + h.init(ctx); + init_wires(); + init_bels(); + init_pips(); + } + + void pack() override + { + // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis + const pool top_ports{ + CellTypePort(id_INBUF, id_PAD), + CellTypePort(id_OUTBUF, id_PAD), + }; + h.remove_nextpnr_iobs(top_ports); + // Replace constants with LUTs + const dict vcc_params = {{id_INIT, Property(0xFFFF, 16)}}; + const dict gnd_params = {{id_INIT, Property(0x0000, 16)}}; + h.replace_constants(CellTypePort(id_LUT4, id_F), CellTypePort(id_LUT4, id_F), vcc_params, gnd_params); + // Constrain directly connected LUTs and FFs together to use dedicated resources + int lutffs = h.constrain_cell_pairs(pool{{id_LUT4, id_F}}, pool{{id_DFF, id_D}}, 1); + log_info("Constrained %d LUTFF pairs.\n", lutffs); + } + + void prePlace() override { assign_cell_info(); } + + bool isBelLocationValid(BelId bel) const override + { + Loc l = ctx->getBelLocation(bel); + if (is_io(l.x, l.y)) { + return true; + } else { + return slice_valid(l.x, l.y, l.z / 2); + } + } + + private: + ViaductHelpers h; + // Configuration + // Grid size including IOBs at edges + const int X = 32, Y = 32; + // SLICEs per tile + const int N = 8; + // LUT input count + const int K = 4; + // Number of local wires + const int Wl = N * (K + 1) + 8; + // 1/Fc for bel input wire pips; local wire pips and neighbour pips + const int Si = 4, Sq = 4, Sl = 8; + + // For fast wire lookups + struct TileWires + { + std::vector clk, q, f, d, i; + std::vector l; + std::vector pad; + }; + + std::vector> wires_by_tile; + + // Create wires to attach to bels and pips + void init_wires() + { + log_info("Creating wires...\n"); + wires_by_tile.resize(Y); + for (int y = 0; y < Y; y++) { + auto &row_wires = wires_by_tile.at(y); + row_wires.resize(X); + for (int x = 0; x < X; x++) { + auto &w = row_wires.at(x); + for (int z = 0; z < N; z++) { + // Clock input + w.clk.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("CLK%d", z))), ctx->id("CLK"), x, y)); + // FF input + w.d.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("D%d", z))), ctx->id("D"), x, y)); + // FF and LUT outputs + w.q.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("Q%d", z))), ctx->id("Q"), x, y)); + w.f.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("F%d", z))), ctx->id("F"), x, y)); + // LUT inputs + for (int i = 0; i < K; i++) + w.i.push_back( + ctx->addWire(h.xy_id(x, y, ctx->id(stringf("L%dI%d", z, i))), ctx->id("I"), x, y)); + } + // Local wires + for (int l = 0; l < Wl; l++) + w.l.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("LOCAL%d", l))), ctx->id("LOCAL"), x, y)); + // Pad wires for IO + if (is_io(x, y) && x != y) + for (int z = 0; z < 2; z++) + w.pad.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("PAD%d", z))), id_PAD, x, y)); + } + } + } + bool is_io(int x, int y) const + { + // IO are on the edges of the device + return (x == 0) || (x == (X - 1)) || (y == 0) || (y == (Y - 1)); + } + // Create IO bels in an IO tile + void add_io_bels(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + for (int z = 0; z < 2; z++) { + BelId b = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("IO%d", z))), id_IOB, Loc(x, y, z), false, false); + ctx->addBelInout(b, id_PAD, w.pad.at(z)); + ctx->addBelInput(b, id_I, w.i.at(z * K + 0)); + ctx->addBelInput(b, id_EN, w.i.at(z * K + 1)); + ctx->addBelOutput(b, id_O, w.q.at(z)); + } + } + PipId add_pip(Loc loc, WireId src, WireId dst, delay_t delay = 0.05) + { + IdStringList name = IdStringList::concat(ctx->getWireName(dst), ctx->getWireName(src)); + return ctx->addPip(name, ctx->id("PIP"), src, dst, delay, loc); + } + // Create LUT and FF bels in a logic tile + void add_slice_bels(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + for (int z = 0; z < N; z++) { + // Create LUT bel + BelId lut = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_LUT", z))), id_LUT4, Loc(x, y, z * 2), false, + false); + for (int k = 0; k < K; k++) + ctx->addBelInput(lut, ctx->id(stringf("I[%d]", k)), w.i.at(z * K + k)); + ctx->addBelOutput(lut, id_F, w.f.at(z)); + // FF data can come from LUT output or LUT I3 + add_pip(Loc(x, y, 0), w.f.at(z), w.d.at(z)); + add_pip(Loc(x, y, 0), w.i.at(z * K + (K - 1)), w.d.at(z)); + // Create DFF bel + BelId dff = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_FF", z))), id_DFF, Loc(x, y, z * 2 + 1), + false, false); + ctx->addBelInput(dff, id_CLK, w.clk.at(z)); + ctx->addBelInput(dff, id_D, w.d.at(z)); + ctx->addBelOutput(dff, id_Q, w.q.at(z)); + } + } + // Create bels according to tile type + void init_bels() + { + log_info("Creating bels...\n"); + for (int y = 0; y < Y; y++) { + for (int x = 0; x < X; x++) { + if (is_io(x, y)) { + if (x == y) + continue; // don't put IO in corners + add_io_bels(x, y); + } else { + add_slice_bels(x, y); + } + } + } + } + + // Create PIPs inside a tile; following an example synthetic routing pattern + void add_tile_pips(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + Loc loc(x, y, 0); + auto create_input_pips = [&](WireId dst, int offset, int skip) { + for (int i = (offset % skip); i < Wl; i += skip) + add_pip(loc, w.l.at(i), dst, 0.05); + }; + for (int z = 0; z < N; z++) { + create_input_pips(w.clk.at(z), 0, Si); + for (int k = 0; k < K; k++) + create_input_pips(w.i.at(z * K + k), k, Si); + } + auto create_output_pips = [&](WireId dst, int offset, int skip) { + for (int z = (offset % skip); z < N; z += skip) { + add_pip(loc, w.f.at(z), dst, 0.05); + add_pip(loc, w.q.at(z), dst, 0.05); + } + }; + auto create_neighbour_pips = [&](WireId dst, int nx, int ny, int offset, int skip) { + if (nx < 0 || nx >= X) + return; + if (ny < 0 || ny >= Y) + return; + auto &nw = wires_by_tile.at(ny).at(nx); + for (int i = (offset % skip); i < Wl; i += skip) + add_pip(loc, dst, nw.l.at(i), 0.1); + }; + for (int i = 0; i < Wl; i++) { + WireId dst = w.l.at(i); + create_output_pips(dst, i % Sq, Sq); + create_neighbour_pips(dst, x - 1, y - 1, (i + 1) % Sl, Sl); + create_neighbour_pips(dst, x - 1, y, (i + 2) % Sl, Sl); + create_neighbour_pips(dst, x - 1, y + 1, (i + 3) % Sl, Sl); + create_neighbour_pips(dst, x, y - 1, (i + 4) % Sl, Sl); + create_neighbour_pips(dst, x, y + 1, (i + 5) % Sl, Sl); + create_neighbour_pips(dst, x + 1, y - 1, (i + 6) % Sl, Sl); + create_neighbour_pips(dst, x + 1, y, (i + 7) % Sl, Sl); + create_neighbour_pips(dst, x + 1, y + 1, (i + 8) % Sl, Sl); + } + } + void init_pips() + { + log_info("Creating pips...\n"); + for (int y = 0; y < Y; y++) + for (int x = 0; x < X; x++) + add_tile_pips(x, y); + } + // Validity checking + struct ExampleCellInfo + { + const NetInfo *lut_f = nullptr, *ff_d = nullptr; + bool lut_i3_used = false; + }; + std::vector fast_cell_info; + void assign_cell_info() + { + fast_cell_info.resize(ctx->cells.size()); + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + auto &fc = fast_cell_info.at(ci->flat_index); + if (ci->type == id_LUT4) { + fc.lut_f = get_net_or_empty(ci, id_F); + fc.lut_i3_used = (get_net_or_empty(ci, ctx->id(stringf("I[%d]", K - 1))) != nullptr); + } else if (ci->type == id_DFF) { + fc.ff_d = get_net_or_empty(ci, id_D); + } + } + } + bool slice_valid(int x, int y, int z) const + { + const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2))); + const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1))); + if (!lut || !ff) + return true; // always valid if only LUT or FF used + const auto &lut_data = fast_cell_info.at(lut->flat_index); + const auto &ff_data = fast_cell_info.at(ff->flat_index); + // In our example arch; the FF D can either be driven from LUT F or LUT I3 + // so either; FF D must equal LUT F or LUT I3 must be unused + if (ff_data.ff_d == lut_data.lut_f) + return true; + if (lut_data.lut_i3_used) + return false; + return true; + } + // Bel bucket functions + IdString getBelBucketForCellType(IdString cell_type) const override + { + if (cell_type.in(id_INBUF, id_OUTBUF)) + return id_IOB; + return cell_type; + } + bool isValidBelForCellType(IdString cell_type, BelId bel) const override + { + IdString bel_type = ctx->getBelType(bel); + if (bel_type == id_IOB) + return cell_type.in(id_INBUF, id_OUTBUF); + else + return (bel_type == cell_type); + } +}; + +struct ExampleArch : ViaductArch +{ + ExampleArch() : ViaductArch("example"){}; + std::unique_ptr create(const dict &args) + { + return std::make_unique(); + } +} exampleArch; +} // namespace + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct/example/example_map.v b/generic/viaduct/example/example_map.v new file mode 100644 index 0000000000..f701f0ed7a --- /dev/null +++ b/generic/viaduct/example/example_map.v @@ -0,0 +1,12 @@ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + + localparam rep = 1<<(4-WIDTH); + + LUT4 #(.INIT({rep{LUT}})) _TECHMAP_REPLACE_ (.I(A), .F(Y)); +endmodule + +module \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule diff --git a/generic/viaduct/example/example_prims.v b/generic/viaduct/example/example_prims.v new file mode 100644 index 0000000000..d2813129b4 --- /dev/null +++ b/generic/viaduct/example/example_prims.v @@ -0,0 +1,35 @@ +module LUT4 #( + parameter [15:0] INIT = 0 +) ( + input [3:0] I, + output F +); + wire [7:0] s3 = I[3] ? INIT[15:8] : INIT[7:0]; + wire [3:0] s2 = I[2] ? s3[ 7:4] : s3[3:0]; + wire [1:0] s1 = I[1] ? s2[ 3:2] : s2[1:0]; + assign F = I[0] ? s1[1] : s1[0]; +endmodule + +module DFF ( + input CLK, D, + output reg Q +); + initial Q = 1'b0; + always @(posedge CLK) + Q <= D; +endmodule + +module INBUF ( + input PAD, + output O, +); + assign O = PAD; +endmodule + +module OUTBUF ( + output PAD, + input I, +); + assign PAD = I; +endmodule + diff --git a/generic/viaduct/example/synth_viaduct_example.tcl b/generic/viaduct/example/synth_viaduct_example.tcl new file mode 100644 index 0000000000..a9d18f5612 --- /dev/null +++ b/generic/viaduct/example/synth_viaduct_example.tcl @@ -0,0 +1,24 @@ +# Usage +# tcl synth_viaduct_example.tcl {out.json} + +yosys read_verilog -lib [file dirname [file normalize $argv0]]/example_prims.v +yosys hierarchy -check +yosys proc +yosys flatten +yosys tribuf -logic +yosys deminout +yosys synth -run coarse +yosys memory_map +yosys opt -full +yosys iopadmap -bits -inpad INBUF O:PAD -outpad OUTBUF I:PAD +yosys techmap -map +/techmap.v +yosys opt -fast +yosys dfflegalize -cell \$_DFF_P_ 0 +yosys abc -lut 4 -dress +yosys clean +yosys techmap -map [file dirname [file normalize $argv0]]/example_map.v +yosys clean +yosys hierarchy -check +yosys stat + +if {$argc > 0} { yosys write_json [lindex $argv 0] } diff --git a/generic/viaduct/example/viaduct_example.sh b/generic/viaduct/example/viaduct_example.sh new file mode 100755 index 0000000000..0842df200b --- /dev/null +++ b/generic/viaduct/example/viaduct_example.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex +# Run synthesis +yosys -p "tcl synth_viaduct_example.tcl blinky.json" ../../examples/blinky.v +# Run PnR +${NEXTPNR:-../../../build/nextpnr-generic} --uarch example --json blinky.json diff --git a/generic/viaduct_api.cc b/generic/viaduct_api.cc new file mode 100644 index 0000000000..8a7b631395 --- /dev/null +++ b/generic/viaduct_api.cc @@ -0,0 +1,118 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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 "viaduct_api.h" +#include "nextpnr.h" + +// Default implementations for Viaduct API hooks + +NEXTPNR_NAMESPACE_BEGIN + +void ViaductAPI::init(Context *ctx) { this->ctx = ctx; } + +std::vector ViaductAPI::getCellTypes() const +{ + pool cell_types; + for (auto bel : ctx->bels) { + cell_types.insert(bel.type); + } + + return std::vector{cell_types.begin(), cell_types.end()}; +} +BelBucketId ViaductAPI::getBelBucketForBel(BelId bel) const { return ctx->getBelType(bel); } +BelBucketId ViaductAPI::getBelBucketForCellType(IdString cell_type) const { return cell_type; } +bool ViaductAPI::isValidBelForCellType(IdString cell_type, BelId bel) const +{ + return ctx->getBelType(bel) == cell_type; +} + +delay_t ViaductAPI::estimateDelay(WireId src, WireId dst) const +{ + const WireInfo &s = ctx->wire_info(src); + const WireInfo &d = ctx->wire_info(dst); + int dx = abs(s.x - d.x); + int dy = abs(s.y - d.y); + return (dx + dy) * ctx->args.delayScale + ctx->args.delayOffset; +} +delay_t ViaductAPI::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const +{ + NPNR_UNUSED(src_pin); + NPNR_UNUSED(dst_pin); + auto driver_loc = ctx->getBelLocation(src_bel); + auto sink_loc = ctx->getBelLocation(dst_bel); + + int dx = abs(sink_loc.x - driver_loc.x); + int dy = abs(sink_loc.y - driver_loc.y); + return (dx + dy) * ctx->args.delayScale + ctx->args.delayOffset; +} +ArcBounds ViaductAPI::getRouteBoundingBox(WireId src, WireId dst) const +{ + ArcBounds bb; + int src_x = ctx->wire_info(src).x; + int src_y = ctx->wire_info(src).y; + int dst_x = ctx->wire_info(dst).x; + int dst_y = ctx->wire_info(dst).y; + + bb.x0 = src_x; + bb.y0 = src_y; + bb.x1 = src_x; + bb.y1 = src_y; + + auto extend = [&](int x, int y) { + bb.x0 = std::min(bb.x0, x); + bb.x1 = std::max(bb.x1, x); + bb.y0 = std::min(bb.y0, y); + bb.y1 = std::max(bb.y1, y); + }; + extend(dst_x, dst_y); + return bb; +} + +ViaductArch *ViaductArch::list_head; +ViaductArch::ViaductArch(const std::string &name) : name(name) +{ + list_next = ViaductArch::list_head; + ViaductArch::list_head = this; +} +std::string ViaductArch::list() +{ + std::string result; + ViaductArch *cursor = ViaductArch::list_head; + while (cursor) { + if (!result.empty()) + result += ", "; + result += cursor->name; + cursor = cursor->list_next; + } + return result; +} +std::unique_ptr ViaductArch::create(const std::string &name, const dict &args) +{ + ViaductArch *cursor = ViaductArch::list_head; + while (cursor) { + if (cursor->name != name) { + cursor = cursor->list_next; + continue; + } + return cursor->create(args); + } + return {}; +} + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct_api.h b/generic/viaduct_api.h new file mode 100644 index 0000000000..bc9b4311ce --- /dev/null +++ b/generic/viaduct_api.h @@ -0,0 +1,114 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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 VIADUCT_API_H +#define VIADUCT_API_H + +#include "nextpnr_namespaces.h" +#include "nextpnr_types.h" + +NEXTPNR_NAMESPACE_BEGIN + +/* +Viaduct -- a series of small arches + +Viaduct is a framework that provides an 'inbetween' step between nextpnr-generic +using Python bindings and a full-custom arch. + +It allows an arch to programmatically build a set of bels (placement locations) +and a routing graph in-memory at startup; and then hook into nextpnr's flow +and validity checking rules at runtime with custom C++ code. + +To create a Viaduct 'uarch', the following are required: + - an implementation of ViaductAPI. At a minimum; you will need to use ctx->addBel, ctx->addWire and ctx->addPip to +create the graph of placement and routing resources in-memory. Also implement any placement validity checking required - +like rules for how LUTs and FFs can be placed together in a SLICE. + - an instance of a struct deriving from ViaductArch - this is how the uarch is discovered. Override create(args) to +create an instance of your ViaductAPI implementation. + - these should be within C++ files in a new subfolder of 'viaduct'. Add the name of this subfolder to the list of +VIADUCT_UARCHES in family.cmake if building in-tree. + +For an example of how these pieces fit together; see 'viaduct/example' which implements a small synthetic architecture +using this framework. + +*/ + +struct Context; + +struct ViaductAPI +{ + virtual void init(Context *ctx); + Context *ctx; + + // --- Bel functions --- + // Called when a bel is placed/unplaced (with cell=nullptr for a unbind) + virtual void notifyBelChange(BelId bel, CellInfo *cell) {} + // This only needs to return false if a bel is disabled for a microarch-specific reason and not just because it's + // bound (which the base generic will deal with) + virtual bool checkBelAvail(BelId bel) const { return true; } + // Mirror the ArchAPI functions - see archapi.md + virtual std::vector getCellTypes() const; + virtual BelBucketId getBelBucketForBel(BelId bel) const; + virtual BelBucketId getBelBucketForCellType(IdString cell_type) const; + virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const; + virtual bool isBelLocationValid(BelId bel) const { return true; } + + // --- Wire and pip functions --- + // Called when a wire/pip is placed/unplaced (with net=nullptr for a unbind) + virtual void notifyWireChange(WireId wire, NetInfo *net) {} + virtual void notifyPipChange(PipId pip, NetInfo *net) {} + // These only need to return false if a wire/pip is disabled for a microarch-specific reason and not just because + // it's bound (which the base arch will deal with) + virtual bool checkWireAvail(WireId wire) const { return true; } + virtual bool checkPipAvail(PipId pip) const { return true; } + virtual bool checkPipAvailForNet(PipId pip, NetInfo *net) const { return checkPipAvail(pip); }; + + // --- Route lookahead --- + virtual delay_t estimateDelay(WireId src, WireId dst) const; + virtual delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const; + virtual ArcBounds getRouteBoundingBox(WireId src, WireId dst) const; + + // --- Flow hooks --- + virtual void pack(){}; // replaces the pack function + // Called before and after main placement and routing + virtual void prePlace(){}; + virtual void postPlace(){}; + virtual void preRoute(){}; + virtual void postRoute(){}; + + virtual ~ViaductAPI(){}; +}; + +struct ViaductArch +{ + static ViaductArch *list_head; + ViaductArch *list_next = nullptr; + + std::string name; + ViaductArch(const std::string &name); + ~ViaductArch(){}; + virtual std::unique_ptr create(const dict &args) = 0; + + static std::string list(); + static std::unique_ptr create(const std::string &name, const dict &args); +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct_constids.h b/generic/viaduct_constids.h new file mode 100644 index 0000000000..82180953c5 --- /dev/null +++ b/generic/viaduct_constids.h @@ -0,0 +1,74 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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 VIADUCT_CONSTIDS_H +#define VIADUCT_CONSTIDS_H + +/* +This enables use of 'constids' similar to a 'true' nextpnr arch in a viaduct uarch. +To use: + - create a 'constids.inc' file in your uarch folder containing one ID per line; inside X( ) + - set the VIADUCT_CONSTIDS macro to the path to this file relative to the generic arch base + - in your main file; also define GEN_INIT_CONSTIDS to create init_uarch_constids(Context*) which you should call in +init + - include this file +*/ + +#include "nextpnr_namespaces.h" + +#ifdef VIADUCT_MAIN +#include "idstring.h" +#endif + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +#ifndef Q_MOC_RUN +enum ConstIds +{ + ID_NONE +#define X(t) , ID_##t +#include VIADUCT_CONSTIDS +#undef X + , +}; + +#define X(t) static constexpr auto id_##t = IdString(ID_##t); +#include VIADUCT_CONSTIDS +#undef X +#endif + +#ifdef GEN_INIT_CONSTIDS + +void init_uarch_constids(Context *ctx) +{ +#define X(t) IdString::initialize_add(ctx, #t, ID_##t); + +#include VIADUCT_CONSTIDS + +#undef X +} + +#endif + +} // namespace + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct_helpers.cc b/generic/viaduct_helpers.cc new file mode 100644 index 0000000000..36bdd6bec1 --- /dev/null +++ b/generic/viaduct_helpers.cc @@ -0,0 +1,163 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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 "viaduct_helpers.h" +#include "design_utils.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +void ViaductHelpers::resize_ids(int x, int y) +{ + NPNR_ASSERT(x >= 0 && y >= 0 && x <= 20000 && y <= 20000); + while (int(x_ids.size()) <= x) { + IdString next = ctx->id(stringf("X%d", int(x_ids.size()))); + x_ids.push_back(next); + } + while (int(y_ids.size()) <= y) { + IdString next = ctx->id(stringf("Y%d", int(y_ids.size()))); + y_ids.push_back(next); + } +} + +IdStringList ViaductHelpers::xy_id(int x, int y, IdString base) +{ + resize_ids(x, y); + std::array result{x_ids.at(x), y_ids.at(y), base}; + return IdStringList(result); +} + +IdStringList ViaductHelpers::xy_id(int x, int y, IdStringList base) +{ + resize_ids(x, y); + std::array prefix{x_ids.at(x), y_ids.at(y)}; + return IdStringList::concat(IdStringList(prefix), base); +} + +void ViaductHelpers::remove_nextpnr_iobs(const pool &top_ports) +{ + std::vector to_remove; + for (auto &cell : ctx->cells) { + auto &ci = *cell.second; + if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf"))) + continue; + NetInfo *i = get_net_or_empty(&ci, ctx->id("I")); + if (i && i->driver.cell) { + if (!top_ports.count(CellTypePort(i->driver))) + log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), + ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); + } + NetInfo *o = get_net_or_empty(&ci, ctx->id("O")); + if (o) { + for (auto &usr : o->users) { + if (!top_ports.count(CellTypePort(usr))) + log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci), + ctx->nameOf(usr.cell), ctx->nameOf(usr.port)); + } + } + disconnect_port(ctx, &ci, ctx->id("I")); + disconnect_port(ctx, &ci, ctx->id("O")); + to_remove.push_back(ci.name); + } + for (IdString cell_name : to_remove) + ctx->cells.erase(cell_name); +} + +int ViaductHelpers::constrain_cell_pairs(const pool &src_ports, const pool &sink_ports, + int delta_z) +{ + int constrained = 0; + for (auto &cell : ctx->cells) { + auto &ci = *cell.second; + if (ci.cluster != ClusterId()) + continue; // don't constrain already-constrained cells + bool done = false; + for (auto &port : ci.ports) { + // look for starting source ports + if (port.second.type != PORT_OUT || !port.second.net) + continue; + if (!src_ports.count(CellTypePort(ci.type, port.first))) + continue; + for (auto &usr : port.second.net->users) { + if (!sink_ports.count(CellTypePort(usr))) + continue; + if (usr.cell->cluster != ClusterId()) + continue; + // Add the constraint + ci.cluster = ci.name; + ci.constr_abs_z = false; + ci.constr_children.push_back(usr.cell); + usr.cell->cluster = ci.name; + usr.cell->constr_x = 0; + usr.cell->constr_y = 0; + usr.cell->constr_z = delta_z; + usr.cell->constr_abs_z = false; + ++constrained; + done = true; + break; + } + if (done) + break; + } + } + return constrained; +} + +void ViaductHelpers::replace_constants(CellTypePort vcc_driver, CellTypePort gnd_driver, + const dict &vcc_params, + const dict &gnd_params) +{ + CellInfo *vcc_drv = ctx->createCell(ctx->id("$PACKER_VCC_DRV"), vcc_driver.cell_type); + vcc_drv->addOutput(vcc_driver.port); + for (auto &p : vcc_params) + vcc_drv->params[p.first] = p.second; + + CellInfo *gnd_drv = ctx->createCell(ctx->id("$PACKER_GND_DRV"), gnd_driver.cell_type); + gnd_drv->addOutput(gnd_driver.port); + for (auto &p : gnd_params) + gnd_drv->params[p.first] = p.second; + + NetInfo *vcc_net = ctx->createNet(ctx->id("$PACKER_VCC")); + NetInfo *gnd_net = ctx->createNet(ctx->id("$PACKER_GND")); + + std::vector trim_cells; + std::vector trim_nets; + for (auto &net : ctx->nets) { + auto &ni = *net.second; + if (!ni.driver.cell) + continue; + if (ni.driver.cell->type != ctx->id("GND") && ni.driver.cell->type != ctx->id("VCC")) + continue; + NetInfo *replace = (ni.driver.cell->type == ctx->id("VCC")) ? vcc_net : gnd_net; + for (auto &usr : ni.users) { + usr.cell->ports.at(usr.port).net = replace; + replace->users.push_back(usr); + } + trim_cells.push_back(ni.driver.cell->name); + trim_nets.push_back(ni.name); + } + for (IdString cell_name : trim_cells) + ctx->cells.erase(cell_name); + for (IdString net_name : trim_nets) + ctx->nets.erase(net_name); +} + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct_helpers.h b/generic/viaduct_helpers.h new file mode 100644 index 0000000000..8cba84115e --- /dev/null +++ b/generic/viaduct_helpers.h @@ -0,0 +1,82 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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 VIADUCT_HELPERS_H +#define VIADUCT_HELPERS_H + +#include "nextpnr_namespaces.h" +#include "nextpnr_types.h" + +NEXTPNR_NAMESPACE_BEGIN + +/* +Viaduct -- a series of small arches + +See viaduct_api.h for more background. + +viaduct_helpers provides some features for building up arches using the viaduct API +*/ + +// Used to configure various generic pack functions +struct CellTypePort +{ + CellTypePort() : cell_type(), port(){}; + CellTypePort(IdString cell_type, IdString port) : cell_type(cell_type), port(port){}; + explicit CellTypePort(const PortRef &net_port) + : cell_type(net_port.cell ? net_port.cell->type : IdString()), port(net_port.port){}; + inline bool operator==(const CellTypePort &other) const + { + return cell_type == other.cell_type && port == other.port; + } + inline bool operator!=(const CellTypePort &other) const + { + return cell_type != other.cell_type || port != other.port; + } + inline unsigned hash() const { return mkhash(cell_type.hash(), port.hash()); } + IdString cell_type, port; +}; + +struct ViaductHelpers +{ + ViaductHelpers(){}; + Context *ctx; + void init(Context *ctx) { this->ctx = ctx; } + // IdStringList components for x and y locations + std::vector x_ids, y_ids; + void resize_ids(int x, int y); + // Get an IdStringList for a hierarchical ID + // Because this uses an IdStringList with seperate X and Y components; this will be much more efficient than + // creating unique strings for each object in each X and Y position + IdStringList xy_id(int x, int y, IdString base); + IdStringList xy_id(int x, int y, IdStringList base); + // Common packing functions + // Remove nextpnr-inserted IO buffers; where IO buffer insertion is done in synthesis + // expects a set of top-level port types + void remove_nextpnr_iobs(const pool &top_ports); + // Constrain cells with certain port connection patterns together with a fixed z-offset + int constrain_cell_pairs(const pool &src_ports, const pool &sink_ports, int delta_z); + // Replace constants with given driving cells + void replace_constants(CellTypePort vcc_driver, CellTypePort gnd_driver, + const dict &vcc_params = {}, + const dict &gnd_params = {}); +}; + +NEXTPNR_NAMESPACE_END + +#endif From ae7c2261befd961a06a7422b8ae79b54c89e03bc Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 11 Jan 2022 15:28:13 +0100 Subject: [PATCH 041/712] Switched integer pair hashing function from DJB2 to Cantor Signed-off-by: Maciej Kurc --- common/hashlib.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/hashlib.h b/common/hashlib.h index b71f012984..70de8c919e 100644 --- a/common/hashlib.h +++ b/common/hashlib.h @@ -26,8 +26,11 @@ NEXTPNR_NAMESPACE_BEGIN const int hashtable_size_trigger = 2; const int hashtable_size_factor = 3; -// The XOR version of DJB2 -inline unsigned int mkhash(unsigned int a, unsigned int b) { return ((a << 5) + a) ^ b; } +// Cantor pairing function for two non-negative integers +// https://en.wikipedia.org/wiki/Pairing_function +inline unsigned int mkhash(unsigned int a, unsigned int b) { + return (a*a + 3*a + 2*a*b + b + b*b) / 2; +} // traditionally 5381 is used as starting value for the djb2 hash const unsigned int mkhash_init = 5381; From b5fc788153bd011a4476946af5ccebde05fd2ad2 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Tue, 18 Jan 2022 15:12:45 +0100 Subject: [PATCH 042/712] Sync with the current state of mistral --- .github/workflows/mistral_ci.yml | 2 +- mistral/arch.h | 1 - mistral/base_bitstream.cc | 100 ------------------------------ mistral/bitstream.cc | 103 ++----------------------------- 4 files changed, 5 insertions(+), 201 deletions(-) delete mode 100644 mistral/base_bitstream.cc diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index 64300d78f4..0da633e68d 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: e039b595529ab573d9cb01c64ef927f9d81d63ce + MISTRAL_REVISION: db29e403bede4d514fcf83948db9d4c05df07e96 run: | source ./.github/ci/build_mistral.sh get_dependencies diff --git a/mistral/arch.h b/mistral/arch.h index 471b5251ee..9c38d5d2c3 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -562,7 +562,6 @@ struct Arch : BaseArch // ------------------------------------------------- - void init_base_bitstream(); // base_bitstream.cc void build_bitstream(); // bitstream.cc }; diff --git a/mistral/base_bitstream.cc b/mistral/base_bitstream.cc deleted file mode 100644 index 9fa74fb92b..0000000000 --- a/mistral/base_bitstream.cc +++ /dev/null @@ -1,100 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 gatecat - * - * 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" - -NEXTPNR_NAMESPACE_BEGIN - -namespace { -// Device-specific default config for the sx120f die -void default_sx120f(CycloneV *cv) -{ - // Default PMA config? - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 11), CycloneV::FFPLL_IQCLK_DIRECTION, 0, CycloneV::TRISTATE); - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 11), CycloneV::FFPLL_IQCLK_DIRECTION, 1, CycloneV::TRISTATE); - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 23), CycloneV::FFPLL_IQCLK_DIRECTION, 0, CycloneV::DOWN); - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 23), CycloneV::FFPLL_IQCLK_DIRECTION, 1, CycloneV::UP); - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::FFPLL_IQCLK_DIRECTION, 0, CycloneV::UP); - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::FFPLL_IQCLK_DIRECTION, 1, CycloneV::UP); - cv->bmux_b_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::FPLL_DRV_EN, 0, 0); - cv->bmux_m_set(CycloneV::PMA3, CycloneV::xy2pos(0, 35), CycloneV::HCLK_TOP_OUT_DRIVER, 0, CycloneV::TRISTATE); - // Default PLL config - cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN0, 0, 1); - cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN0_PRECOMP, 0, 1); - cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN1, 0, 1); - cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_ATB_EN1_PRECOMP, 0, 1); - cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_BG_KICKSTART, 0, 1); - cv->bmux_b_set(CycloneV::FPLL, CycloneV::xy2pos(0, 73), CycloneV::PL_AUX_VBGMON_POWERDOWN, 0, 1); - // Default TERM config - cv->bmux_b_set(CycloneV::TERM, CycloneV::xy2pos(89, 34), CycloneV::INTOSC_2_EN, 0, 0); - - // TODO: what if these pins are used? where do these come from - for (int z = 0; z < 4; z++) { - cv->bmux_m_set(CycloneV::GPIO, CycloneV::xy2pos(89, 43), CycloneV::IOCSR_STD, z, CycloneV::NVR_LOW); - cv->bmux_m_set(CycloneV::GPIO, CycloneV::xy2pos(89, 66), CycloneV::IOCSR_STD, z, CycloneV::NVR_LOW); - } - for (int y : {38, 44, 51, 58, 65, 73, 79}) { - // TODO: Why only these upper DQS? is there a pattern? - cv->bmux_b_set(CycloneV::DQS16, CycloneV::xy2pos(89, y), CycloneV::RB_2X_CLK_DQS_INV, 0, 1); - cv->bmux_b_set(CycloneV::DQS16, CycloneV::xy2pos(89, y), CycloneV::RB_ACLR_LFIFO_EN, 0, 1); - cv->bmux_b_set(CycloneV::DQS16, CycloneV::xy2pos(89, y), CycloneV::RB_LFIFO_BYPASS, 0, 0); - } - - // Discover these mux values using - // grep 'i [_A-Z0-9.]* 1' empty.bt - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 12), 69), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 13), 4), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 34), 69), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 35), 4), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 37), 31), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 40), 43), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 46), 69), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 47), 43), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 53), 69), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 54), 4), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(0, 73), 68), true); - - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 18), 66), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 20), 8), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 27), 69), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 28), 43), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 59), 66), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 61), 8), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 68), 69), true); - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(9, 69), 43), true); - - for (int z = 10; z <= 45; z++) - cv->inv_set(CycloneV::rnode(CycloneV::GOUT, CycloneV::xy2pos(51, 80), z), true); -} -} // namespace - -void Arch::init_base_bitstream() -{ - switch (cyclonev->current_model()->variant.die.type) { - case CycloneV::SX120F: - default_sx120f(cyclonev); - break; - default: - log_error("FIXME: die type %s currently unsupported for bitgen.\n", - cyclonev->current_model()->variant.die.name); - } -} - -NEXTPNR_NAMESPACE_END diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index eed508b38b..b432be037d 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -29,108 +29,13 @@ struct MistralBitgen Context *ctx; CycloneV *cv; - void init() + void options() { - ctx->init_base_bitstream(); - // Default options - cv->opt_b_set(CycloneV::ALLOW_DEVICE_WIDE_OUTPUT_ENABLE_DIS, true); - if (!ctx->setting("compress_rbf", false)) + if (!ctx->setting("compress_rbf", false)) { cv->opt_b_set(CycloneV::COMPRESSION_DIS, true); - cv->opt_n_set(CycloneV::CRC_DIVIDE_ORDER, 8); - cv->opt_b_set(CycloneV::CVP_CONF_DONE_EN, true); - cv->opt_b_set(CycloneV::DEVICE_WIDE_RESET_EN, true); - cv->opt_n_set(CycloneV::DRIVE_STRENGTH, 8); - cv->opt_b_set(CycloneV::IOCSR_READY_FROM_CSR_DONE_EN, true); - cv->opt_b_set(CycloneV::NCEO_DIS, true); - cv->opt_b_set(CycloneV::OCT_DONE_DIS, true); - cv->opt_r_set(CycloneV::OPT_A, 0x1dff); - if (!ctx->setting("compress_rbf", false)) cv->opt_r_set(CycloneV::OPT_B, 0xffffff40adffffffULL); - else + } else cv->opt_r_set(CycloneV::OPT_B, 0xffffff402dffffffULL); - cv->opt_b_set(CycloneV::RELEASE_CLEARS_BEFORE_TRISTATES_DIS, true); - cv->opt_b_set(CycloneV::RETRY_CONFIG_ON_ERROR_EN, true); - cv->opt_r_set(CycloneV::START_UP_CLOCK, 0x3F); - // Default inversion - write_default_inv(); - } - - void write_default_inv() - { - // Some PNODEs are inverted by default. Set them up here. - for (const auto &pn2r : cv->get_all_p2r()) { - const auto &pn = pn2r.first; - auto pt = CycloneV::pn2pt(pn); - auto pi = CycloneV::pn2pi(pn); - - switch (CycloneV::pn2bt(pn)) { - case CycloneV::HMC: { - // HMC OE are inverted to set OE=0, i.e. unused pins floating - // TODO: handle the case when we are using the HMC or HMC bypass - std::string name(CycloneV::port_type_names[pt]); - if (name.compare(0, 5, "IOINT") != 0 || name.compare(name.size() - 2, 2, "OE") != 0) - continue; - cv->inv_set(pn2r.second, true); - break; - }; - // HPS IO - TODO: what about when we actually support the HPS primitives? - case CycloneV::HPS_BOOT: { - switch (pt) { - case CycloneV::CSEL_EN: - case CycloneV::BSEL_EN: - case CycloneV::BOOT_FROM_FPGA_READY: - case CycloneV::BOOT_FROM_FPGA_ON_FAILURE: - cv->inv_set(pn2r.second, true); - break; - case CycloneV::CSEL: - if (pi < 2) - cv->inv_set(pn2r.second, true); - break; - case CycloneV::BSEL: - if (pi < 3) - cv->inv_set(pn2r.second, true); - break; - default: - break; - }; - break; - }; - case CycloneV::HPS_CROSS_TRIGGER: { - if (pt == CycloneV::CLK_EN) - cv->inv_set(pn2r.second, true); - break; - }; - case CycloneV::HPS_TEST: { - if (pt == CycloneV::CFG_DFX_BYPASS_ENABLE) - cv->inv_set(pn2r.second, true); - break; - }; - case CycloneV::GPIO: { - // Ignore GPIO used by the design - BelId bel = ctx->bel_by_block_idx(CycloneV::pn2x(pn), CycloneV::pn2y(pn), id_MISTRAL_IO, - CycloneV::pn2bi(pn)); - if (bel != BelId() && ctx->getBoundBelCell(bel) != nullptr) - continue; - // Bonded IO invert OEIN.1 which disables the output buffer and floats the IO - // Unbonded IO invert OEIN.0 which enables the output buffer, and {DATAIN.[0123]} to drive a constant - // GND, presumably for power/EMI reasons - bool is_bonded = cv->pin_find_pnode(pn) != nullptr; - if (is_bonded && (pt != CycloneV::OEIN || pi != 1)) - continue; - if (!is_bonded && (pt != CycloneV::DATAIN) && (pt != CycloneV::OEIN || pi != 0)) - continue; - cv->inv_set(pn2r.second, true); - break; - }; - case CycloneV::FPLL: { - if (pt == CycloneV::EXTSWITCH0 || (pt == CycloneV::CLKEN && pi < 2)) - cv->inv_set(pn2r.second, true); - break; - }; - default: - break; - } - } } void write_dqs() @@ -392,7 +297,7 @@ struct MistralBitgen void run() { cv->clear(); - init(); + options(); write_routing(); write_dqs(); write_cells(); From 91a0eb93672e9c764f03fd1b0a7d22595a61c516 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Tue, 18 Jan 2022 22:37:35 +0100 Subject: [PATCH 043/712] Mistral: fix gpio OE, add hmc bypass support --- .github/workflows/mistral_ci.yml | 2 +- mistral/arch.h | 34 +++++++++++++++++- mistral/bitstream.cc | 62 +++++++++++++++++++------------- mistral/io.cc | 10 +++--- 4 files changed, 78 insertions(+), 30 deletions(-) diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index 0da633e68d..a02026cfff 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: db29e403bede4d514fcf83948db9d4c05df07e96 + MISTRAL_REVISION: 0c2ab2b2c6af33fea1c20349be2e0068366ed615 run: | source ./.github/ci/build_mistral.sh get_dependencies diff --git a/mistral/arch.h b/mistral/arch.h index 9c38d5d2c3..98340d4e9a 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -461,9 +461,41 @@ struct Arch : BaseArch void add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire); + CycloneV::rnode_t find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi = -1, int pi = -1) const + { + auto pn1 = CycloneV::pnode(bt, x, y, port, bi, pi); + auto rn1 = cyclonev->pnode_to_rnode(pn1); + if(rn1) + return rn1; + + if(bt == CycloneV::GPIO) { + auto pn2 = cyclonev->p2p_to(pn1); + if(!pn2) { + auto pnv = cyclonev->p2p_from(pn1); + if(!pnv.empty()) + pn2 = pnv[0]; + } + auto pn3 = cyclonev->hmc_get_bypass(pn2); + auto rn2 = cyclonev->pnode_to_rnode(pn3); + return rn2; + } + + return 0; + } + WireId get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const { - return WireId(cyclonev->pnode_to_rnode(CycloneV::pnode(bt, x, y, port, bi, pi))); + auto rn = find_rnode(bt, x, y, port, bi, pi); + if(rn) + return WireId(rn); + + fprintf(stderr, "Trying to connect unknown node %s\n", CycloneV::pn2s(CycloneV::pnode(bt, x, y, port, bi, pi)).c_str()); + exit(1); + } + + bool has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const + { + return find_rnode(bt, x, y, port, bi, pi) != 0; } void create_lab(int x, int y, bool is_mlab); // lab.cc diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index b432be037d..3307972781 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -29,6 +29,34 @@ struct MistralBitgen Context *ctx; CycloneV *cv; + using rnode_t = CycloneV::rnode_t; + using pnode_t = CycloneV::pnode_t; + using pos_t = CycloneV::pos_t; + using block_type_t = CycloneV::block_type_t; + using port_type_t = CycloneV::port_type_t; + + rnode_t find_rnode(block_type_t bt, pos_t pos, port_type_t port, int bi = -1, int pi = -1) const + { + auto pn1 = CycloneV::pnode(bt, pos, port, bi, pi); + auto rn1 = cv->pnode_to_rnode(pn1); + if(rn1) + return rn1; + + if(bt == CycloneV::GPIO) { + auto pn2 = cv->p2p_to(pn1); + if(!pn2) { + auto pnv = cv->p2p_from(pn1); + if(!pnv.empty()) + pn2 = pnv[0]; + } + auto pn3 = cv->hmc_get_bypass(pn2); + auto rn2 = cv->pnode_to_rnode(pn3); + return rn2; + } + + return 0; + } + void options() { if (!ctx->setting("compress_rbf", false)) { @@ -38,27 +66,6 @@ struct MistralBitgen cv->opt_r_set(CycloneV::OPT_B, 0xffffff402dffffffULL); } - void write_dqs() - { - for (auto pos : cv->dqs16_get_pos()) { - int x = CycloneV::pos2x(pos), y = CycloneV::pos2y(pos); - // DQS bypass for used output pins - for (int z = 0; z < 16; z++) { - int ioy = y + (z / 4) - 2; - if (ioy < 0 || ioy >= int(cv->get_tile_sy())) - continue; - BelId bel = ctx->bel_by_block_idx(x, ioy, id_MISTRAL_IO, z % 4); - if (bel == BelId()) - continue; - CellInfo *ci = ctx->getBoundBelCell(bel); - if (ci == nullptr || (ci->type != id_MISTRAL_IO && ci->type != id_MISTRAL_OB)) - continue; // not an output - cv->bmux_m_set(CycloneV::DQS16, pos, CycloneV::INPUT_REG4_SEL, z, CycloneV::SEL_LOCKED_DPA); - cv->bmux_r_set(CycloneV::DQS16, pos, CycloneV::RB_T9_SEL_EREG_CFF_DELAY, z, 0x1f); - } - } - } - void write_routing() { for (auto &net : ctx->nets) { @@ -87,12 +94,20 @@ struct MistralBitgen if (is_output) { cv->bmux_m_set(CycloneV::GPIO, pos, CycloneV::DRIVE_STRENGTH, bi, CycloneV::V3P3_LVTTL_16MA_LVCMOS_2MA); cv->bmux_m_set(CycloneV::GPIO, pos, CycloneV::IOCSR_STD, bi, CycloneV::DIS); + + // Output gpios must also bypass things in the associated dqs + auto dqs = cv->p2p_to(CycloneV::pnode(CycloneV::GPIO, pos, CycloneV::PNONE, bi, -1)); + printf("%s -> %s\n", CycloneV::pn2s(CycloneV::pnode(CycloneV::GPIO, pos, CycloneV::PNONE, bi, -1)).c_str(), CycloneV::pn2s(dqs).c_str()); + if(dqs) { + cv->bmux_m_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::INPUT_REG4_SEL, CycloneV::pn2bi(dqs), CycloneV::SEL_LOCKED_DPA); + cv->bmux_r_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::RB_T9_SEL_EREG_CFF_DELAY, CycloneV::pn2bi(dqs), 0x1f); + } } // There seem to be two mirrored OEIN inversion bits for constant OE for inputs/outputs. This might be to // prevent a single bitflip from turning inputs to outputs and messing up other devices on the boards, notably // ECP5 does similar. OEIN.0 inverted for outputs; OEIN.1 for inputs - cv->inv_set(cv->pnode_to_rnode(CycloneV::pnode(CycloneV::GPIO, pos, CycloneV::OEIN, bi, is_output ? 0 : 1)), - true); + cv->inv_set(find_rnode(CycloneV::GPIO, pos, CycloneV::OEIN, bi, 0), is_output); + cv->inv_set(find_rnode(CycloneV::GPIO, pos, CycloneV::OEIN, bi, 1), !is_output); } void write_clkbuf_cell(CellInfo *ci, int x, int y, int bi) @@ -299,7 +314,6 @@ struct MistralBitgen cv->clear(); options(); write_routing(); - write_dqs(); write_cells(); write_labs(); } diff --git a/mistral/io.cc b/mistral/io.cc index dab3672e28..a0a01af38a 100644 --- a/mistral/io.cc +++ b/mistral/io.cc @@ -30,10 +30,12 @@ void Arch::create_gpio(int x, int y) WireId pad = add_wire(x, y, id(stringf("PAD[%d]", z))); BelId bel = add_bel(x, y, id(stringf("IO[%d]", z)), id_MISTRAL_IO); add_bel_pin(bel, id_PAD, PORT_INOUT, pad); - // FIXME: is the port index of zero always correct? - add_bel_pin(bel, id_I, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)); - add_bel_pin(bel, id_OE, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::OEIN, 0)); - add_bel_pin(bel, id_O, PORT_OUT, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAIN, 0)); + if(has_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)) { + // FIXME: is the port index of zero always correct? + add_bel_pin(bel, id_I, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)); + add_bel_pin(bel, id_OE, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::OEIN, 0)); + add_bel_pin(bel, id_O, PORT_OUT, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAIN, 0)); + } bel_data(bel).block_index = z; } } From 27d38de612ce4109723704f863ac82a6cbddf10c Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Wed, 19 Jan 2022 08:42:29 +0100 Subject: [PATCH 044/712] Mistral: Use log_error, remove leftover debugging printf. --- mistral/arch.cc | 36 ++++++++++++++++++++++++++++++++++++ mistral/arch.h | 39 +++------------------------------------ mistral/bitstream.cc | 1 - 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/mistral/arch.cc b/mistral/arch.cc index 034ecb980a..f61d07abe0 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -43,6 +43,42 @@ void IdString::initialize_arch(const BaseCtx *ctx) #undef X } +CycloneV::rnode_t Arch::find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi, int pi) const +{ + auto pn1 = CycloneV::pnode(bt, x, y, port, bi, pi); + auto rn1 = cyclonev->pnode_to_rnode(pn1); + if(rn1) + return rn1; + + if(bt == CycloneV::GPIO) { + auto pn2 = cyclonev->p2p_to(pn1); + if(!pn2) { + auto pnv = cyclonev->p2p_from(pn1); + if(!pnv.empty()) + pn2 = pnv[0]; + } + auto pn3 = cyclonev->hmc_get_bypass(pn2); + auto rn2 = cyclonev->pnode_to_rnode(pn3); + return rn2; + } + + return 0; +} + +WireId Arch::get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi) const +{ + auto rn = find_rnode(bt, x, y, port, bi, pi); + if(rn) + return WireId(rn); + + log_error("Trying to connect unknown node %s\n", CycloneV::pn2s(CycloneV::pnode(bt, x, y, port, bi, pi)).c_str()); +} + +bool Arch::has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi) const +{ + return find_rnode(bt, x, y, port, bi, pi) != 0; +} + Arch::Arch(ArchArgs args) { this->args = args; diff --git a/mistral/arch.h b/mistral/arch.h index 98340d4e9a..e931df2d08 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -461,42 +461,9 @@ struct Arch : BaseArch void add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire); - CycloneV::rnode_t find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi = -1, int pi = -1) const - { - auto pn1 = CycloneV::pnode(bt, x, y, port, bi, pi); - auto rn1 = cyclonev->pnode_to_rnode(pn1); - if(rn1) - return rn1; - - if(bt == CycloneV::GPIO) { - auto pn2 = cyclonev->p2p_to(pn1); - if(!pn2) { - auto pnv = cyclonev->p2p_from(pn1); - if(!pnv.empty()) - pn2 = pnv[0]; - } - auto pn3 = cyclonev->hmc_get_bypass(pn2); - auto rn2 = cyclonev->pnode_to_rnode(pn3); - return rn2; - } - - return 0; - } - - WireId get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const - { - auto rn = find_rnode(bt, x, y, port, bi, pi); - if(rn) - return WireId(rn); - - fprintf(stderr, "Trying to connect unknown node %s\n", CycloneV::pn2s(CycloneV::pnode(bt, x, y, port, bi, pi)).c_str()); - exit(1); - } - - bool has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const - { - return find_rnode(bt, x, y, port, bi, pi) != 0; - } + CycloneV::rnode_t find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi = -1, int pi = -1) const; + WireId get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const; + bool has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const; void create_lab(int x, int y, bool is_mlab); // lab.cc void create_gpio(int x, int y); // io.cc diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 3307972781..340f4b9642 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -97,7 +97,6 @@ struct MistralBitgen // Output gpios must also bypass things in the associated dqs auto dqs = cv->p2p_to(CycloneV::pnode(CycloneV::GPIO, pos, CycloneV::PNONE, bi, -1)); - printf("%s -> %s\n", CycloneV::pn2s(CycloneV::pnode(CycloneV::GPIO, pos, CycloneV::PNONE, bi, -1)).c_str(), CycloneV::pn2s(dqs).c_str()); if(dqs) { cv->bmux_m_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::INPUT_REG4_SEL, CycloneV::pn2bi(dqs), CycloneV::SEL_LOCKED_DPA); cv->bmux_r_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::RB_T9_SEL_EREG_CFF_DELAY, CycloneV::pn2bi(dqs), 0x1f); From 18f71ace8c0cd08478ec7ab1e05cc7036ba18c6a Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 25 Jan 2022 12:08:52 +0100 Subject: [PATCH 045/712] Removed the need for MULT36_CORE bel for implementing the MULTADDSUB9X9WIDE macro Signed-off-by: Maciej Kurc --- nexus/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index c3d9cba286..e89dc9c00d 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1784,7 +1784,7 @@ struct NexusPacker copy_port(ctx, ci, mt.has_addsub ? id_SIGNED : id_SIGNEDA, mult9[i], id_ASIGNED); } - bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36); + bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36) && !(mt.wide > 0); // Configure mult18x36s for (int i = 0; i < mt.N18x36; i++) { mult18x36[i]->params[id_MULT36] = mult36_used ? std::string("ENABLED") : std::string("DISABLED"); From e51e82d6a93d2d46ab11601769e736dd5029d70d Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 28 Jan 2022 13:51:10 +0100 Subject: [PATCH 046/712] Added honoring OSCA output frequency tolerance during constraints generation Signed-off-by: Maciej Kurc --- nexus/pack.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index e89dc9c00d..41f9d80664 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1987,8 +1987,9 @@ struct NexusPacker copy_constraint(ci, id_CLKI, id_CLKO, 1); } else if (ci->type == id_OSC_CORE) { int div = int_or_default(ci->params, ctx->id("HF_CLK_DIV"), 128); - set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1))); - set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10))); + const float tol = 1.15f; // OSCA has +/-15% frequency tolerance, assume the worst case. + set_period(ci, id_HFCLKOUT, delay_t(tol * (1.0e6 / 450) * (div + 1))); + set_period(ci, id_LFCLKOUT, delay_t(tol * (1.0e3 / 10))); } else if (ci->type == id_PLL_CORE) { static const std::array div{id_DIVA, id_DIVB, id_DIVC, id_DIVD, id_DIVE, id_DIVF}; static const std::array output{id_CLKOP, id_CLKOS, id_CLKOS2, From 22e4081c73ab7eec3c9f0841fcb1f247a85b5265 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 29 Jan 2022 14:45:17 +1000 Subject: [PATCH 047/712] gowin: Add GUI. * Items such as LUT, DFF, MUX, ALU, IOB are displayed; * Local wires, 1-2-4-8 wires are displayed; * The clock spines, taps and branches are displayed with some caveats. For now, you can not create a project in the GUI because of possible conflict with another PR (about GW1NR-9C support), but you can specify the board in the command line and load .JSON and .CST in the GUI. Although ALUs are displayed, but the CIN and COUT wires are not. This is still an unsolved problem. Signed-off-by: YRabbit --- common/nextpnr_base_types.h | 2 + common/svg.cc | 2 + gowin/arch.cc | 232 +- gowin/arch.h | 29 +- gowin/constids.inc | 342 +++ gowin/cst.cc | 40 + gowin/cst.h | 34 + gowin/gfx.cc | 932 ++++++ gowin/gfx.h | 4935 ++++++++++++++++++++++++++++++ gowin/main.cc | 1 + gui/designwidget.cc | 6 + gui/fpgaviewwidget.cc | 83 +- gui/gowin/mainwindow.cc | 60 +- gui/gowin/mainwindow.h | 12 + gui/gowin/nextpnr.qrc | 3 + gui/gowin/resources/open_cst.png | Bin 0 -> 9399 bytes 16 files changed, 6656 insertions(+), 57 deletions(-) create mode 100644 gowin/cst.cc create mode 100644 gowin/cst.h create mode 100644 gowin/gfx.cc create mode 100644 gowin/gfx.h create mode 100644 gui/gowin/resources/open_cst.png diff --git a/common/nextpnr_base_types.h b/common/nextpnr_base_types.h index 2f114bf8f9..944bf0b8b5 100644 --- a/common/nextpnr_base_types.h +++ b/common/nextpnr_base_types.h @@ -46,6 +46,8 @@ struct GraphicElement TYPE_BOX, TYPE_CIRCLE, TYPE_LABEL, + TYPE_LOCAL_ARROW, // Located entirely within the cell boundaries, coordinates in the range [0., 1.] + TYPE_LOCAL_LINE, TYPE_MAX } type = TYPE_NONE; diff --git a/common/svg.cc b/common/svg.cc index d20508431b..c5e2ea36c9 100644 --- a/common/svg.cc +++ b/common/svg.cc @@ -57,6 +57,8 @@ struct SVGWriter switch (el.type) { case GraphicElement::TYPE_LINE: case GraphicElement::TYPE_ARROW: + case GraphicElement::TYPE_LOCAL_LINE: + case GraphicElement::TYPE_LOCAL_ARROW: out << stringf("", (el.x1 + dxy.x) * scale, (el.y1 + dxy.y) * scale, (el.x2 + dxy.x) * scale, (el.y2 + dxy.y) * scale, get_stroke_colour(el.style)) diff --git a/gowin/arch.cc b/gowin/arch.cc index 8c654b2eb9..c55d4a7147 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -23,6 +23,7 @@ #include #include #include "embed.h" +#include "gfx.h" #include "nextpnr.h" #include "placer1.h" #include "placer_heap.h" @@ -32,6 +33,148 @@ NEXTPNR_NAMESPACE_BEGIN +// GUI +void Arch::fixClockSpineDecals(void) +{ + for (auto sp : clockSpinesCache) { + // row: (#of spine cells, wire_id) + dict> rows; + IdString min_x, max_x; + min_x = max_x = *sp.second.begin(); + for (auto wire : sp.second) { + WireInfo &wi = wire_info(wire); + std::pair &row = rows[wi.y]; + ++row.first; + row.second = wire; + if (wi.x < wire_info(min_x).x) { + min_x = wire; + } else { + if (wi.x > wire_info(max_x).x) { + max_x = wire; + } + } + } + // central mux row owns the global decal + int mux_row = -1; + for (auto row : rows) { + if (row.second.first == 1) { + mux_row = row.first; + break; + } + } + // if there is no separate central mux than all decals are the same + if (mux_row == -1) { + mux_row = rows.begin()->first; + WireInfo &wi = wire_info(rows.at(mux_row).second); + GraphicElement &el_active = decal_graphics.at(wi.decalxy_active.decal).at(0); + GraphicElement &el_inactive = decal_graphics.at(wi.decalxy_inactive.decal).at(0); + el_active.y1 -= wi.y; + el_active.y2 -= wi.y; + el_inactive.y1 -= wi.y; + el_inactive.y2 -= wi.y; + el_active.x1 += wire_info(min_x).x; + el_active.x2 += wire_info(max_x).x; + el_inactive.x1 += wire_info(min_x).x; + el_inactive.x2 += wire_info(max_x).x; + } else { + // change the global decal + WireInfo &wi = wire_info(rows.at(mux_row).second); + // clear spine decals + float y = 0.; + for (auto wire : sp.second) { + if (wire == wi.name) { + continue; + } + wire_info(wire).decalxy_active = DecalXY(); + wire_info(wire).decalxy_inactive = DecalXY(); + y = wire_info(wire).y; + } + GraphicElement &el_active = decal_graphics.at(wi.decalxy_active.decal).at(0); + GraphicElement &el_inactive = decal_graphics.at(wi.decalxy_inactive.decal).at(0); + el_active.y1 -= y; + el_active.y2 -= y; + el_inactive.y1 -= y; + el_inactive.y2 -= y; + el_active.x1 += wire_info(min_x).x; + el_active.x2 += wire_info(max_x).x; + el_inactive.x1 += wire_info(min_x).x; + el_inactive.x2 += wire_info(max_x).x; + } + refreshUi(); + } +} + +void Arch::updateClockSpinesCache(IdString spine_id, IdString wire_id) +{ + std::vector &sp = clockSpinesCache[spine_id]; + if (std::find(sp.begin(), sp.end(), wire_id) == sp.end()) { + sp.push_back(wire_id); + } +} + +DecalXY Arch::getBelDecal(BelId bel) const +{ + CellInfo *ci = getBoundBelCell(bel); + if (ci == nullptr) { + return bels.at(bel).decalxy_inactive; + } else { + // LUT + used/unused DFF + if (bels.at(bel).type == id_SLICE) { + DecalXY decalxy = bels.at(bel).decalxy_active; + if (!ci->params.at(id_FF_USED).as_bool()) { + decalxy.decal = id_DECAL_LUT_UNUSED_DFF_ACTIVE; + if (ci->params.count(id_ALU_MODE) != 0) { + decalxy.decal = id_DECAL_ALU_ACTIVE; + } + } + return decalxy; + } + } + return bels.at(bel).decalxy_active; +} + +DecalXY Arch::getGroupDecal(GroupId grp) const { return groups.at(grp).decalxy; } + +DecalXY Arch::getPipDecal(PipId pip) const +{ + if (getBoundPipNet(pip) == nullptr) { + return pips.at(pip).decalxy_inactive; + } + return pips.at(pip).decalxy_active; +} + +DecalXY Arch::getWireDecal(WireId wire) const +{ + static std::vector clk_wires = {id_GB00, id_GB10, id_GB20, id_GB30, id_GB40, id_GB50, id_GB60, id_GB70}; + static std::vector pip_dst = {id_CLK0, id_CLK1, id_CLK2, id_EW10, id_EW20, id_SN10, id_SN20}; + if (getBoundWireNet(wire) == nullptr) { + if (std::find(clk_wires.begin(), clk_wires.end(), wires.at(wire).type) != clk_wires.end()) { + for (auto dst : pip_dst) { + // check if pip is used + char pip_name[20]; + snprintf(pip_name, sizeof(pip_name), "%s_%s", wire.c_str(this), dst.c_str(this)); + if (pips.count(id(pip_name)) != 0) { + if (getBoundPipNet(id(pip_name)) != nullptr) { + return wires.at(wire).decalxy_active; + } + } + } + } else { + // spines + if (clockSpinesCache.count(wires.at(wire).type) != 0) { + std::vector const &sp = clockSpinesCache.at(wires.at(wire).type); + for (auto w : sp) { + if (getBoundWireNet(w) != nullptr) { + return wires.at(wire).decalxy_active; + } + } + } + } + return wires.at(wire).decalxy_inactive; + } + return wires.at(wire).decalxy_active; +} + WireInfo &Arch::wire_info(IdString wire) { auto w = wires.find(wire); @@ -58,7 +201,6 @@ BelInfo &Arch::bel_info(IdString bel) void Arch::addWire(IdString name, IdString type, int x, int y) { - // std::cout << name.str(this) << std::endl; NPNR_ASSERT(wires.count(name) == 0); WireInfo &wi = wires[name]; wi.name = name; @@ -105,6 +247,13 @@ void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWi tilePipDimZ[loc.x][loc.y] = std::max(tilePipDimZ[loc.x][loc.y], loc.z + 1); } +void Arch::addGroup(IdString name) +{ + NPNR_ASSERT(groups.count(name) == 0); + GroupInfo &gi = groups[name]; + gi.name = name; +} + void Arch::addBel(IdString name, IdString type, Loc loc, bool gb) { NPNR_ASSERT(bels.count(name) == 0); @@ -189,24 +338,41 @@ void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic) refreshUi(); } -void Arch::setWireDecal(WireId wire, DecalXY decalxy) +void Arch::setWireDecal(WireId wire, DecalXY active, DecalXY inactive) { - wire_info(wire).decalxy = decalxy; + wire_info(wire).decalxy_active = active; + wire_info(wire).decalxy_inactive = inactive; refreshUiWire(wire); } -void Arch::setPipDecal(PipId pip, DecalXY decalxy) +void Arch::setPipDecal(PipId pip, DecalXY active, DecalXY inactive) { - pip_info(pip).decalxy = decalxy; + pip_info(pip).decalxy_active = active; + pip_info(pip).decalxy_inactive = inactive; refreshUiPip(pip); } -void Arch::setBelDecal(BelId bel, DecalXY decalxy) +void Arch::setBelDecal(BelId bel, DecalXY active, DecalXY inactive) { - bel_info(bel).decalxy = decalxy; + bel_info(bel).decalxy_active = active; + bel_info(bel).decalxy_inactive = inactive; refreshUiBel(bel); } +void Arch::setDefaultDecals(void) +{ + for (BelId bel : getBels()) { + gfxSetBelDefaultDecal(this, bel_info(bel)); + } + for (PipId pip : getPips()) { + gfxSetPipDefaultDecal(this, pip_info(pip)); + } + for (WireId wire : getWires()) { + gfxSetWireDefaultDecal(this, wire_info(wire)); + } + fixClockSpineDecals(); +} + void Arch::setGroupDecal(GroupId group, DecalXY decalxy) { groups[group].decalxy = decalxy; @@ -519,6 +685,7 @@ void Arch::read_cst(std::istream &in) insloc } cst_type; + settings.erase(id("cst")); while (!in.eof()) { std::getline(in, line); cst_type = ioloc; @@ -584,6 +751,7 @@ void Arch::read_cst(std::istream &in) } } } + settings[id("cst")] = 1; } // Add all MUXes for the cell @@ -689,6 +857,14 @@ Arch::Arch(ArchArgs args) : args(args) IdString::initialize_add(this, db->id_strs[i].get(), uint32_t(i) + db->num_constids); } + // Empty decal + addDecalGraphic(IdString(), GraphicElement()); + + if (args.gui) { + // decals + gfxCreateBelDecals(this); + } + // setup package IdString package_name; IdString device_id; @@ -762,9 +938,17 @@ Arch::Arch(ArchArgs args) : args(args) // The reverse order of the enumeration simplifies the creation // of MUX2_LUT8s: they need the existence of the wire on the right. for (int i = db->rows * db->cols - 1; i >= 0; --i) { + IdString grpname; int row = i / db->cols; int col = i % db->cols; const TilePOD *tile = db->grid[i].get(); + if (args.gui) { + // CRU decal + snprintf(buf, 32, "R%dC%d_CRU", row + 1, col + 1); + grpname = id(buf); + addGroup(grpname); + setGroupDecal(grpname, gfxGetCruGroupDecalXY(col, row)); + } // setup wires const PairPOD *pips[2] = {tile->pips.get(), tile->clock_pips.get()}; unsigned int num_pips[2] = {tile->num_pips, tile->num_clock_pips}; @@ -837,6 +1021,14 @@ Arch::Arch(ArchArgs args) : args(args) if (z == 0) { addMuxBels(db, row, col); } + if (z % 2 == 0) { + snprintf(buf, 32, "R%dC%d_LUT_GRP%d", row + 1, col + 1, z); + grpname = id(buf); + if (args.gui) { + addGroup(grpname); + setGroupDecal(grpname, gfxGetLutGroupDecalXY(col, row, z >> 1)); + } + } break; case ID_IOBJ: z++; /* fall-through*/ @@ -933,7 +1125,6 @@ Arch::Arch(ArchArgs args) : args(args) DelayQuad delay = getWireTypeDelay(destid); // local alias auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, srcid.index); - // std::cout << "srcid " << srcid.str(this) << std::endl; if (local_alias != nullptr) { srcid = IdString(local_alias->src_id); gsrcname = wireToGlobal(srcrow, srccol, db, srcid); @@ -950,12 +1141,14 @@ Arch::Arch(ArchArgs args) : args(args) srcrow = alias_src->src_row; srcid = IdString(alias_src->src_id); gsrcname = wireToGlobal(srcrow, srccol, db, srcid); - // std::cout << buf << std::endl; } addPip(pipname, destid, gsrcname, gdestname, delay, Loc(col, row, j)); } } } + if (args.gui) { + setDefaultDecals(); + } // Permissible combinations of modes in a single slice dff_comp_mode[id_DFF] = id_DFF; @@ -1015,6 +1208,17 @@ BelId Arch::getBelByLocation(Loc loc) const const std::vector &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); } +bool Arch::haveBelType(int x, int y, IdString bel_type) +{ + for (auto bel : getBelsByTile(x, y)) { + BelInfo bi = bel_info(bel); + if (bi.type == bel_type) { + return true; + } + } + return false; +} + bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; } void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) @@ -1303,6 +1507,16 @@ bool Arch::route() return result; } +// --------------------------------------------------------------- +std::vector Arch::getDecalGraphics(DecalId decal) const +{ + if (!decal_graphics.count(decal)) { + // XXX + return std::vector(); + } + return decal_graphics.at(decal); +} + // --------------------------------------------------------------- bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const diff --git a/gowin/arch.h b/gowin/arch.h index 9f969f54f0..9ce3bd1c29 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -176,6 +176,7 @@ struct ArchArgs // y = mx + c relationship between distance and delay for interconnect // delay estimates double delayScale = 0.4, delayOffset = 0.4; + bool gui; }; struct WireInfo; @@ -187,7 +188,7 @@ struct PipInfo NetInfo *bound_net; WireId srcWire, dstWire; DelayQuad delay; - DecalXY decalxy; + DecalXY decalxy_active, decalxy_inactive; Loc loc; }; @@ -200,7 +201,7 @@ struct WireInfo BelPin uphill_bel_pin; std::vector downhill_bel_pins; std::vector bel_pins; - DecalXY decalxy; + DecalXY decalxy_active, decalxy_inactive; int x, y; }; @@ -217,7 +218,7 @@ struct BelInfo std::map attrs; CellInfo *bound_cell; dict pins; - DecalXY decalxy; + DecalXY decalxy_active, decalxy_inactive; int x, y, z; bool gb; }; @@ -270,8 +271,6 @@ struct ArchRanges : BaseArchRanges using GroupWiresRangeT = const std::vector &; using GroupPipsRangeT = const std::vector &; using GroupGroupsRangeT = const std::vector &; - // Decals - using DecalGfxRangeT = const std::vector &; }; struct Arch : BaseArch @@ -312,16 +311,23 @@ struct Arch : BaseArch void addBelOutput(IdString bel, IdString name, IdString wire); void addBelInout(IdString bel, IdString name, IdString wire); + void addGroup(IdString name); void addGroupBel(IdString group, IdString bel); void addGroupWire(IdString group, IdString wire); void addGroupPip(IdString group, IdString pip); void addGroupGroup(IdString group, IdString grp); void addDecalGraphic(DecalId decal, const GraphicElement &graphic); - void setWireDecal(WireId wire, DecalXY decalxy); - void setPipDecal(PipId pip, DecalXY decalxy); - void setBelDecal(BelId bel, DecalXY decalxy); + void setWireDecal(WireId wire, DecalXY active, DecalXY inactive); + void setPipDecal(PipId pip, DecalXY active, DecalXY inactive); + void setBelDecal(BelId bel, DecalXY active, DecalXY inactive); + void setDefaultDecals(void); void setGroupDecal(GroupId group, DecalXY decalxy); + std::vector getDecalGraphics(DecalId decal) const override; + DecalXY getBelDecal(BelId bel) const override; + DecalXY getGroupDecal(GroupId grp) const override; + DecalXY getPipDecal(PipId pip) const override; + DecalXY getWireDecal(WireId pip) const override; void setWireAttr(IdString wire, IdString key, const std::string &value); void setPipAttr(IdString pip, IdString key, const std::string &value); @@ -452,6 +458,7 @@ struct Arch : BaseArch // Internal usage void assignArchInfo() override; bool cellsCompatible(const CellInfo **cells, int count) const; + bool haveBelType(int x, int y, IdString bel_type); // start Z for the MUX2LUT5 bels int const mux_0_z = 10; // chip db version @@ -459,6 +466,12 @@ struct Arch : BaseArch std::vector cell_types; + // clock spines cache + // spine_id : [wire_id, wire_id, ...] + dict> clockSpinesCache; + void updateClockSpinesCache(IdString spine_id, IdString wire_id); + void fixClockSpineDecals(void); + // Permissible combinations of modes in a single slice std::map dff_comp_mode; }; diff --git a/gowin/constids.inc b/gowin/constids.inc index 6a730d5d5e..82d0bb11df 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -313,6 +313,312 @@ X(COUT2) X(COUT3) X(COUT4) X(COUT5) + +// wires +// SN +X(S10) +X(S10_loop0) +X(S13) +X(S13_loop0) +X(N10) +X(N10_loop0) +X(N13) +X(N13_loop0) +X(SN10_loop_n) +X(SN10_loop_s) +X(SN20_loop_n) +X(SN20_loop_s) +X(S20) +X(S20_loop0) +X(S20_loop1) +X(S21) +X(S21_loop0) +X(S21_loop1) +X(S22) +X(S22_loop0) +X(S22_loop1) +X(S23) +X(S23_loop0) +X(S23_loop1) +X(S24) +X(S24_loop0) +X(S24_loop1) +X(S25) +X(S25_loop0) +X(S25_loop1) +X(S26) +X(S26_loop0) +X(S26_loop1) +X(S27) +X(S27_loop0) +X(S27_loop1) +X(N20) +X(N20_loop0) +X(N20_loop1) +X(N21) +X(N21_loop0) +X(N21_loop1) +X(N22) +X(N22_loop0) +X(N22_loop1) +X(N23) +X(N23_loop0) +X(N23_loop1) +X(N24) +X(N24_loop0) +X(N24_loop1) +X(N25) +X(N25_loop0) +X(N25_loop1) +X(N26) +X(N26_loop0) +X(N26_loop1) +X(N27) +X(N27_loop0) +X(N27_loop1) +X(S80) +X(S80_loop0) +X(S80_loop1) +X(S80_loop2) +X(S80_loop3) +X(S80_loop4) +X(S80_loop5) +X(S80_loop6) +X(S80_loop7) +X(N80) +X(N80_loop0) +X(N80_loop1) +X(N80_loop2) +X(N80_loop3) +X(N80_loop4) +X(N80_loop5) +X(N80_loop6) +X(N80_loop7) +X(S81) +X(S81_loop0) +X(S81_loop1) +X(S81_loop2) +X(S81_loop3) +X(S81_loop4) +X(S81_loop5) +X(S81_loop6) +X(S81_loop7) +X(N81) +X(N81_loop0) +X(N81_loop1) +X(N81_loop2) +X(N81_loop3) +X(N81_loop4) +X(N81_loop5) +X(N81_loop6) +X(N81_loop7) +X(S82) +X(S82_loop0) +X(S82_loop1) +X(S82_loop2) +X(S82_loop3) +X(S82_loop4) +X(S82_loop5) +X(S82_loop6) +X(S82_loop7) +X(N82) +X(N82_loop0) +X(N82_loop1) +X(N82_loop2) +X(N82_loop3) +X(N82_loop4) +X(N82_loop5) +X(N82_loop6) +X(N82_loop7) +X(S83) +X(S83_loop0) +X(S83_loop1) +X(S83_loop2) +X(S83_loop3) +X(S83_loop4) +X(S83_loop5) +X(S83_loop6) +X(S83_loop7) +X(N83) +X(N83_loop0) +X(N83_loop1) +X(N83_loop2) +X(N83_loop3) +X(N83_loop4) +X(N83_loop5) +X(N83_loop6) +X(N83_loop7) + +// WE +X(E10) +X(E10_loop0) +X(E13) +X(E13_loop0) +X(W10) +X(W10_loop0) +X(W13) +X(W13_loop0) +X(EW10_loop_w) +X(EW10_loop_e) +X(EW20_loop_w) +X(EW20_loop_e) +// +X(E20) +X(E20_loop0) +X(E20_loop1) +X(E21) +X(E21_loop0) +X(E21_loop1) +X(E22) +X(E22_loop0) +X(E22_loop1) +X(E23) +X(E23_loop0) +X(E23_loop1) +X(E24) +X(E24_loop0) +X(E24_loop1) +X(E25) +X(E25_loop0) +X(E25_loop1) +X(E26) +X(E26_loop0) +X(E26_loop1) +X(E27) +X(E27_loop0) +X(E27_loop1) +X(W20) +X(W20_loop0) +X(W20_loop1) +X(W21) +X(W21_loop0) +X(W21_loop1) +X(W22) +X(W22_loop0) +X(W22_loop1) +X(W23) +X(W23_loop0) +X(W23_loop1) +X(W24) +X(W24_loop0) +X(W24_loop1) +X(W25) +X(W25_loop0) +X(W25_loop1) +X(W26) +X(W26_loop0) +X(W26_loop1) +X(W27) +X(W27_loop0) +X(W27_loop1) +// +X(E80) +X(E80_loop0) +X(E80_loop1) +X(E80_loop2) +X(E80_loop3) +X(E80_loop4) +X(E80_loop5) +X(E80_loop6) +X(E80_loop7) +X(W80) +X(W80_loop0) +X(W80_loop1) +X(W80_loop2) +X(W80_loop3) +X(W80_loop4) +X(W80_loop5) +X(W80_loop6) +X(W80_loop7) +X(E81) +X(E81_loop0) +X(E81_loop1) +X(E81_loop2) +X(E81_loop3) +X(E81_loop4) +X(E81_loop5) +X(E81_loop6) +X(E81_loop7) +X(W81) +X(W81_loop0) +X(W81_loop1) +X(W81_loop2) +X(W81_loop3) +X(W81_loop4) +X(W81_loop5) +X(W81_loop6) +X(W81_loop7) +X(E82) +X(E82_loop0) +X(E82_loop1) +X(E82_loop2) +X(E82_loop3) +X(E82_loop4) +X(E82_loop5) +X(E82_loop6) +X(E82_loop7) +X(W82) +X(W82_loop0) +X(W82_loop1) +X(W82_loop2) +X(W82_loop3) +X(W82_loop4) +X(W82_loop5) +X(W82_loop6) +X(W82_loop7) +X(E83) +X(E83_loop0) +X(E83_loop1) +X(E83_loop2) +X(E83_loop3) +X(E83_loop4) +X(E83_loop5) +X(E83_loop6) +X(E83_loop7) +X(W83) +X(W83_loop0) +X(W83_loop1) +X(W83_loop2) +X(W83_loop3) +X(W83_loop4) +X(W83_loop5) +X(W83_loop6) +X(W83_loop7) + +// spines +X(SPINE0) +X(SPINE1) +X(SPINE2) +X(SPINE3) +X(SPINE4) +X(SPINE5) +X(SPINE6) +X(SPINE7) +X(SPINE8) +X(SPINE9) +X(SPINE10) +X(SPINE11) +X(SPINE12) +X(SPINE13) +X(SPINE14) +X(SPINE15) +X(SPINE16) +X(SPINE17) +X(SPINE18) +X(SPINE19) +X(SPINE20) +X(SPINE21) +X(SPINE22) +X(SPINE23) +X(SPINE24) +X(SPINE25) +X(SPINE26) +X(SPINE27) +X(SPINE28) +X(SPINE29) +X(SPINE30) +X(SPINE31) + // slice items X(SLICE) X(CLK) @@ -380,6 +686,22 @@ X(GW_MUX2_LUT5) X(GW_MUX2_LUT6) X(GW_MUX2_LUT7) X(GW_MUX2_LUT8) +X(I0MUX0) +X(I1MUX0) +X(I0MUX1) +X(I1MUX1) +X(I0MUX2) +X(I1MUX2) +X(I0MUX3) +X(I1MUX3) +X(I0MUX4) +X(I1MUX4) +X(I0MUX5) +X(I1MUX5) +X(I0MUX6) +X(I1MUX6) +X(I0MUX7) +X(I1MUX7) // ALU X(ALU) @@ -464,3 +786,23 @@ X(d_f) X(fx_ofx1) X(I01) +// GUI +X(DECAL_LUT_ACTIVE) +X(DECAL_LUT_INACTIVE) +X(DECAL_LUTDFF_ACTIVE) +X(DECAL_LUTDFF_INACTIVE) +X(DECAL_LUT_UNUSED_DFF_ACTIVE) +X(DECAL_GRP_LUT) +X(DECAL_CRU) +X(DECAL_MUXUPPER_INACTIVE) +X(DECAL_MUXUPPER_ACTIVE) +X(DECAL_MUXLOWER_INACTIVE) +X(DECAL_MUXLOWER_ACTIVE) +X(DECAL_IOB_INACTIVE) +X(DECAL_IOB_ACTIVE) +X(DECAL_IOBS_INACTIVE) +X(DECAL_IOBS_ACTIVE) +X(DECAL_ALU_ACTIVE) + + + diff --git a/gowin/cst.cc b/gowin/cst.cc new file mode 100644 index 0000000000..7ed74c6c7f --- /dev/null +++ b/gowin/cst.cc @@ -0,0 +1,40 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Claire Xenia Wolf + * Copyright (C) 2018 gatecat + * + * 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 "cst.h" +#include +#include +#include "arch.h" +#include "log.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool read_cst(Context *ctx, std::istream &in) +{ + try { + ctx->read_cst(in); + return true; + } catch (log_execution_error_exception) { + return false; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/gowin/cst.h b/gowin/cst.h new file mode 100644 index 0000000000..3fcfd66385 --- /dev/null +++ b/gowin/cst.h @@ -0,0 +1,34 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Claire Xenia Wolf + * Copyright (C) 2018 gatecat + * + * 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 CST_H +#define CST_H + +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Read the constraints file +bool read_cst(Context *ctx, std::istream &in); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/gowin/gfx.cc b/gowin/gfx.cc new file mode 100644 index 0000000000..ad2c7eadef --- /dev/null +++ b/gowin/gfx.cc @@ -0,0 +1,932 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Claire Xenia Wolf + * + * 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 +#include + +#include "gfx.h" + +NEXTPNR_NAMESPACE_BEGIN + +const int PIP_SRC_DST_LEN = 20; + +static void get_pip_xy(CruSide side, float &off, float &x, float &y) +{ + switch (side) { + case Top: + x = off; + y = cru_y + cru_h; + break; + case Bottom: + x = off; + y = cru_y; + break; + case Left: + x = cru_x; + y = off; + break; + case Right: + x = cru_x + cru_w; + y = off; + break; + case Center: + x = cru_x + cru_w / 2.f; + y = off; + break; + } +} + +void gfxSetPipDefaultDecal(Arch *arch, PipInfo &pip) +{ + DecalXY active, inactive; + std::vector split_res; + IdString src_loc_id, dst_loc_id; + char buf[PIP_SRC_DST_LEN]; + + active.x = inactive.x = pip.loc.x; + active.y = inactive.y = arch->gridDimY - 1. - pip.loc.y; + boost::split(split_res, pip.name.str(arch), [](char c) { return c == '_'; }); + src_loc_id = arch->id(split_res.at(1)); + dst_loc_id = arch->id(split_res.at(2)); + snprintf(buf, PIP_SRC_DST_LEN, "%s_%s_active", src_loc_id.c_str(arch), dst_loc_id.c_str(arch)); + IdString active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, PIP_SRC_DST_LEN, "%s_%s_inactive", src_loc_id.c_str(arch), dst_loc_id.c_str(arch)); + IdString inactive_id = arch->id(buf); + inactive.decal = inactive_id; + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + // clock? + if (dst_loc_id == id_GT00 || dst_loc_id == id_GT10) { + WireInfo &wi = arch->wire_info(pip.srcWire); + if (wi.type.str(arch).substr(0, 3) != "UNK") { + // create pip + GraphicElement el; + el.type = GraphicElement::TYPE_LOCAL_LINE; + el.style = GraphicElement::STYLE_ACTIVE; + if (dst_loc_id == id_GT00) { + el.x1 = WIRE_X(CLK_GT00_X); + } else { + el.x1 = WIRE_X(CLK_GT10_X); + } + el.x2 = el.x1 + spine_pip_off; + el.y2 = spineY.at(arch->wire_info(pip.srcWire).type); + el.y1 = el.y2 - spine_pip_off; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + } else { + // XXX + if (pipPoint.count(src_loc_id) == 0 || pipPoint.count(dst_loc_id) == 0) { + // std::cout << "*R" << pip.loc.y + 1 << "C" << pip.loc.x + 1 << " no " << pip.name.str(arch) << " " << + // buf << std::endl; + } else { + GraphicElement el; + el.type = GraphicElement::TYPE_LOCAL_ARROW; + el.style = GraphicElement::STYLE_ACTIVE; + CruSide srcSide = pipPoint.at(src_loc_id).first; + float srcOff = pipPoint.at(src_loc_id).second; + CruSide dstSide = pipPoint.at(dst_loc_id).first; + float dstOff = pipPoint.at(dst_loc_id).second; + if (srcSide != dstSide) { + get_pip_xy(srcSide, srcOff, el.x1, el.y1); + get_pip_xy(dstSide, dstOff, el.x2, el.y2); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + } else { + get_pip_xy(srcSide, srcOff, el.x1, el.y1); + float dst_x = 0, dst_y = 0, m_x = 0, m_y = 0; + get_pip_xy(dstSide, dstOff, dst_x, dst_y); + switch (dstSide) { + case Top: + m_x = el.x1 + (dst_x - el.x1) / 2.f; + m_y = dst_y - std::max(cru_h * 0.1f, std::min(cru_h * 0.4f, std::abs(el.x1 - dst_x))); + break; + case Bottom: + m_x = el.x1 + (dst_x - el.x1) / 2.f; + m_y = dst_y + std::max(cru_h * 0.1f, std::min(cru_h * 0.4f, std::abs(el.x1 - dst_x))); + break; + case Right: + m_x = dst_x - std::max(cru_w * 0.1f, std::min(cru_w * 0.4f, std::abs(el.y1 - dst_y))); + m_y = el.y1 + (dst_y - el.y1) / 2.f; + break; + case Left: + m_x = dst_x + std::max(cru_w * 0.1f, std::min(cru_w * 0.4f, std::abs(el.y1 - dst_y))); + m_y = el.y1 + (dst_y - el.y1) / 2.f; + break; + default: // unreachable + break; + } + el.x2 = m_x; + el.y2 = m_y; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = m_x; + el.y1 = m_y; + el.x2 = dst_x; + el.y2 = dst_y; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + } + } + } + } + arch->setPipDecal(pip.name, active, inactive); +} + +const int WIRE_ID_LEN = 30; + +void gfxSetWireDefaultDecal(Arch *arch, WireInfo &wire) +{ + DecalXY active, inactive; + IdString active_id, inactive_id; + GraphicElement el; + std::vector split_res; + char buf[WIRE_ID_LEN]; + + if (std::find(decalless_wires.begin(), decalless_wires.end(), wire.name) != decalless_wires.end()) { + arch->setWireDecal(wire.type, DecalXY(), DecalXY()); + return; + } + // local to cell + if (arch->haveBelType(wire.x, wire.y, id_SLICE) && sliceLocalWires.count(wire.type) != 0) { + snprintf(buf, sizeof(buf), "%s_active", wire.type.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "%s_inactive", wire.type.c_str(arch)); + inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = wire.x; + active.y = inactive.y = arch->gridDimY - 1. - wire.y; + + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LOCAL_LINE; + for (auto seg : sliceLocalWires.at(wire.type)) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = std::get<0>(seg); + el.y1 = std::get<1>(seg); + el.x2 = std::get<2>(seg); + el.y2 = std::get<3>(seg); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + } + arch->setWireDecal(wire.name, active, inactive); + return; + } + // spines + if (spineY.count(wire.type) != 0) { + snprintf(buf, sizeof(buf), "%s_active", wire.type.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "%s_inactive", wire.type.c_str(arch)); + inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = 0.; + active.y = inactive.y = 0.; + + // update clock spines cache + arch->updateClockSpinesCache(wire.type, wire.name); + + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = 0.2; // cell's x will be added later in fixClockSpineDecals + el.x2 = 0.7; // cell's x will be added later in fixClockSpineDecals + el.y1 = spineY.at(wire.type) + arch->gridDimY - 1.; // cell's y will be added later in fixClockSpineDecals + el.y2 = el.y1; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + } + arch->setWireDecal(wire.name, active, inactive); + return; + } + + // global simple wires like IMUX + if (globalSimpleWires.count(wire.type) != 0) { + snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); + inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = 0; + active.y = inactive.y = 0; + + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LINE; + for (auto seg : globalSimpleWires.at(wire.type)) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = std::get<0>(seg) + wire.x; + el.y1 = std::get<1>(seg) + arch->gridDimY - 1. - wire.y; + el.x2 = std::get<2>(seg) + wire.x; + el.y2 = std::get<3>(seg) + arch->gridDimY - 1. - wire.y; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + } + arch->setWireDecal(wire.name, active, inactive); + return; + } + + // global + boost::split(split_res, wire.name.str(arch), [](char c) { return c == '_'; }); + if (split_res.size() >= 2) { + IdString wire_id = arch->id(split_res.at(1)); + // wrap + if ((wire.y == (arch->gridDimY - 1) && split_res.at(1).at(0) == 'S') || + (wire.y == 0 && split_res.at(1).at(0) == 'N')) { + wire_id = arch->id(split_res.at(1) + "_loop0"); + } + if ((wire.x == (arch->gridDimX - 1) && split_res.at(1).at(0) == 'E') || + (wire.x == 0 && split_res.at(1).at(0) == 'W')) { + wire_id = arch->id(split_res.at(1) + "_loop0"); + } + // SN wires + if (split_res.at(1).substr(0, 2) == "SN") { + if (wire.y == 0) { + wire_id = arch->id(split_res.at(1) + "_loop_n"); + } else { + if (wire.y == (arch->gridDimY - 1)) { + wire_id = arch->id(split_res.at(1) + "_loop_s"); + } + } + } else { + // wrap 2 hop + if ((wire.y == (arch->gridDimY - 2) && split_res.at(1).substr(0, 2) == "S2") || + (wire.y == 1 && split_res.at(1).substr(0, 2) == "N2")) { + wire_id = arch->id(split_res.at(1) + "_loop1"); + } + // wrap 4 hop + if (split_res.at(1).substr(0, 2) == "S8" || split_res.at(1).substr(0, 2) == "N8") { + char loop_buf[5 + 2]; + if (split_res.at(1).substr(0, 2) == "N8") { + if (wire.y < 8) { + snprintf(loop_buf, sizeof(loop_buf), "_loop%1u", wire.y); + wire_id = arch->id(split_res.at(1) + loop_buf); + } + } else { + if (arch->gridDimY - 1 - wire.y < 8) { + snprintf(loop_buf, sizeof(loop_buf), "_loop%1u", arch->gridDimY - 1 - wire.y); + wire_id = arch->id(split_res.at(1) + loop_buf); + } + } + } + } + // EW wires + if (split_res.at(1).substr(0, 2) == "EW") { + if (wire.x == 0) { + wire_id = arch->id(split_res.at(1) + "_loop_w"); + } else { + if (wire.x == (arch->gridDimX - 1)) { + wire_id = arch->id(split_res.at(1) + "_loop_e"); + } + } + } else { + // wrap 2 hop + if ((wire.x == (arch->gridDimX - 2) && split_res.at(1).substr(0, 2) == "E2") || + (wire.x == 1 && split_res.at(1).substr(0, 2) == "W2")) { + wire_id = arch->id(split_res.at(1) + "_loop1"); + } + // wrap 4 hop + if (split_res.at(1).substr(0, 2) == "E8" || split_res.at(1).substr(0, 2) == "W8") { + char loop_buf[5 + 2]; + if (split_res.at(1).substr(0, 2) == "W8") { + if (wire.x < 8) { + snprintf(loop_buf, sizeof(loop_buf), "_loop%1u", wire.x); + wire_id = arch->id(split_res.at(1) + loop_buf); + } + } else { + if (arch->gridDimX - 1 - wire.x < 8) { + snprintf(loop_buf, sizeof(loop_buf), "_loop%1u", arch->gridDimX - 1 - wire.x); + wire_id = arch->id(split_res.at(1) + loop_buf); + } + } + } + } + // really create decal + if (globalWires.count(wire_id) != 0) { + snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); + inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = 0; + active.y = inactive.y = 0; + + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LINE; + for (auto seg : globalWires.at(wire_id)) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = std::get<0>(seg) + wire.x; + el.y1 = std::get<1>(seg) + arch->gridDimY - 1. - wire.y; + el.x2 = std::get<2>(seg) + wire.x; + el.y2 = std::get<3>(seg) + arch->gridDimY - 1. - wire.y; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + } + arch->setWireDecal(wire.name, active, inactive); + return; + } + // clock branches + // # of rows is unknown so generate wire ids at runtime + if (split_res.at(1).substr(0, 3) == "GBO") { + snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + inactive_id = IdString(); + inactive.decal = inactive_id; + active.x = inactive.x = 0; + active.y = inactive.y = 0; + + float pip_x = PIP_X(id_GBO0); + float line_y = WIRE_Y(CLK_GBO0_Y) + arch->gridDimY - 1. - wire.y; + float line_0 = WIRE_Y(0) + arch->gridDimY - 1. - wire.y; + if (split_res.at(1).at(3) == '1') { + pip_x = PIP_X(id_GBO1); + line_y = WIRE_Y(CLK_GBO1_Y) + arch->gridDimY - 1. - wire.y; + } + + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = wire.x + pip_x; + el.y1 = line_y; + el.x2 = el.x1; + el.y2 = line_0; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = pip_x; + el.y1 = line_y; + el.x2 = pip_x + arch->gridDimX - 1.; + el.y2 = el.y1; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + } + arch->setWireDecal(wire.name, active, inactive); + return; + } else { + if (split_res.at(1).substr(0, 2) == "GT") { + snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + // snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); + // inactive_id = arch->id(buf); + inactive_id = IdString(); + inactive.decal = inactive_id; + active.x = inactive.x = 0; + active.y = inactive.y = 0; + + float pip_y = PIP_Y(id_GT00); + float line_x = WIRE_X(CLK_GT00_X) + wire.x; + float line_0 = WIRE_X(0) + wire.x; + if (split_res.at(1).at(2) == '1') { + pip_y = PIP_Y(id_GT10); + line_x = WIRE_X(CLK_GT10_X) + wire.x; + } + + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = line_x; + el.y1 = pip_y + arch->gridDimY - 1.; + el.x2 = el.x1; + el.y2 = pip_y; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + + for (int i = 0; i <= arch->gridDimY - 1; ++i) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = line_x; + el.y1 = pip_y + arch->gridDimY - 1. - i; + el.x2 = line_0; + el.y2 = el.y1; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_HIDDEN; + arch->addDecalGraphic(inactive_id, el); + } + } + arch->setWireDecal(wire.name, active, inactive); + return; + } else { + if (split_res.at(1).substr(0, 2) == "GB") { + snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); + active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); + inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = 0; + active.y = inactive.y = 0; + + float line_y = WIRE_Y(CLK_GBO0_Y) + arch->gridDimY - 1. - wire.y; + float line_0 = WIRE_Y(0) + arch->gridDimY - 1. - wire.y; + float pip_x = PIP_X(arch->id(split_res.at(1))); + if (split_res.at(1).at(2) >= '4') { + line_y = WIRE_Y(CLK_GBO1_Y) + arch->gridDimY - 1. - wire.y; + } + + // create if absent + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = wire.x + pip_x; + el.y1 = line_y; + el.x2 = el.x1; + el.y2 = line_0; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + arch->setWireDecal(wire.name, active, inactive); + return; + } + } + } + } + // std::cout << wire.name.str(arch) << ":" << wire.type.str(arch) << " R" << wire.y + 1 << "C" << wire.x + 1 << + // std::endl; +} + +void gfxCreateBelDecals(Arch *arch) +{ + GraphicElement el; + // LUTs + el.type = GraphicElement::TYPE_BOX; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = lut_x; + el.x2 = el.x1 + lut_w; + el.y1 = 0.; + el.y2 = el.y1 + lut_h; + arch->addDecalGraphic(id_DECAL_LUT_ACTIVE, el); + arch->addDecalGraphic(id_DECAL_LUTDFF_ACTIVE, el); + arch->addDecalGraphic(id_DECAL_LUT_UNUSED_DFF_ACTIVE, el); + arch->addDecalGraphic(id_DECAL_ALU_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(id_DECAL_LUT_INACTIVE, el); + arch->addDecalGraphic(id_DECAL_LUTDFF_INACTIVE, el); + el.x1 = dff_x; + el.x2 = el.x1 + dff_w; + el.y1 = 0.; + el.y2 = el.y1 + lut_h; + arch->addDecalGraphic(id_DECAL_LUTDFF_INACTIVE, el); + arch->addDecalGraphic(id_DECAL_LUT_UNUSED_DFF_ACTIVE, el); + arch->addDecalGraphic(id_DECAL_ALU_ACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_LUTDFF_ACTIVE, el); + el.type = GraphicElement::TYPE_LOCAL_LINE; + el.x1 = lut_x + 0.33f * lut_w; + el.x2 = el.x1 + 0.33f * lut_w; + el.y1 = 0.66f * lut_h; + el.y2 = el.y1; + arch->addDecalGraphic(id_DECAL_ALU_ACTIVE, el); + el.y1 = 0.3f * lut_h; + el.y2 = el.y1; + arch->addDecalGraphic(id_DECAL_ALU_ACTIVE, el); + el.x1 = lut_x + 0.5f * lut_w; + el.x2 = el.x1; + el.y1 = 0.5f * lut_h; + el.y2 = el.y1 + 0.33f * lut_h; + arch->addDecalGraphic(id_DECAL_ALU_ACTIVE, el); + + // LUT group + el.type = GraphicElement::TYPE_BOX; + el.style = GraphicElement::STYLE_FRAME; + el.x1 = grp_lut_x; + el.x2 = el.x1 + grp_lut_w; + el.y1 = 0.; + el.y2 = el.y1 + grp_lut_h; + arch->addDecalGraphic(id_DECAL_GRP_LUT, el); + + // CRU group + el.type = GraphicElement::TYPE_BOX; + el.style = GraphicElement::STYLE_FRAME; + el.x1 = cru_x; + el.x2 = el.x1 + cru_w; + el.y1 = cru_y; + el.y2 = el.y1 + cru_h; + arch->addDecalGraphic(id_DECAL_CRU, el); + + // Mux with upper 1 input + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.; + el.x2 = mux_w; + el.y1 = 0.; + el.y2 = mux_f; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = el.x2; + el.y1 = el.y2; + el.y2 = mux_h - mux_f; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x2 = 0.; + el.y1 = el.y2; + el.y2 = mux_h; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = el.x2; + el.y1 = mux_h; + el.y2 = 0.; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + // 1 + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.0038; + el.x2 = 0.0118; + el.y1 = el.y2 = 0.0598; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = (el.x1 + el.x2) / 2.; + el.x2 = el.x1; + el.y2 = 0.0808; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x2 = 0.0038; + el.y1 = el.y2; + el.y2 = 0.0797; + arch->addDecalGraphic(id_DECAL_MUXUPPER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXUPPER_ACTIVE, el); + + // Mux with lower 1 input + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.; + el.x2 = mux_w; + el.y1 = 0.; + el.y2 = mux_f; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = el.x2; + el.y1 = el.y2; + el.y2 = mux_h - mux_f; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x2 = 0.; + el.y1 = el.y2; + el.y2 = mux_h; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = el.x2; + el.y1 = mux_h; + el.y2 = 0.; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + // 1 + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.0038; + el.x2 = 0.0118; + el.y1 = el.y2 = 0.0140; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = (el.x1 + el.x2) / 2.; + el.x2 = el.x1; + el.y2 = 0.0352; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x2 = 0.0038; + el.y1 = el.y2; + el.y2 = 0.0341; + arch->addDecalGraphic(id_DECAL_MUXLOWER_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_MUXLOWER_ACTIVE, el); + + // IOB + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.; + el.x2 = io_w; + el.y1 = 0.; + el.y2 = el.y1; + arch->addDecalGraphic(id_DECAL_IOB_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOB_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = el.x2; + el.y2 = io_h; + arch->addDecalGraphic(id_DECAL_IOB_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOB_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.; + el.y1 = el.y2; + arch->addDecalGraphic(id_DECAL_IOB_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOB_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x2 = el.x1; + el.y2 = 0.; + arch->addDecalGraphic(id_DECAL_IOB_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOB_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = io_w; + el.x2 = io_w * 1.3f; + el.y2 = el.y1 = io_h / 2.f; + arch->addDecalGraphic(id_DECAL_IOB_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOB_ACTIVE, el); + + // IOBS + el.type = GraphicElement::TYPE_LINE; + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.; + el.x2 = ios_w; + el.y1 = 0.; + el.y2 = el.y1; + arch->addDecalGraphic(id_DECAL_IOBS_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOBS_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = el.x2; + el.y2 = ios_h; + arch->addDecalGraphic(id_DECAL_IOBS_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOBS_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = 0.; + el.y1 = el.y2; + arch->addDecalGraphic(id_DECAL_IOBS_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOBS_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x2 = el.x1; + el.y2 = 0.; + arch->addDecalGraphic(id_DECAL_IOBS_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOBS_ACTIVE, el); + el.style = GraphicElement::STYLE_INACTIVE; + el.x1 = ios_w; + el.x2 = ios_w * 1.3f; + el.y2 = el.y1 = ios_h / 2.f; + arch->addDecalGraphic(id_DECAL_IOBS_INACTIVE, el); + el.style = GraphicElement::STYLE_ACTIVE; + arch->addDecalGraphic(id_DECAL_IOBS_ACTIVE, el); +} + +void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel) +{ + DecalXY active, inactive; + switch (bel.type.hash()) { + case ID_SLICE: + active.x = inactive.x = bel.x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + lut_y[bel.z]; + if (bel.z < 6) { + active.decal = id_DECAL_LUTDFF_ACTIVE; + inactive.decal = id_DECAL_LUTDFF_INACTIVE; + } else { + active.decal = id_DECAL_LUT_ACTIVE; + inactive.decal = id_DECAL_LUT_INACTIVE; + } + arch->setBelDecal(bel.name, active, inactive); + break; + case ID_GW_MUX2_LUT5: + active.x = inactive.x = bel.x + mux2lut5_x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - arch->mux_0_z) >> 1]; + active.decal = id_DECAL_MUXUPPER_ACTIVE; + inactive.decal = id_DECAL_MUXUPPER_INACTIVE; + arch->setBelDecal(bel.name, active, inactive); + break; + case ID_GW_MUX2_LUT6: + active.x = inactive.x = bel.x + mux2lut6_x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - arch->mux_0_z) / 5]; + active.decal = id_DECAL_MUXLOWER_ACTIVE; + inactive.decal = id_DECAL_MUXLOWER_INACTIVE; + arch->setBelDecal(bel.name, active, inactive); + break; + case ID_GW_MUX2_LUT7: + active.x = inactive.x = bel.x + mux2lut7_x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut7_y; + active.decal = id_DECAL_MUXLOWER_ACTIVE; + inactive.decal = id_DECAL_MUXLOWER_INACTIVE; + arch->setBelDecal(bel.name, active, inactive); + break; + case ID_GW_MUX2_LUT8: + active.x = inactive.x = bel.x + mux2lut8_x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut8_y; + active.decal = id_DECAL_MUXUPPER_ACTIVE; + inactive.decal = id_DECAL_MUXUPPER_INACTIVE; + arch->setBelDecal(bel.name, active, inactive); + break; + case ID_IOB: + active.x = inactive.x = bel.x + io_x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + io_y + bel.z * (2 * io_gap + io_h); + active.decal = id_DECAL_IOB_ACTIVE; + inactive.decal = id_DECAL_IOB_INACTIVE; + arch->setBelDecal(bel.name, active, inactive); + gfxSetIOBWireDecals(arch, bel); + break; + case ID_IOBS: + active.x = inactive.x = bel.x + ios_x + (ios_w + ios_gap_x) * (bel.z % 3); + active.y = inactive.y = arch->gridDimY - 1. - bel.y + ios_y + (ios_h + ios_gap_y) * (bel.z / 3); + active.decal = id_DECAL_IOBS_ACTIVE; + inactive.decal = id_DECAL_IOBS_INACTIVE; + arch->setBelDecal(bel.name, active, inactive); + gfxSetIOBSWireDecals(arch, bel); + break; + default: + break; + } +} + +void gfxSetIOBWireDecals(Arch *arch, BelInfo &bel) +{ + DecalXY active, inactive; + GraphicElement el; + char buf[20]; + + // set decals for I, O and OE input wires + for (auto pi : bel.pins) { + WireInfo &wi = arch->wire_info(pi.second.wire); + // decal name: wire_port_z_active|inactive + snprintf(buf, sizeof(buf), "%s_%s_%u_active", wi.type.c_str(arch), pi.first.c_str(arch), bel.z); + IdString active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "%s_%s_%u_inactive", wi.type.c_str(arch), pi.first.c_str(arch), bel.z); + IdString inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = bel.x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y; + if (arch->decal_graphics.count(active_id) == 0) { + el.type = GraphicElement::TYPE_LOCAL_LINE; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = cru_x + cru_w; + el.y1 = pipPoint.at(wi.type).second; + el.x2 = io_x; + el.y2 = portPoint.at(pi.first) + io_y + bel.z * (2 * io_gap + io_h); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + for (auto seg : portSign.at(pi.first)) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = std::get<0>(seg) + io_x; + el.y1 = std::get<1>(seg) + io_y + bel.z * (2 * io_gap + io_h); + el.x2 = std::get<2>(seg) + io_x; + el.y2 = std::get<3>(seg) + io_y + bel.z * (2 * io_gap + io_h); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + } + arch->setWireDecal(wi.name, active, inactive); + } +} + +void gfxSetIOBSWireDecals(Arch *arch, BelInfo &bel) +{ + DecalXY active, inactive; + GraphicElement el; + char buf[20]; + + // set decals for I, O and OE input wires + for (auto pi : bel.pins) { + WireInfo &wi = arch->wire_info(pi.second.wire); + // decal name: ios_wire_port_z_active|inactive + snprintf(buf, sizeof(buf), "ios_%s_%s_%u_active", wi.type.c_str(arch), pi.first.c_str(arch), bel.z); + IdString active_id = arch->id(buf); + active.decal = active_id; + snprintf(buf, sizeof(buf), "ios_%s_%s_%u_inactive", wi.type.c_str(arch), pi.first.c_str(arch), bel.z); + IdString inactive_id = arch->id(buf); + inactive.decal = inactive_id; + active.x = inactive.x = bel.x; + active.y = inactive.y = arch->gridDimY - 1. - bel.y; + if (arch->decal_graphics.count(active_id) == 0) { + // leftmost wires + el.type = GraphicElement::TYPE_LOCAL_LINE; + if (bel.z % 3 == 0) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = cru_x + cru_w; + el.y1 = pipPoint.at(wi.type).second; + el.x2 = ios_x; + el.y2 = ios_scl * portPoint.at(pi.first) + ios_y + (ios_h + ios_gap_y) * (bel.z / 3); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } else { + float col = (bel.z % 3) - 1; + float rel_port = portPoint.at(pi.first) / io_h; + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = cru_x + cru_w; + el.y1 = pipPoint.at(wi.type).second; + el.x2 = ios_x * (0.97 - 0.02 * col); + el.y2 = (rel_port + col) * 0.5 * ios_gap_y + ios_y + ios_h + (ios_h + ios_gap_y) * (bel.z / 3); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = ios_x + (ios_w + ios_gap_x) * (col + 1) - ios_gap_x + ios_w * 0.3 + + rel_port * (ios_gap_x - 0.3 * ios_w); + el.y1 = el.y2; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + el.style = GraphicElement::STYLE_ACTIVE; + el.x2 = el.x1; + el.y2 = ios_scl * portPoint.at(pi.first) + ios_y + (ios_h + ios_gap_y) * (bel.z / 3); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = ios_x + (ios_w + ios_gap_x) * (col + 1); + el.y1 = el.y2; + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + el.style = GraphicElement::STYLE_ACTIVE; + } + // signs + for (auto seg : portSign.at(pi.first)) { + el.style = GraphicElement::STYLE_ACTIVE; + el.x1 = ios_scl * std::get<0>(seg) + ios_x + (ios_w + ios_gap_x) * (bel.z % 3); + el.y1 = ios_scl * std::get<1>(seg) + ios_y + (ios_h + ios_gap_y) * (bel.z / 3); + el.x2 = ios_scl * std::get<2>(seg) + ios_x + (ios_w + ios_gap_x) * (bel.z % 3); + el.y2 = ios_scl * std::get<3>(seg) + ios_y + (ios_h + ios_gap_y) * (bel.z / 3); + arch->addDecalGraphic(active_id, el); + el.style = GraphicElement::STYLE_INACTIVE; + arch->addDecalGraphic(inactive_id, el); + } + } + arch->setWireDecal(wi.name, active, inactive); + } +} + +DecalXY gfxGetLutGroupDecalXY(int x, int y, int z) +{ + DecalXY decalxy; + decalxy.decal = id_DECAL_GRP_LUT; + decalxy.x = x; + decalxy.y = y + grp_lut_y[z]; + return decalxy; +} + +DecalXY gfxGetCruGroupDecalXY(int x, int y) +{ + DecalXY decalxy; + decalxy.decal = id_DECAL_CRU; + decalxy.x = x; + decalxy.y = y; + return decalxy; +} + +NEXTPNR_NAMESPACE_END diff --git a/gowin/gfx.h b/gowin/gfx.h new file mode 100644 index 0000000000..574d73106a --- /dev/null +++ b/gowin/gfx.h @@ -0,0 +1,4935 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Claire Xenia Wolf + * + * 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 GFX_H +#define GFX_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +// LUTs +const float lut_w = 0.6732 - 0.6386; +const float lut_h = 0.9392 - 0.9074; +const float lut_x = 0.6386; +const float lut_y[] = {1. - 0.9392, 1. - 0.8870, 1. - 0.7864, 1. - 0.7321, + 1. - 0.6399, 1. - 0.5847, 1. - 0.5068, 1. - 0.4503}; +const float dff_w = 0.0580; +const float dff_x = 0.6821; +const float grp_lut_w = 0.1399; +const float grp_lut_h = 0.0954; +const float grp_lut_x = 0.6284; +const float grp_lut_y[] = {1. - 0.9440, 1. - 0.7915, 1. - 0.6442, 1. - 0.5101}; + +// mux +const float mux_w = 0.8134 - 0.7899; +const float mux_f = 0.9450 - 0.9358; +const float mux_h = grp_lut_h; + +const float mux2lut5_x = 0.7900; +const float mux2lut5_y[] = {grp_lut_y[0], grp_lut_y[1], grp_lut_y[2], grp_lut_y[3]}; +const float mux2lut6_x = 0.8378; +const float mux2lut6_y[] = {1. - 0.9261, 1. - 0.6205}; +const float mux2lut7_x = 0.8859; +const float mux2lut7_y = 1. - 0.7870; +const float mux2lut8_x = 0.9337; +const float mux2lut8_y = 1. - 0.8098; + +// pip +enum CruSide +{ + Top, + Bottom, + Left, + Right, + Center +}; +const float cru_x = 0.2568; +const float cru_y = 1. - 0.9783; +const float cru_w = 0.6010 - cru_x; +const float cru_h = 1. - cru_y - 0.3742; + +const float lut_A_off = 1. - 0.9107 - lut_y[0]; +const float lut_D_off = lut_h - lut_A_off; +const float lut_B_off = lut_A_off - (lut_h - lut_D_off) / 3.; +const float lut_C_off = lut_D_off + (lut_h - lut_D_off) / 3.; + +const float right_wire_dist = (grp_lut_y[1] - grp_lut_y[0] - grp_lut_h) / 11.; +const float left_wire_dist = cru_h / 100.; +const float top_wire_dist = cru_w / 100.; +const float clk_ce_set_vdist = (lut_y[1] - lut_y[0] - lut_h) / 4.; + +const float sn_dist = cru_x / 125.; +const float ew_dist = (1. - cru_y - cru_h) / 130.; +const float wrap_len = 0.02f; +const float spine_pip_off = 0.11f; + +const float io_x = cru_x + cru_w + 0.1; +const float io_w = (1. - io_x) / 3.; +const float io_gap = 0.03; +const float io_h = (cru_h - 4. * io_gap) / 2.; +const float io_y = cru_y + io_gap; + +const float ios_scl = 0.5; +const float ios_h = ios_scl * io_h; +const float ios_w = ios_scl * io_w; +const float ios_gap_y = io_gap; +const float ios_gap_x = io_gap * 1.4; +const float ios_x = io_x; +const float ios_y = ios_scl * io_y; + +const dict portPoint = { + {id_O, 3. * io_h / 4.}, + {id_I, 2. * io_h / 4.}, + {id_OEN, 1. * io_h / 4.}, +}; + +const dict>> portSign = { + {id_O, + {{io_h / 14. * 1.33, portPoint.at(id_O) + io_h / 14., io_h / 14. * 1.66, portPoint.at(id_O) + io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_O) + io_h / 14., io_h / 14. * 2., portPoint.at(id_O) + io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_O) + io_h / 14. * 0.6, io_h / 14. * 2., + portPoint.at(id_O) - io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_O) - io_h / 14. * 0.6, io_h / 14. * 1.66, portPoint.at(id_O) - io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_O) - io_h / 14., io_h / 14. * 1.33, portPoint.at(id_O) - io_h / 14.}, + {io_h / 14. * 1.33, portPoint.at(id_O) - io_h / 14., io_h / 14. * 1., portPoint.at(id_O) - io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_O) - io_h / 14. * 0.6, io_h / 14. * 1., + portPoint.at(id_O) + io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_O) + io_h / 14. * 0.6, io_h / 14. * 1.33, + portPoint.at(id_O) + io_h / 14.}}}, + {id_I, + {{io_h / 14., portPoint.at(id_I) + io_h / 14., 2. * io_h / 14., portPoint.at(id_I) + io_h / 14.}, + {io_h / 14. * 1.5, portPoint.at(id_I) + io_h / 14., 1. * io_h / 14. * 1.5, portPoint.at(id_I) - io_h / 14.}, + {io_h / 14., portPoint.at(id_I) - io_h / 14., 2. * io_h / 14., portPoint.at(id_I) - io_h / 14.}}}, + {id_OEN, + {{io_h / 14. * 1.33, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 1.66, portPoint.at(id_OEN) + io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 2., + portPoint.at(id_OEN) + io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_OEN) + io_h / 14. * 0.6, io_h / 14. * 2., + portPoint.at(id_OEN) - io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_OEN) - io_h / 14. * 0.6, io_h / 14. * 1.66, + portPoint.at(id_OEN) - io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 1.33, portPoint.at(id_OEN) - io_h / 14.}, + {io_h / 14. * 1.33, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 1., + portPoint.at(id_OEN) - io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_OEN) - io_h / 14. * 0.6, io_h / 14. * 1., + portPoint.at(id_OEN) + io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_OEN) + io_h / 14. * 0.6, io_h / 14. * 1.33, + portPoint.at(id_OEN) + io_h / 14.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 3.2, portPoint.at(id_OEN) + io_h / 14.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) + 0., io_h / 14. * 3.2, portPoint.at(id_OEN) + 0.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 3.2, portPoint.at(id_OEN) - io_h / 14.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 2.2, portPoint.at(id_OEN) - io_h / 14.}}}, +}; + +const dict spineY = { + {id_SPINE0, 1. - 1. * ew_dist}, {id_SPINE1, 1. - 2. * ew_dist}, {id_SPINE2, 1. - 3. * ew_dist}, + {id_SPINE3, 1. - 4. * ew_dist}, {id_SPINE4, 1. - 5. * ew_dist}, {id_SPINE5, 1. - 6. * ew_dist}, + {id_SPINE6, 1. - 7. * ew_dist}, {id_SPINE7, 1. - 8. * ew_dist}, {id_SPINE8, 1. - 1. * ew_dist}, + {id_SPINE9, 1. - 2. * ew_dist}, {id_SPINE10, 1. - 3. * ew_dist}, {id_SPINE11, 1. - 4. * ew_dist}, + {id_SPINE12, 1. - 5. * ew_dist}, {id_SPINE13, 1. - 6. * ew_dist}, {id_SPINE14, 1. - 7. * ew_dist}, + {id_SPINE15, 1. - 8. * ew_dist}, {id_SPINE16, 1. - 1. * ew_dist}, {id_SPINE17, 1. - 2. * ew_dist}, + {id_SPINE18, 1. - 3. * ew_dist}, {id_SPINE19, 1. - 4. * ew_dist}, {id_SPINE20, 1. - 5. * ew_dist}, + {id_SPINE21, 1. - 6. * ew_dist}, {id_SPINE22, 1. - 7. * ew_dist}, {id_SPINE23, 1. - 8. * ew_dist}, + {id_SPINE24, 1. - 1. * ew_dist}, {id_SPINE25, 1. - 2. * ew_dist}, {id_SPINE26, 1. - 3. * ew_dist}, + {id_SPINE27, 1. - 4. * ew_dist}, {id_SPINE28, 1. - 5. * ew_dist}, {id_SPINE29, 1. - 6. * ew_dist}, + {id_SPINE30, 1. - 7. * ew_dist}, {id_SPINE31, 1. - 8. * ew_dist}, +}; + +const dict> pipPoint = { + {id_X01, {Center, cru_y + 1. * cru_h / 9.}}, + {id_X02, {Center, cru_y + 2. * cru_h / 9.}}, + {id_X03, {Center, cru_y + 3. * cru_h / 9.}}, + {id_X04, {Center, cru_y + 4. * cru_h / 9.}}, + {id_X05, {Center, cru_y + 5. * cru_h / 9.}}, + {id_X06, {Center, cru_y + 6. * cru_h / 9.}}, + {id_X07, {Center, cru_y + 7. * cru_h / 9.}}, + {id_X08, {Center, cru_y + 8. * cru_h / 9.}}, + // LUT inputs + {id_A0, {Right, lut_y[0] + lut_A_off}}, + {id_B0, {Right, lut_y[0] + lut_B_off}}, + {id_C0, {Right, lut_y[0] + lut_C_off}}, + {id_D0, {Right, lut_y[0] + lut_D_off}}, + {id_A1, {Right, lut_y[1] + lut_A_off}}, + {id_B1, {Right, lut_y[1] + lut_B_off}}, + {id_C1, {Right, lut_y[1] + lut_C_off}}, + {id_D1, {Right, lut_y[1] + lut_D_off}}, + {id_A2, {Right, lut_y[2] + lut_A_off}}, + {id_B2, {Right, lut_y[2] + lut_B_off}}, + {id_C2, {Right, lut_y[2] + lut_C_off}}, + {id_D2, {Right, lut_y[2] + lut_D_off}}, + {id_A3, {Right, lut_y[3] + lut_A_off}}, + {id_B3, {Right, lut_y[3] + lut_B_off}}, + {id_C3, {Right, lut_y[3] + lut_C_off}}, + {id_D3, {Right, lut_y[3] + lut_D_off}}, + {id_A4, {Right, lut_y[4] + lut_A_off}}, + {id_B4, {Right, lut_y[4] + lut_B_off}}, + {id_C4, {Right, lut_y[4] + lut_C_off}}, + {id_D4, {Right, lut_y[4] + lut_D_off}}, + {id_A5, {Right, lut_y[5] + lut_A_off}}, + {id_B5, {Right, lut_y[5] + lut_B_off}}, + {id_C5, {Right, lut_y[5] + lut_C_off}}, + {id_D5, {Right, lut_y[5] + lut_D_off}}, + {id_A6, {Right, lut_y[6] + lut_A_off}}, + {id_B6, {Right, lut_y[6] + lut_B_off}}, + {id_C6, {Right, lut_y[6] + lut_C_off}}, + {id_D6, {Right, lut_y[6] + lut_D_off}}, + {id_A7, {Right, lut_y[7] + lut_A_off}}, + {id_B7, {Right, lut_y[7] + lut_B_off}}, + {id_C7, {Right, lut_y[7] + lut_C_off}}, + {id_D7, {Right, lut_y[7] + lut_D_off}}, + // wires below LUT0 + {id_Q0, {Right, grp_lut_y[0] - right_wire_dist}}, + {id_F0, {Right, grp_lut_y[0] - 2. * right_wire_dist}}, + {id_OF3, {Right, grp_lut_y[0] - 3. * right_wire_dist}}, + // wires between LUT1 and LUT2 + {id_Q2, {Right, grp_lut_y[1] - right_wire_dist}}, + {id_F2, {Right, grp_lut_y[1] - 2. * right_wire_dist}}, + {id_OF2, {Right, grp_lut_y[1] - 3. * right_wire_dist}}, + {id_OF1, {Right, grp_lut_y[1] - 4. * right_wire_dist}}, + {id_OF0, {Right, grp_lut_y[1] - 5. * right_wire_dist}}, + {id_SEL1, {Right, grp_lut_y[1] - 6. * right_wire_dist}}, + {id_OF7, {Right, grp_lut_y[1] - 7. * right_wire_dist}}, + {id_SEL0, {Right, grp_lut_y[1] - 8. * right_wire_dist}}, + {id_F1, {Right, grp_lut_y[1] - 9. * right_wire_dist}}, + {id_Q1, {Right, grp_lut_y[1] - 10. * right_wire_dist}}, + // wires between LUT3 and LUT4 + {id_Q4, {Right, grp_lut_y[2] - right_wire_dist}}, + {id_F4, {Right, grp_lut_y[2] - 2. * right_wire_dist}}, + {id_OF4, {Right, grp_lut_y[2] - 3. * right_wire_dist}}, + {id_OF5, {Right, grp_lut_y[2] - 4. * right_wire_dist}}, + {id_SEL7, {Right, grp_lut_y[2] - 5. * right_wire_dist}}, + {id_SEL3, {Right, grp_lut_y[2] - 6. * right_wire_dist}}, + {id_SEL2, {Right, grp_lut_y[2] - 7. * right_wire_dist}}, + {id_F3, {Right, grp_lut_y[2] - 8. * right_wire_dist}}, + {id_Q3, {Right, grp_lut_y[2] - 9. * right_wire_dist}}, + // wires between LUT5 and LUT6 + {id_F6, {Right, grp_lut_y[3] - right_wire_dist}}, + {id_SEL5, {Right, grp_lut_y[3] - 2. * right_wire_dist}}, + {id_SEL4, {Right, grp_lut_y[3] - 4. * right_wire_dist}}, + {id_F5, {Right, grp_lut_y[3] - 5. * right_wire_dist}}, + {id_Q5, {Right, grp_lut_y[3] - 6. * right_wire_dist}}, + // Q6, Q7 --- IOB + {id_Q6, {Right, grp_lut_y[3] + grp_lut_h * 0.33}}, + {id_Q7, {Right, grp_lut_y[3] + grp_lut_h * 0.66}}, + // wires above LUT7 + {id_F7, {Right, grp_lut_y[3] + grp_lut_h + right_wire_dist}}, + {id_SEL6, {Right, grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist}}, + {id_OF6, {Right, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist}}, + // DI0-5 + {id_DI5, {Right, cru_y + cru_h - 0.5 * right_wire_dist}}, + {id_DI4, {Right, cru_y + cru_h - 1. * right_wire_dist}}, + {id_DI3, {Right, cru_y + cru_h - 1.5 * right_wire_dist}}, + {id_DI2, {Right, cru_y + cru_h - 2. * right_wire_dist}}, + {id_DI1, {Right, cru_y + cru_h - 2.5 * right_wire_dist}}, + {id_DI0, {Right, cru_y + cru_h - 3. * right_wire_dist}}, + // Q6 + // CLK, CE, SET-RESET + {id_CLK0, {Right, lut_y[1] - clk_ce_set_vdist}}, + {id_CE0, {Right, lut_y[1] - 2. * clk_ce_set_vdist}}, + {id_LSR0, {Right, lut_y[1] - 3. * clk_ce_set_vdist}}, + {id_CLK1, {Right, lut_y[3] - clk_ce_set_vdist}}, + {id_CE1, {Right, lut_y[3] - 2. * clk_ce_set_vdist}}, + {id_LSR1, {Right, lut_y[3] - 3. * clk_ce_set_vdist}}, + {id_CLK2, {Right, lut_y[5] - clk_ce_set_vdist}}, + {id_CE2, {Right, lut_y[5] - 2. * clk_ce_set_vdist}}, + {id_LSR2, {Right, lut_y[5] - 3. * clk_ce_set_vdist}}, + // SN + // 1 hop + {id_S100, {Left, cru_y + 1. * left_wire_dist}}, + {id_S101, {Left, cru_y + 2. * left_wire_dist}}, + {id_S130, {Left, cru_y + 3. * left_wire_dist}}, + {id_S131, {Left, cru_y + 4. * left_wire_dist}}, + {id_N101, {Left, cru_y + 5. * left_wire_dist}}, + {id_N100, {Left, cru_y + 6. * left_wire_dist}}, + {id_N131, {Left, cru_y + 7. * left_wire_dist}}, + {id_N130, {Left, cru_y + 8. * left_wire_dist}}, + // 1 hop SN + {id_N111, {Left, cru_y + 9. * left_wire_dist}}, + {id_SN10, {Left, cru_y + 10. * left_wire_dist}}, + {id_S111, {Left, cru_y + 11. * left_wire_dist}}, + {id_N121, {Left, cru_y + 12. * left_wire_dist}}, + {id_SN20, {Left, cru_y + 13. * left_wire_dist}}, + {id_S121, {Left, cru_y + 14. * left_wire_dist}}, + // 2 hop + {id_S200, {Left, cru_y + 15. * left_wire_dist}}, + {id_S201, {Left, cru_y + 16. * left_wire_dist}}, + {id_N202, {Left, cru_y + 17. * left_wire_dist}}, + {id_S202, {Left, cru_y + 18. * left_wire_dist}}, + {id_N201, {Left, cru_y + 19. * left_wire_dist}}, + {id_N200, {Left, cru_y + 20. * left_wire_dist}}, + + {id_S210, {Left, cru_y + 21. * left_wire_dist}}, + {id_S211, {Left, cru_y + 22. * left_wire_dist}}, + {id_N212, {Left, cru_y + 23. * left_wire_dist}}, + {id_S212, {Left, cru_y + 24. * left_wire_dist}}, + {id_N211, {Left, cru_y + 25. * left_wire_dist}}, + {id_N210, {Left, cru_y + 26. * left_wire_dist}}, + + {id_S220, {Left, cru_y + 27. * left_wire_dist}}, + {id_S221, {Left, cru_y + 28. * left_wire_dist}}, + {id_N222, {Left, cru_y + 29. * left_wire_dist}}, + {id_S222, {Left, cru_y + 30. * left_wire_dist}}, + {id_N221, {Left, cru_y + 31. * left_wire_dist}}, + {id_N220, {Left, cru_y + 32. * left_wire_dist}}, + + {id_S230, {Left, cru_y + 33. * left_wire_dist}}, + {id_S231, {Left, cru_y + 34. * left_wire_dist}}, + {id_N232, {Left, cru_y + 35. * left_wire_dist}}, + {id_S232, {Left, cru_y + 36. * left_wire_dist}}, + {id_N231, {Left, cru_y + 37. * left_wire_dist}}, + {id_N230, {Left, cru_y + 38. * left_wire_dist}}, + + {id_S240, {Left, cru_y + 39. * left_wire_dist}}, + {id_S241, {Left, cru_y + 40. * left_wire_dist}}, + {id_N242, {Left, cru_y + 41. * left_wire_dist}}, + {id_S242, {Left, cru_y + 42. * left_wire_dist}}, + {id_N241, {Left, cru_y + 43. * left_wire_dist}}, + {id_N240, {Left, cru_y + 44. * left_wire_dist}}, + + {id_S250, {Left, cru_y + 45. * left_wire_dist}}, + {id_S251, {Left, cru_y + 46. * left_wire_dist}}, + {id_N252, {Left, cru_y + 47. * left_wire_dist}}, + {id_S252, {Left, cru_y + 48. * left_wire_dist}}, + {id_N251, {Left, cru_y + 49. * left_wire_dist}}, + {id_N250, {Left, cru_y + 50. * left_wire_dist}}, + + {id_S260, {Left, cru_y + 51. * left_wire_dist}}, + {id_S261, {Left, cru_y + 52. * left_wire_dist}}, + {id_N262, {Left, cru_y + 53. * left_wire_dist}}, + {id_S262, {Left, cru_y + 54. * left_wire_dist}}, + {id_N261, {Left, cru_y + 55. * left_wire_dist}}, + {id_N260, {Left, cru_y + 56. * left_wire_dist}}, + + {id_S270, {Left, cru_y + 57. * left_wire_dist}}, + {id_S271, {Left, cru_y + 58. * left_wire_dist}}, + {id_N272, {Left, cru_y + 59. * left_wire_dist}}, + {id_S272, {Left, cru_y + 60. * left_wire_dist}}, + {id_N271, {Left, cru_y + 61. * left_wire_dist}}, + {id_N270, {Left, cru_y + 62. * left_wire_dist}}, + + // Clocks + {id_GT10, {Left, cru_y + 63. * left_wire_dist}}, + {id_GT00, {Left, cru_y + 68. * left_wire_dist}}, + + // 4 hop + {id_N808, {Left, cru_y + 73. * left_wire_dist}}, + {id_S800, {Left, cru_y + 74. * left_wire_dist}}, + {id_S804, {Left, cru_y + 75. * left_wire_dist}}, + {id_N804, {Left, cru_y + 76. * left_wire_dist}}, + {id_N800, {Left, cru_y + 77. * left_wire_dist}}, + {id_S808, {Left, cru_y + 78. * left_wire_dist}}, + + {id_N818, {Left, cru_y + 79. * left_wire_dist}}, + {id_S810, {Left, cru_y + 80. * left_wire_dist}}, + {id_S814, {Left, cru_y + 81. * left_wire_dist}}, + {id_N814, {Left, cru_y + 82. * left_wire_dist}}, + {id_N810, {Left, cru_y + 83. * left_wire_dist}}, + {id_S818, {Left, cru_y + 84. * left_wire_dist}}, + + {id_N828, {Left, cru_y + 85. * left_wire_dist}}, + {id_S820, {Left, cru_y + 86. * left_wire_dist}}, + {id_S824, {Left, cru_y + 87. * left_wire_dist}}, + {id_N824, {Left, cru_y + 88. * left_wire_dist}}, + {id_N820, {Left, cru_y + 89. * left_wire_dist}}, + {id_S828, {Left, cru_y + 90. * left_wire_dist}}, + + {id_N838, {Left, cru_y + 91. * left_wire_dist}}, + {id_S830, {Left, cru_y + 92. * left_wire_dist}}, + {id_S834, {Left, cru_y + 93. * left_wire_dist}}, + {id_N834, {Left, cru_y + 94. * left_wire_dist}}, + {id_N830, {Left, cru_y + 95. * left_wire_dist}}, + {id_S838, {Left, cru_y + 96. * left_wire_dist}}, + + // EW + // 1 hop + {id_E101, {Top, cru_x + 1. * top_wire_dist}}, + {id_E100, {Top, cru_x + 2. * top_wire_dist}}, + {id_E131, {Top, cru_x + 3. * top_wire_dist}}, + {id_E130, {Top, cru_x + 4. * top_wire_dist}}, + {id_W100, {Top, cru_x + 5. * top_wire_dist}}, + {id_W101, {Top, cru_x + 6. * top_wire_dist}}, + {id_W130, {Top, cru_x + 7. * top_wire_dist}}, + {id_W131, {Top, cru_x + 8. * top_wire_dist}}, + // 1 hop EW + {id_E111, {Top, cru_x + 9. * top_wire_dist}}, + {id_EW10, {Top, cru_x + 10. * top_wire_dist}}, + {id_W111, {Top, cru_x + 11. * top_wire_dist}}, + {id_E121, {Top, cru_x + 12. * top_wire_dist}}, + {id_EW20, {Top, cru_x + 13. * top_wire_dist}}, + {id_W121, {Top, cru_x + 14. * top_wire_dist}}, + // 2 hop + {id_E202, {Top, cru_x + 15. * top_wire_dist}}, + {id_E201, {Top, cru_x + 16. * top_wire_dist}}, + {id_W200, {Top, cru_x + 17. * top_wire_dist}}, + {id_E200, {Top, cru_x + 18. * top_wire_dist}}, + {id_W201, {Top, cru_x + 19. * top_wire_dist}}, + {id_W202, {Top, cru_x + 20. * top_wire_dist}}, + + {id_E212, {Top, cru_x + 21. * top_wire_dist}}, + {id_E211, {Top, cru_x + 22. * top_wire_dist}}, + {id_W210, {Top, cru_x + 23. * top_wire_dist}}, + {id_E210, {Top, cru_x + 24. * top_wire_dist}}, + {id_W211, {Top, cru_x + 25. * top_wire_dist}}, + {id_W212, {Top, cru_x + 26. * top_wire_dist}}, + + {id_E222, {Top, cru_x + 27. * top_wire_dist}}, + {id_E221, {Top, cru_x + 28. * top_wire_dist}}, + {id_W220, {Top, cru_x + 29. * top_wire_dist}}, + {id_E220, {Top, cru_x + 30. * top_wire_dist}}, + {id_W221, {Top, cru_x + 31. * top_wire_dist}}, + {id_W222, {Top, cru_x + 32. * top_wire_dist}}, + + {id_E232, {Top, cru_x + 33. * top_wire_dist}}, + {id_E231, {Top, cru_x + 34. * top_wire_dist}}, + {id_W230, {Top, cru_x + 35. * top_wire_dist}}, + {id_E230, {Top, cru_x + 36. * top_wire_dist}}, + {id_W231, {Top, cru_x + 37. * top_wire_dist}}, + {id_W232, {Top, cru_x + 38. * top_wire_dist}}, + + {id_E242, {Top, cru_x + 39. * top_wire_dist}}, + {id_E241, {Top, cru_x + 40. * top_wire_dist}}, + {id_W240, {Top, cru_x + 41. * top_wire_dist}}, + {id_E240, {Top, cru_x + 42. * top_wire_dist}}, + {id_W241, {Top, cru_x + 43. * top_wire_dist}}, + {id_W242, {Top, cru_x + 44. * top_wire_dist}}, + + {id_E252, {Top, cru_x + 45. * top_wire_dist}}, + {id_E251, {Top, cru_x + 46. * top_wire_dist}}, + {id_W250, {Top, cru_x + 47. * top_wire_dist}}, + {id_E250, {Top, cru_x + 48. * top_wire_dist}}, + {id_W251, {Top, cru_x + 49. * top_wire_dist}}, + {id_W252, {Top, cru_x + 50. * top_wire_dist}}, + + {id_E262, {Top, cru_x + 51. * top_wire_dist}}, + {id_E261, {Top, cru_x + 52. * top_wire_dist}}, + {id_W260, {Top, cru_x + 53. * top_wire_dist}}, + {id_E260, {Top, cru_x + 54. * top_wire_dist}}, + {id_W261, {Top, cru_x + 55. * top_wire_dist}}, + {id_W262, {Top, cru_x + 56. * top_wire_dist}}, + + {id_E272, {Top, cru_x + 57. * top_wire_dist}}, + {id_E271, {Top, cru_x + 58. * top_wire_dist}}, + {id_W270, {Top, cru_x + 59. * top_wire_dist}}, + {id_E270, {Top, cru_x + 60. * top_wire_dist}}, + {id_W271, {Top, cru_x + 61. * top_wire_dist}}, + {id_W272, {Top, cru_x + 62. * top_wire_dist}}, + + // Global taps -> bracnhes + {id_GBO0, {Top, cru_x + 63. * top_wire_dist}}, + {id_GB00, {Top, cru_x + 64. * top_wire_dist}}, + {id_GB10, {Top, cru_x + 65. * top_wire_dist}}, + {id_GB20, {Top, cru_x + 66. * top_wire_dist}}, + {id_GB30, {Top, cru_x + 67. * top_wire_dist}}, + {id_GBO1, {Top, cru_x + 68. * top_wire_dist}}, + {id_GB40, {Top, cru_x + 68. * top_wire_dist}}, + {id_GB50, {Top, cru_x + 69. * top_wire_dist}}, + {id_GB60, {Top, cru_x + 70. * top_wire_dist}}, + {id_GB70, {Top, cru_x + 71. * top_wire_dist}}, + + // 4 hop + {id_E808, {Top, cru_x + 72. * top_wire_dist}}, + {id_W800, {Top, cru_x + 73. * top_wire_dist}}, + {id_W804, {Top, cru_x + 74. * top_wire_dist}}, + {id_E804, {Top, cru_x + 75. * top_wire_dist}}, + {id_E800, {Top, cru_x + 76. * top_wire_dist}}, + {id_W808, {Top, cru_x + 77. * top_wire_dist}}, + + {id_E818, {Top, cru_x + 78. * top_wire_dist}}, + {id_W810, {Top, cru_x + 79. * top_wire_dist}}, + {id_W814, {Top, cru_x + 80. * top_wire_dist}}, + {id_E814, {Top, cru_x + 81. * top_wire_dist}}, + {id_E810, {Top, cru_x + 82. * top_wire_dist}}, + {id_W818, {Top, cru_x + 83. * top_wire_dist}}, + + {id_E828, {Top, cru_x + 84. * top_wire_dist}}, + {id_W820, {Top, cru_x + 85. * top_wire_dist}}, + {id_W824, {Top, cru_x + 86. * top_wire_dist}}, + {id_E824, {Top, cru_x + 87. * top_wire_dist}}, + {id_E820, {Top, cru_x + 88. * top_wire_dist}}, + {id_W828, {Top, cru_x + 89. * top_wire_dist}}, + + {id_E838, {Top, cru_x + 90. * top_wire_dist}}, + {id_W830, {Top, cru_x + 91. * top_wire_dist}}, + {id_W834, {Top, cru_x + 92. * top_wire_dist}}, + {id_E834, {Top, cru_x + 93. * top_wire_dist}}, + {id_E830, {Top, cru_x + 94. * top_wire_dist}}, + {id_W838, {Top, cru_x + 95. * top_wire_dist}}, + +}; + +// wire +const std::vector decalless_wires = {id_X01, id_X02, id_X03, id_X04, id_X05, id_X06, id_X07}; + +const float clk_ce_set_hdist = dff_w / 4.; +const float dff_f_x = (grp_lut_x + grp_lut_w + dff_x + dff_w) / 2.; +const float mux5i_x = (grp_lut_x + grp_lut_w + mux2lut5_x) / 2.; + +// id, {x1, y1, x2, y2} +const dict>> sliceLocalWires = { + // dff + {id_CLK0, + {{cru_x + cru_w, pipPoint.at(id_CLK0).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK0).second}, + {dff_x + clk_ce_set_hdist, lut_y[1], dff_x + clk_ce_set_hdist, lut_y[0] + lut_h}}}, + {id_CE0, + {{cru_x + cru_w, pipPoint.at(id_CE0).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE0).second}, + {dff_x + 2 * clk_ce_set_hdist, lut_y[1], dff_x + 2. * clk_ce_set_hdist, lut_y[0] + lut_h}}}, + {id_LSR0, + {{cru_x + cru_w, pipPoint.at(id_LSR0).second, dff_x + 3. * clk_ce_set_hdist, pipPoint.at(id_LSR0).second}, + {dff_x + 3 * clk_ce_set_hdist, lut_y[1], dff_x + 3. * clk_ce_set_hdist, lut_y[0] + lut_h}}}, + {id_CLK1, + {{cru_x + cru_w, pipPoint.at(id_CLK1).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK1).second}, + {dff_x + clk_ce_set_hdist, lut_y[3], dff_x + clk_ce_set_hdist, lut_y[2] + lut_h}}}, + {id_CE1, + {{cru_x + cru_w, pipPoint.at(id_CE1).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE1).second}, + {dff_x + 2 * clk_ce_set_hdist, lut_y[3], dff_x + 2. * clk_ce_set_hdist, lut_y[2] + lut_h}}}, + {id_LSR1, + {{cru_x + cru_w, pipPoint.at(id_LSR1).second, dff_x + 3. * clk_ce_set_hdist, pipPoint.at(id_LSR1).second}, + {dff_x + 3 * clk_ce_set_hdist, lut_y[3], dff_x + 3. * clk_ce_set_hdist, lut_y[2] + lut_h}}}, + {id_CLK2, + {{cru_x + cru_w, pipPoint.at(id_CLK2).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK2).second}, + {dff_x + clk_ce_set_hdist, lut_y[5], dff_x + clk_ce_set_hdist, lut_y[4] + lut_h}}}, + {id_CE2, + {{cru_x + cru_w, pipPoint.at(id_CE2).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE2).second}, + {dff_x + 2 * clk_ce_set_hdist, lut_y[5], dff_x + 2. * clk_ce_set_hdist, lut_y[4] + lut_h}}}, + {id_LSR2, + {{cru_x + cru_w, pipPoint.at(id_LSR2).second, dff_x + 3 * clk_ce_set_hdist, pipPoint.at(id_LSR2).second}, + {dff_x + 3 * clk_ce_set_hdist, lut_y[5], dff_x + 3. * clk_ce_set_hdist, lut_y[4] + lut_h}}}, + // lut + {id_A0, {{cru_x + cru_w, lut_y[0] + lut_A_off, lut_x, lut_y[0] + lut_A_off}}}, + {id_B0, {{cru_x + cru_w, lut_y[0] + lut_B_off, lut_x, lut_y[0] + lut_B_off}}}, + {id_C0, {{cru_x + cru_w, lut_y[0] + lut_C_off, lut_x, lut_y[0] + lut_C_off}}}, + {id_D0, {{cru_x + cru_w, lut_y[0] + lut_D_off, lut_x, lut_y[0] + lut_D_off}}}, + {id_A1, {{cru_x + cru_w, lut_y[1] + lut_A_off, lut_x, lut_y[1] + lut_A_off}}}, + {id_B1, {{cru_x + cru_w, lut_y[1] + lut_B_off, lut_x, lut_y[1] + lut_B_off}}}, + {id_C1, {{cru_x + cru_w, lut_y[1] + lut_C_off, lut_x, lut_y[1] + lut_C_off}}}, + {id_D1, {{cru_x + cru_w, lut_y[1] + lut_D_off, lut_x, lut_y[1] + lut_D_off}}}, + {id_A2, {{cru_x + cru_w, lut_y[2] + lut_A_off, lut_x, lut_y[2] + lut_A_off}}}, + {id_B2, {{cru_x + cru_w, lut_y[2] + lut_B_off, lut_x, lut_y[2] + lut_B_off}}}, + {id_C2, {{cru_x + cru_w, lut_y[2] + lut_C_off, lut_x, lut_y[2] + lut_C_off}}}, + {id_D2, {{cru_x + cru_w, lut_y[2] + lut_D_off, lut_x, lut_y[2] + lut_D_off}}}, + {id_A3, {{cru_x + cru_w, lut_y[3] + lut_A_off, lut_x, lut_y[3] + lut_A_off}}}, + {id_B3, {{cru_x + cru_w, lut_y[3] + lut_B_off, lut_x, lut_y[3] + lut_B_off}}}, + {id_C3, {{cru_x + cru_w, lut_y[3] + lut_C_off, lut_x, lut_y[3] + lut_C_off}}}, + {id_D3, {{cru_x + cru_w, lut_y[3] + lut_D_off, lut_x, lut_y[3] + lut_D_off}}}, + {id_A4, {{cru_x + cru_w, lut_y[4] + lut_A_off, lut_x, lut_y[4] + lut_A_off}}}, + {id_B4, {{cru_x + cru_w, lut_y[4] + lut_B_off, lut_x, lut_y[4] + lut_B_off}}}, + {id_C4, {{cru_x + cru_w, lut_y[4] + lut_C_off, lut_x, lut_y[4] + lut_C_off}}}, + {id_D4, {{cru_x + cru_w, lut_y[4] + lut_D_off, lut_x, lut_y[4] + lut_D_off}}}, + {id_A5, {{cru_x + cru_w, lut_y[5] + lut_A_off, lut_x, lut_y[5] + lut_A_off}}}, + {id_B5, {{cru_x + cru_w, lut_y[5] + lut_B_off, lut_x, lut_y[5] + lut_B_off}}}, + {id_C5, {{cru_x + cru_w, lut_y[5] + lut_C_off, lut_x, lut_y[5] + lut_C_off}}}, + {id_D5, {{cru_x + cru_w, lut_y[5] + lut_D_off, lut_x, lut_y[5] + lut_D_off}}}, + {id_A6, {{cru_x + cru_w, lut_y[6] + lut_A_off, lut_x, lut_y[6] + lut_A_off}}}, + {id_B6, {{cru_x + cru_w, lut_y[6] + lut_B_off, lut_x, lut_y[6] + lut_B_off}}}, + {id_C6, {{cru_x + cru_w, lut_y[6] + lut_C_off, lut_x, lut_y[6] + lut_C_off}}}, + {id_D6, {{cru_x + cru_w, lut_y[6] + lut_D_off, lut_x, lut_y[6] + lut_D_off}}}, + {id_A7, {{cru_x + cru_w, lut_y[7] + lut_A_off, lut_x, lut_y[7] + lut_A_off}}}, + {id_B7, {{cru_x + cru_w, lut_y[7] + lut_B_off, lut_x, lut_y[7] + lut_B_off}}}, + {id_C7, {{cru_x + cru_w, lut_y[7] + lut_C_off, lut_x, lut_y[7] + lut_C_off}}}, + {id_D7, {{cru_x + cru_w, lut_y[7] + lut_D_off, lut_x, lut_y[7] + lut_D_off}}}, + // wires below LUT0 + {id_Q0, + {{cru_x + cru_w, grp_lut_y[0] - right_wire_dist, dff_f_x, grp_lut_y[0] - right_wire_dist}, + {dff_f_x, grp_lut_y[0] - right_wire_dist, dff_f_x, lut_y[0] + lut_h / 2.}, + {dff_f_x, lut_y[0] + lut_h / 2., dff_x + dff_w, lut_y[0] + lut_h / 2.}}}, + {id_F0, + {{cru_x + cru_w, grp_lut_y[0] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[0] - 2. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[0] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[0] + lut_h / 2.}, + {lut_x + lut_w, lut_y[0] + lut_h / 2., dff_x, lut_y[0] + lut_h / 2.}}}, + {id_I0MUX0, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[0] - 2. * right_wire_dist, mux5i_x, + grp_lut_y[0] - 2. * right_wire_dist}, + {mux5i_x, grp_lut_y[0] - 2. * right_wire_dist, mux5i_x, lut_y[0] + lut_h / 2.}, + {mux5i_x, lut_y[0] + lut_h / 2., mux2lut5_x, lut_y[0] + lut_h / 2.}}}, + {id_OF3, + {{cru_x + cru_w, grp_lut_y[0] - 3. * right_wire_dist, mux2lut7_x + 4. / 3. * mux_w, + grp_lut_y[0] - 3. * right_wire_dist}, + {mux2lut7_x + 4. / 3. * mux_w, grp_lut_y[0] - 3. * right_wire_dist, mux2lut7_x + 4. / 3. * mux_w, + mux2lut7_y + mux_h / 2.}, + {mux2lut7_x + 4. / 3. * mux_w, mux2lut7_y + mux_h / 2., mux2lut7_x + mux_w, mux2lut7_y + mux_h / 2.}}}, + {id_I1MUX7, {{mux2lut7_x + 4. / 3. * mux_w, mux2lut7_y + mux_h / 2., mux2lut8_x, mux2lut7_y + mux_h / 2.}}}, + // wires between LUT1 and LUT2 + {id_Q1, + {{cru_x + cru_w, grp_lut_y[1] - 10. * right_wire_dist, dff_f_x, grp_lut_y[1] - 10. * right_wire_dist}, + {dff_f_x, grp_lut_y[1] - 10. * right_wire_dist, dff_f_x, lut_y[1] + lut_h / 2.}, + {dff_f_x, lut_y[1] + lut_h / 2., dff_x + dff_w, lut_y[1] + lut_h / 2.}}}, + {id_F1, + {{cru_x + cru_w, grp_lut_y[1] - 9. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[1] - 9. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 9. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[1] + lut_h / 2.}, + {lut_x + lut_w, lut_y[1] + lut_h / 2., dff_x, lut_y[1] + lut_h / 2.}}}, + {id_I1MUX0, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 9. * right_wire_dist, mux5i_x, + grp_lut_y[1] - 9. * right_wire_dist}, + {mux5i_x, grp_lut_y[1] - 9. * right_wire_dist, mux5i_x, lut_y[1] + lut_h / 2.}, + {mux5i_x, lut_y[1] + lut_h / 2., mux2lut5_x, lut_y[1] + lut_h / 2.}}}, + {id_SEL0, + {{cru_x + cru_w, grp_lut_y[1] - 8. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[1] - 8. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[1] - 8. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[0] + mux_h - mux_f / 2.}}}, + {id_OF7, + {{cru_x + cru_w, grp_lut_y[1] - 7. * right_wire_dist, mux2lut8_x + 4. / 3. * mux_w, + grp_lut_y[1] - 7. * right_wire_dist}, + {mux2lut8_x + 4. / 3. * mux_w, grp_lut_y[1] - 7. * right_wire_dist, mux2lut8_x + 4. / 3. * mux_w, + mux2lut8_y + mux_h / 2.}, + {mux2lut8_x + 4. / 3. * mux_w, mux2lut8_y + mux_h / 2., mux2lut8_x + mux_w, mux2lut8_y + mux_h / 2.}}}, + {id_SEL1, + {{cru_x + cru_w, grp_lut_y[1] - 6. * right_wire_dist, mux2lut6_x + mux_w / 2., + grp_lut_y[1] - 6. * right_wire_dist}, + {mux2lut6_x + mux_w / 2., grp_lut_y[1] - 6. * right_wire_dist, mux2lut6_x + mux_w / 2., + mux2lut6_y[0] + mux_h - mux_f / 2.}}}, + {id_OF0, + {{cru_x + cru_w, grp_lut_y[1] - 5. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + grp_lut_y[1] - 5. * right_wire_dist}, + {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[1] - 5. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + mux2lut5_y[0] + mux_h / 2.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[0] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[0] + mux_h / 2.}}}, + {id_I1MUX1, + {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[0] + mux_h / 2., mux2lut6_x, mux2lut5_y[0] + mux_h / 2.}}}, + {id_OF1, + {{cru_x + cru_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + grp_lut_y[1] - 4. * right_wire_dist}, + {mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut6_y[0] + mux_h / 2.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut6_y[0] + mux_h / 2., mux2lut6_x + mux_w, mux2lut6_y[0] + mux_h / 2.}}}, + {id_I1MUX3, + {{mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut7_y + mux_h * 1. / 4.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut7_y + mux_h * 1. / 4., mux2lut7_x, mux2lut7_y + mux_h * 1. / 4.}}}, + {id_OF2, + {{cru_x + cru_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, + grp_lut_y[1] - 3. * right_wire_dist}, + {mux2lut5_x + 5. / 3. * mux_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, + mux2lut5_y[1] + mux_h / 2.}, + {mux2lut5_x + 5. / 3. * mux_w, mux2lut5_y[1] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[1] + mux_h / 2.}}}, + {id_I0MUX1, + {{mux2lut5_x + 5. / 3. * mux_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, + mux2lut6_y[0] + mux_h * 3. / 4.}, + {mux2lut5_x + 5. / 3. * mux_w, mux2lut6_y[0] + mux_h * 3. / 4., mux2lut6_x, + mux2lut6_y[0] + mux_h * 3. / 4.}}}, + {id_F2, + {{cru_x + cru_w, grp_lut_y[1] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[1] - 2. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[2] + lut_h / 2.}, + {lut_x + lut_w, lut_y[2] + lut_h / 2., dff_x, lut_y[2] + lut_h / 2.}}}, + {id_I0MUX2, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 2. * right_wire_dist, mux5i_x, + grp_lut_y[1] - 2. * right_wire_dist}, + {mux5i_x, grp_lut_y[1] - 2. * right_wire_dist, mux5i_x, lut_y[2] + lut_h / 2.}, + {mux5i_x, lut_y[2] + lut_h / 2., mux2lut5_x, lut_y[2] + lut_h / 2.}}}, + {id_Q2, + {{cru_x + cru_w, grp_lut_y[1] - right_wire_dist, dff_f_x, grp_lut_y[1] - right_wire_dist}, + {dff_f_x, grp_lut_y[1] - right_wire_dist, dff_f_x, lut_y[2] + lut_h / 2.}, + {dff_f_x, lut_y[2] + lut_h / 2., dff_x + dff_w, lut_y[2] + lut_h / 2.}}}, + // wires between LUT3 and LUT4 + {id_Q3, + {{cru_x + cru_w, grp_lut_y[2] - 9. * right_wire_dist, dff_f_x, grp_lut_y[2] - 9. * right_wire_dist}, + {dff_f_x, grp_lut_y[2] - 9. * right_wire_dist, dff_f_x, lut_y[3] + lut_h / 2.}, + {dff_f_x, lut_y[3] + lut_h / 2., dff_x + dff_w, lut_y[3] + lut_h / 2.}}}, + {id_F3, + {{cru_x + cru_w, grp_lut_y[2] - 8. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[2] - 8. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 8. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[3] + lut_h / 2.}, + {lut_x + lut_w, lut_y[3] + lut_h / 2., dff_x, lut_y[3] + lut_h / 2.}}}, + {id_I1MUX2, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 8. * right_wire_dist, mux5i_x, + grp_lut_y[2] - 8. * right_wire_dist}, + {mux5i_x, grp_lut_y[2] - 8. * right_wire_dist, mux5i_x, lut_y[3] + lut_h / 2.}, + {mux5i_x, lut_y[3] + lut_h / 2., mux2lut5_x, lut_y[3] + lut_h / 2.}}}, + {id_SEL2, + {{cru_x + cru_w, grp_lut_y[2] - 7. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[2] - 7. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[2] - 7. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[1] + mux_h - mux_f / 2.}}}, + {id_SEL3, + {{cru_x + cru_w, grp_lut_y[2] - 6. * right_wire_dist, mux2lut7_x + mux_w / 2., + grp_lut_y[2] - 6. * right_wire_dist}, + {mux2lut7_x + mux_w / 2., grp_lut_y[2] - 6. * right_wire_dist, mux2lut7_x + mux_w / 2., + mux2lut7_y + mux_h - mux_f / 2.}}}, + {id_SEL7, + {{cru_x + cru_w, grp_lut_y[2] - 5. * right_wire_dist, mux2lut8_x + mux_w / 2., + grp_lut_y[2] - 5. * right_wire_dist}, + {mux2lut8_x + mux_w / 2., grp_lut_y[2] - 5. * right_wire_dist, mux2lut8_x + mux_w / 2., + mux2lut8_y + mux_h - mux_f / 2.}}}, + {id_OF5, + {{cru_x + cru_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + grp_lut_y[2] - 4. * right_wire_dist}, + {mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut6_y[1] + mux_h / 2.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut6_y[1] + mux_h / 2., mux2lut6_x + mux_w, mux2lut6_y[1] + mux_h / 2.}}}, + {id_I0MUX3, + {{mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut7_y + mux_h * 3. / 4.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut7_y + mux_h * 3. / 4., mux2lut7_x, mux2lut7_y + mux_h * 3. / 4.}}}, + {id_OF4, + {{cru_x + cru_w, grp_lut_y[2] - 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + grp_lut_y[2] - 3. * right_wire_dist}, + {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[2] - 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + mux2lut5_y[2] + mux_h / 2.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[2] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[2] + mux_h / 2.}}}, + {id_I1MUX5, + {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[2] + mux_h / 2., mux2lut6_x, mux2lut5_y[2] + mux_h / 2.}}}, + {id_F4, + {{cru_x + cru_w, grp_lut_y[2] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[2] - 2. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[4] + lut_h / 2.}, + {lut_x + lut_w, lut_y[4] + lut_h / 2., dff_x, lut_y[4] + lut_h / 2.}}}, + {id_I0MUX4, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 2. * right_wire_dist, mux5i_x, + grp_lut_y[2] - 2. * right_wire_dist}, + {mux5i_x, grp_lut_y[2] - 2. * right_wire_dist, mux5i_x, lut_y[4] + lut_h / 2.}, + {mux5i_x, lut_y[4] + lut_h / 2., mux2lut5_x, lut_y[4] + lut_h / 2.}}}, + {id_Q4, + {{cru_x + cru_w, grp_lut_y[2] - right_wire_dist, dff_f_x, grp_lut_y[2] - right_wire_dist}, + {dff_f_x, grp_lut_y[2] - right_wire_dist, dff_f_x, lut_y[4] + lut_h / 2.}, + {dff_f_x, lut_y[4] + lut_h / 2., dff_x + dff_w, lut_y[4] + lut_h / 2.}}}, + // wires between LUT5 and LUT6 + {id_Q5, + {{cru_x + cru_w, grp_lut_y[3] - 6. * right_wire_dist, dff_f_x, grp_lut_y[3] - 6. * right_wire_dist}, + {dff_f_x, grp_lut_y[3] - 6. * right_wire_dist, dff_f_x, lut_y[5] + lut_h / 2.}, + {dff_f_x, lut_y[5] + lut_h / 2., dff_x + dff_w, lut_y[5] + lut_h / 2.}}}, + {id_F5, + {{cru_x + cru_w, grp_lut_y[3] - 5. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[3] - 5. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - 5. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[5] + lut_h / 2.}, + {lut_x + lut_w, lut_y[5] + lut_h / 2., dff_x, lut_y[5] + lut_h / 2.}}}, + {id_I1MUX4, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - 5. * right_wire_dist, mux5i_x, + grp_lut_y[3] - 5. * right_wire_dist}, + {mux5i_x, grp_lut_y[3] - 5. * right_wire_dist, mux5i_x, lut_y[5] + lut_h / 2.}, + {mux5i_x, lut_y[5] + lut_h / 2., mux2lut5_x, lut_y[5] + lut_h / 2.}}}, + {id_SEL4, + {{cru_x + cru_w, grp_lut_y[3] - 4. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[3] - 4. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[3] - 4. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[2] + mux_h - mux_f / 2.}}}, + {id_SEL5, + {{cru_x + cru_w, grp_lut_y[3] - 2. * right_wire_dist, mux2lut6_x + mux_w / 2., + grp_lut_y[3] - 2. * right_wire_dist}, + {mux2lut6_x + mux_w / 2., grp_lut_y[3] - 2. * right_wire_dist, mux2lut6_x + mux_w / 2., + mux2lut6_y[1] + mux_h - mux_f / 2.}}}, + {id_F6, + {{cru_x + cru_w, grp_lut_y[3] - right_wire_dist, (lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[6] + lut_h / 2.}, + {lut_x + lut_w, lut_y[6] + lut_h / 2., (lut_x + lut_w + dff_x) / 2., lut_y[6] + lut_h / 2.}}}, + {id_I0MUX6, {{(lut_x + lut_w + dff_x) / 2., lut_y[6] + lut_h / 2., mux2lut5_x, lut_y[6] + lut_h / 2.}}}, + // wires above LUT7 + {id_F7, + {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[3] + grp_lut_h + right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] + grp_lut_h + right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[7] + lut_h / 2.}, + {lut_x + lut_w, lut_y[7] + lut_h / 2., (lut_x + lut_w + dff_x) / 2., lut_y[7] + lut_h / 2.}}}, + {id_I1MUX6, {{(lut_x + lut_w + dff_x) / 2., lut_y[7] + lut_h / 2., mux2lut5_x, lut_y[7] + lut_h / 2.}}}, + {id_SEL6, + {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[3] + mux_h - mux_f / 2.}}}, + {id_OF6, + {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist}, + {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + mux2lut5_y[3] + mux_h / 2.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[3] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[3] + mux_h / 2.}}}, + {id_I0MUX5, + {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[3] + mux_h / 2., mux2lut5_x + 4. / 3. * mux_w, + mux2lut6_y[1] + mux_h * 3. / 4.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut6_y[1] + mux_h * 3. / 4., mux2lut6_x, + mux2lut6_y[1] + mux_h * 3. / 4.}}}, +}; + +const dict>> globalSimpleWires = { + {id_I0MUX7, + {{mux2lut8_x, mux2lut8_y + mux_h / 4., mux2lut8_x - 1. / 3. * mux_w, mux2lut8_y + mux_h / 4.}, + {mux2lut8_x - 1. / 3. * mux_w, mux2lut8_y + mux_h / 4., mux2lut8_x - 1. / 3. * mux_w, + cru_y - 2. * right_wire_dist}, + {mux2lut8_x - 1. / 3. * mux_w, cru_y - 2. * right_wire_dist, 1. + mux2lut7_x + 4. / 3. * mux_w, + cru_y - 2. * right_wire_dist}, + {1. + mux2lut7_x + 4. / 3. * mux_w, cru_y - 2. * right_wire_dist, 1. + mux2lut7_x + 4. / 3. * mux_w, + grp_lut_y[0] - 3. * right_wire_dist}}}, +}; + +dict>> const globalWires = { +#define PIP_Y(pip_id) (pipPoint.at(pip_id).second) +#define WIRE_X(offset) (cru_x - ((float)offset) * sn_dist) + // 1 hop + {id_S10, + {{WIRE_X(0), PIP_Y(id_S100), WIRE_X(1), PIP_Y(id_S100)}, + {WIRE_X(1), PIP_Y(id_S100), WIRE_X(1), -1. + PIP_Y(id_S101)}, + {WIRE_X(1), -1. + PIP_Y(id_S101), WIRE_X(0), -1. + PIP_Y(id_S101)}}}, + {id_N10, + {{WIRE_X(0), PIP_Y(id_N100), WIRE_X(2), PIP_Y(id_N100)}, + {WIRE_X(2), PIP_Y(id_N100), WIRE_X(2), 1. + PIP_Y(id_N101)}, + {WIRE_X(2), 1. + PIP_Y(id_N101), WIRE_X(0), 1. + PIP_Y(id_N101)}}}, + {id_S10_loop0, + {{WIRE_X(0), PIP_Y(id_S100), WIRE_X(1), PIP_Y(id_S100)}, + {WIRE_X(1), PIP_Y(id_S100), WIRE_X(1), -1. * wrap_len}, + {WIRE_X(1), -1. * wrap_len, WIRE_X(2), -1. * wrap_len}, + {WIRE_X(2), -1. * wrap_len, WIRE_X(2), PIP_Y(id_N101)}, + {WIRE_X(2), PIP_Y(id_N101), WIRE_X(0), PIP_Y(id_N101)}}}, + {id_N10_loop0, + {{WIRE_X(0), PIP_Y(id_N100), WIRE_X(2), PIP_Y(id_N100)}, + {WIRE_X(2), PIP_Y(id_N100), WIRE_X(2), 1. + 1. * wrap_len}, + {WIRE_X(2), 1. + 1. * wrap_len, WIRE_X(1), 1. + 1. * wrap_len}, + {WIRE_X(1), 1. + 1. * wrap_len, WIRE_X(1), PIP_Y(id_S101)}, + {WIRE_X(1), PIP_Y(id_S101), WIRE_X(0), PIP_Y(id_S101)}}}, + {id_S13, + {{WIRE_X(0), PIP_Y(id_S130), WIRE_X(3), PIP_Y(id_S130)}, + {WIRE_X(3), PIP_Y(id_S130), WIRE_X(3), -1. + PIP_Y(id_S131)}, + {WIRE_X(3), -1. + PIP_Y(id_S131), WIRE_X(0), -1. + PIP_Y(id_S131)}}}, + {id_N13, + {{WIRE_X(0), PIP_Y(id_N130), WIRE_X(4), PIP_Y(id_N130)}, + {WIRE_X(4), PIP_Y(id_N130), WIRE_X(4), 1. + PIP_Y(id_N131)}, + {WIRE_X(4), 1. + PIP_Y(id_N131), WIRE_X(0), 1. + PIP_Y(id_N131)}}}, + {id_S13_loop0, + {{WIRE_X(0), PIP_Y(id_S130), WIRE_X(3), PIP_Y(id_S130)}, + {WIRE_X(3), PIP_Y(id_S130), WIRE_X(3), -1. * wrap_len}, + {WIRE_X(3), -1. * wrap_len, WIRE_X(4), -1. * wrap_len}, + {WIRE_X(4), -1. * wrap_len, WIRE_X(4), PIP_Y(id_N131)}, + {WIRE_X(4), PIP_Y(id_N131), WIRE_X(0), PIP_Y(id_N131)}}}, + {id_N13_loop0, + {{WIRE_X(0), PIP_Y(id_N130), WIRE_X(4), PIP_Y(id_N130)}, + {WIRE_X(4), PIP_Y(id_N130), WIRE_X(4), 1. + 1. * wrap_len}, + {WIRE_X(4), 1. + 1. * wrap_len, WIRE_X(3), 1. + 1. * wrap_len}, + {WIRE_X(3), 1. + 1. * wrap_len, WIRE_X(3), PIP_Y(id_S131)}, + {WIRE_X(3), PIP_Y(id_S131), WIRE_X(0), PIP_Y(id_S131)}}}, + // 1 hop SN + {id_SN10, + {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, + {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + PIP_Y(id_N111)}, + {WIRE_X(6), 1. + PIP_Y(id_N111), WIRE_X(0), 1. + PIP_Y(id_N111)}, + {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. + PIP_Y(id_S111)}, + {WIRE_X(5), -1. + PIP_Y(id_S111), WIRE_X(0), -1. + PIP_Y(id_S111)}}}, + {id_SN10_loop_n, + {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, + {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + 1. * wrap_len}, + {WIRE_X(6), 1. + 1. * wrap_len, WIRE_X(5), 1. + 1. * wrap_len}, + {WIRE_X(5), 1. + 1. * wrap_len, WIRE_X(5), PIP_Y(id_SN10)}, + {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. + PIP_Y(id_S111)}, + {WIRE_X(5), -1. + PIP_Y(id_S111), WIRE_X(0), -1. + PIP_Y(id_S111)}}}, + {id_SN10_loop_s, + {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, + {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + PIP_Y(id_N111)}, + {WIRE_X(6), 1. + PIP_Y(id_N111), WIRE_X(0), 1. + PIP_Y(id_N111)}, + {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. * wrap_len}, + {WIRE_X(5), -1. * wrap_len, WIRE_X(6), -1. * wrap_len}, + {WIRE_X(6), -1. * wrap_len, WIRE_X(6), PIP_Y(id_N111)}, + {WIRE_X(6), PIP_Y(id_N111), WIRE_X(0), PIP_Y(id_N111)}}}, + {id_SN20, + {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, + {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + PIP_Y(id_N121)}, + {WIRE_X(8), 1. + PIP_Y(id_N121), WIRE_X(0), 1. + PIP_Y(id_N121)}, + {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. + PIP_Y(id_S121)}, + {WIRE_X(7), -1. + PIP_Y(id_S121), WIRE_X(0), -1. + PIP_Y(id_S121)}}}, + {id_SN20_loop_n, + {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, + {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + 1. * wrap_len}, + {WIRE_X(8), 1. + 1. * wrap_len, WIRE_X(7), 1. + 1. * wrap_len}, + {WIRE_X(7), 1. + 1. * wrap_len, WIRE_X(7), PIP_Y(id_SN10)}, + {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. + PIP_Y(id_S121)}, + {WIRE_X(7), -1. + PIP_Y(id_S121), WIRE_X(0), -1. + PIP_Y(id_S121)}}}, + {id_SN20_loop_s, + {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, + {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + PIP_Y(id_N121)}, + {WIRE_X(8), 1. + PIP_Y(id_N121), WIRE_X(0), 1. + PIP_Y(id_N121)}, + {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. * wrap_len}, + {WIRE_X(7), -1. * wrap_len, WIRE_X(8), -1. * wrap_len}, + {WIRE_X(8), -1. * wrap_len, WIRE_X(8), PIP_Y(id_N121)}, + {WIRE_X(8), PIP_Y(id_N121), WIRE_X(0), PIP_Y(id_N121)}}}, + // 2 hop + {id_S20, + {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, + {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. + PIP_Y(id_S201)}, + {WIRE_X(11), -1. + PIP_Y(id_S201), WIRE_X(0), -1. + PIP_Y(id_S201)}, + {WIRE_X(9), -1. + PIP_Y(id_S201), WIRE_X(9), -2. + PIP_Y(id_S202)}, + {WIRE_X(9), -2. + PIP_Y(id_S202), WIRE_X(0), -2. + PIP_Y(id_S202)}}}, + {id_N20, + {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, + {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + PIP_Y(id_N201)}, + {WIRE_X(12), 1. + PIP_Y(id_N201), WIRE_X(0), 1. + PIP_Y(id_N201)}, + {WIRE_X(10), 1. + PIP_Y(id_N201), WIRE_X(10), 2. + PIP_Y(id_N202)}, + {WIRE_X(10), 2. + PIP_Y(id_N202), WIRE_X(0), 2. + PIP_Y(id_N202)}}}, + {id_S20_loop0, + {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, + {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. * wrap_len}, + {WIRE_X(11), -1. * wrap_len, WIRE_X(12), -1. * wrap_len}, + {WIRE_X(12), -1. * wrap_len, WIRE_X(12), PIP_Y(id_N201)}, + {WIRE_X(12), PIP_Y(id_N201), WIRE_X(0), PIP_Y(id_N201)}, + {WIRE_X(10), PIP_Y(id_N201), WIRE_X(10), 1. + PIP_Y(id_N202)}, + {WIRE_X(10), 1. + PIP_Y(id_N202), WIRE_X(0), 1. + PIP_Y(id_N202)}}}, + {id_N20_loop0, + {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, + {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + 1. * wrap_len}, + {WIRE_X(12), 1. + 1. * wrap_len, WIRE_X(11), 1. + 1. * wrap_len}, + {WIRE_X(11), 1. + 1. * wrap_len, WIRE_X(11), PIP_Y(id_S201)}, + {WIRE_X(11), PIP_Y(id_S201), WIRE_X(0), PIP_Y(id_S201)}, + {WIRE_X(9), PIP_Y(id_S201), WIRE_X(9), -1. + PIP_Y(id_S202)}, + {WIRE_X(9), -1. + PIP_Y(id_S202), WIRE_X(0), -1. + PIP_Y(id_S202)}}}, + {id_S20_loop1, + {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, + {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. + PIP_Y(id_S201)}, + {WIRE_X(11), -1. + PIP_Y(id_S201), WIRE_X(0), -1. + PIP_Y(id_S201)}, + {WIRE_X(9), -1. + PIP_Y(id_S201), WIRE_X(9), -1. + -1. * wrap_len}, + {WIRE_X(9), -1. + -1. * wrap_len, WIRE_X(10), -1. + -1. * wrap_len}, + {WIRE_X(10), -1. + -1. * wrap_len, WIRE_X(10), -1. + PIP_Y(id_N202)}, + {WIRE_X(10), -1. + PIP_Y(id_N202), WIRE_X(0), -1. + PIP_Y(id_N202)}}}, + {id_N20_loop1, + {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, + {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + PIP_Y(id_N201)}, + {WIRE_X(12), 1. + PIP_Y(id_N201), WIRE_X(0), 1. + PIP_Y(id_N201)}, + {WIRE_X(10), 1. + PIP_Y(id_N201), WIRE_X(10), 2. + 1. * wrap_len}, + {WIRE_X(10), 2. + 1. * wrap_len, WIRE_X(9), 2. + 1. * wrap_len}, + {WIRE_X(9), 2. + 1. * wrap_len, WIRE_X(9), 1. + PIP_Y(id_S202)}, + {WIRE_X(9), 1. + PIP_Y(id_S202), WIRE_X(0), 1. + PIP_Y(id_S202)}}}, + {id_S21, + {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, + {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. + PIP_Y(id_S211)}, + {WIRE_X(15), -1. + PIP_Y(id_S211), WIRE_X(0), -1. + PIP_Y(id_S211)}, + {WIRE_X(13), -1. + PIP_Y(id_S211), WIRE_X(13), -2. + PIP_Y(id_S212)}, + {WIRE_X(13), -2. + PIP_Y(id_S212), WIRE_X(0), -2. + PIP_Y(id_S212)}}}, + {id_N21, + {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, + {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + PIP_Y(id_N211)}, + {WIRE_X(16), 1. + PIP_Y(id_N211), WIRE_X(0), 1. + PIP_Y(id_N211)}, + {WIRE_X(14), 1. + PIP_Y(id_N211), WIRE_X(14), 2. + PIP_Y(id_N212)}, + {WIRE_X(14), 2. + PIP_Y(id_N212), WIRE_X(0), 2. + PIP_Y(id_N212)}}}, + {id_S21_loop0, + {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, + {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. * wrap_len}, + {WIRE_X(15), -1. * wrap_len, WIRE_X(16), -1. * wrap_len}, + {WIRE_X(16), -1. * wrap_len, WIRE_X(16), PIP_Y(id_N211)}, + {WIRE_X(16), PIP_Y(id_N211), WIRE_X(0), PIP_Y(id_N211)}, + {WIRE_X(14), PIP_Y(id_N211), WIRE_X(14), 1. + PIP_Y(id_N212)}, + {WIRE_X(14), 1. + PIP_Y(id_N212), WIRE_X(0), 1. + PIP_Y(id_N212)}}}, + {id_N21_loop0, + {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, + {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + 1. * wrap_len}, + {WIRE_X(16), 1. + 1. * wrap_len, WIRE_X(15), 1. + 1. * wrap_len}, + {WIRE_X(15), 1. + 1. * wrap_len, WIRE_X(15), PIP_Y(id_S211)}, + {WIRE_X(15), PIP_Y(id_S211), WIRE_X(0), PIP_Y(id_S211)}, + {WIRE_X(13), PIP_Y(id_S211), WIRE_X(13), -1. + PIP_Y(id_S212)}, + {WIRE_X(13), -1. + PIP_Y(id_S212), WIRE_X(0), -1. + PIP_Y(id_S212)}}}, + {id_S21_loop1, + {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, + {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. + PIP_Y(id_S211)}, + {WIRE_X(15), -1. + PIP_Y(id_S211), WIRE_X(0), -1. + PIP_Y(id_S211)}, + {WIRE_X(13), -1. + PIP_Y(id_S211), WIRE_X(13), -1. + -1. * wrap_len}, + {WIRE_X(13), -1. + -1. * wrap_len, WIRE_X(14), -1. + -1. * wrap_len}, + {WIRE_X(14), -1. + -1. * wrap_len, WIRE_X(14), -1. + PIP_Y(id_N212)}, + {WIRE_X(14), -1. + PIP_Y(id_N212), WIRE_X(0), -1. + PIP_Y(id_N212)}}}, + {id_N21_loop1, + {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, + {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + PIP_Y(id_N211)}, + {WIRE_X(16), 1. + PIP_Y(id_N211), WIRE_X(0), 1. + PIP_Y(id_N211)}, + {WIRE_X(14), 1. + PIP_Y(id_N211), WIRE_X(14), 2. + 1. * wrap_len}, + {WIRE_X(14), 2. + 1. * wrap_len, WIRE_X(13), 2. + 1. * wrap_len}, + {WIRE_X(13), 2. + 1. * wrap_len, WIRE_X(13), 1. + PIP_Y(id_S212)}, + {WIRE_X(13), 1. + PIP_Y(id_S212), WIRE_X(0), 1. + PIP_Y(id_S212)}}}, + {id_S22, + {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, + {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. + PIP_Y(id_S221)}, + {WIRE_X(19), -1. + PIP_Y(id_S221), WIRE_X(0), -1. + PIP_Y(id_S221)}, + {WIRE_X(17), -1. + PIP_Y(id_S221), WIRE_X(17), -2. + PIP_Y(id_S222)}, + {WIRE_X(17), -2. + PIP_Y(id_S222), WIRE_X(0), -2. + PIP_Y(id_S222)}}}, + {id_N22, + {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, + {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + PIP_Y(id_N221)}, + {WIRE_X(20), 1. + PIP_Y(id_N221), WIRE_X(0), 1. + PIP_Y(id_N221)}, + {WIRE_X(18), 1. + PIP_Y(id_N221), WIRE_X(18), 2. + PIP_Y(id_N222)}, + {WIRE_X(18), 2. + PIP_Y(id_N222), WIRE_X(0), 2. + PIP_Y(id_N222)}}}, + {id_S22_loop0, + {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, + {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. * wrap_len}, + {WIRE_X(19), -1. * wrap_len, WIRE_X(20), -1. * wrap_len}, + {WIRE_X(20), -1. * wrap_len, WIRE_X(20), PIP_Y(id_N221)}, + {WIRE_X(20), PIP_Y(id_N221), WIRE_X(0), PIP_Y(id_N221)}, + {WIRE_X(18), PIP_Y(id_N221), WIRE_X(18), 1. + PIP_Y(id_N222)}, + {WIRE_X(18), 1. + PIP_Y(id_N222), WIRE_X(0), 1. + PIP_Y(id_N222)}}}, + {id_N22_loop0, + {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, + {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + 1. * wrap_len}, + {WIRE_X(20), 1. + 1. * wrap_len, WIRE_X(19), 1. + 1. * wrap_len}, + {WIRE_X(19), 1. + 1. * wrap_len, WIRE_X(19), PIP_Y(id_S221)}, + {WIRE_X(19), PIP_Y(id_S221), WIRE_X(0), PIP_Y(id_S221)}, + {WIRE_X(17), PIP_Y(id_S221), WIRE_X(17), -1. + PIP_Y(id_S222)}, + {WIRE_X(17), -1. + PIP_Y(id_S222), WIRE_X(0), -1. + PIP_Y(id_S222)}}}, + {id_S22_loop1, + {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, + {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. + PIP_Y(id_S221)}, + {WIRE_X(19), -1. + PIP_Y(id_S221), WIRE_X(0), -1. + PIP_Y(id_S221)}, + {WIRE_X(17), -1. + PIP_Y(id_S221), WIRE_X(17), -1. + -1. * wrap_len}, + {WIRE_X(17), -1. + -1. * wrap_len, WIRE_X(18), -1. + -1. * wrap_len}, + {WIRE_X(18), -1. + -1. * wrap_len, WIRE_X(18), -1. + PIP_Y(id_N222)}, + {WIRE_X(18), -1. + PIP_Y(id_N222), WIRE_X(0), -1. + PIP_Y(id_N222)}}}, + {id_N22_loop1, + {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, + {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + PIP_Y(id_N221)}, + {WIRE_X(20), 1. + PIP_Y(id_N221), WIRE_X(0), 1. + PIP_Y(id_N221)}, + {WIRE_X(18), 1. + PIP_Y(id_N221), WIRE_X(18), 2. + 1. * wrap_len}, + {WIRE_X(18), 2. + 1. * wrap_len, WIRE_X(17), 2. + 1. * wrap_len}, + {WIRE_X(17), 2. + 1. * wrap_len, WIRE_X(17), 1. + PIP_Y(id_S222)}, + {WIRE_X(17), 1. + PIP_Y(id_S222), WIRE_X(0), 1. + PIP_Y(id_S222)}}}, + {id_S23, + {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, + {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. + PIP_Y(id_S231)}, + {WIRE_X(23), -1. + PIP_Y(id_S231), WIRE_X(0), -1. + PIP_Y(id_S231)}, + {WIRE_X(21), -1. + PIP_Y(id_S231), WIRE_X(21), -2. + PIP_Y(id_S232)}, + {WIRE_X(21), -2. + PIP_Y(id_S232), WIRE_X(0), -2. + PIP_Y(id_S232)}}}, + {id_N23, + {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, + {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + PIP_Y(id_N231)}, + {WIRE_X(24), 1. + PIP_Y(id_N231), WIRE_X(0), 1. + PIP_Y(id_N231)}, + {WIRE_X(22), 1. + PIP_Y(id_N231), WIRE_X(22), 2. + PIP_Y(id_N232)}, + {WIRE_X(22), 2. + PIP_Y(id_N232), WIRE_X(0), 2. + PIP_Y(id_N232)}}}, + {id_S23_loop0, + {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, + {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. * wrap_len}, + {WIRE_X(23), -1. * wrap_len, WIRE_X(24), -1. * wrap_len}, + {WIRE_X(24), -1. * wrap_len, WIRE_X(24), PIP_Y(id_N231)}, + {WIRE_X(24), PIP_Y(id_N231), WIRE_X(0), PIP_Y(id_N231)}, + {WIRE_X(22), PIP_Y(id_N231), WIRE_X(22), 1. + PIP_Y(id_N232)}, + {WIRE_X(22), 1. + PIP_Y(id_N232), WIRE_X(0), 1. + PIP_Y(id_N232)}}}, + {id_N23_loop0, + {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, + {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + 1. * wrap_len}, + {WIRE_X(24), 1. + 1. * wrap_len, WIRE_X(23), 1. + 1. * wrap_len}, + {WIRE_X(23), 1. + 1. * wrap_len, WIRE_X(23), PIP_Y(id_S231)}, + {WIRE_X(23), PIP_Y(id_S231), WIRE_X(0), PIP_Y(id_S231)}, + {WIRE_X(21), PIP_Y(id_S231), WIRE_X(21), -1. + PIP_Y(id_S232)}, + {WIRE_X(21), -1. + PIP_Y(id_S232), WIRE_X(0), -1. + PIP_Y(id_S232)}}}, + {id_S23_loop1, + {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, + {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. + PIP_Y(id_S231)}, + {WIRE_X(23), -1. + PIP_Y(id_S231), WIRE_X(0), -1. + PIP_Y(id_S231)}, + {WIRE_X(21), -1. + PIP_Y(id_S231), WIRE_X(21), -1. + -1. * wrap_len}, + {WIRE_X(21), -1. + -1. * wrap_len, WIRE_X(22), -1. + -1. * wrap_len}, + {WIRE_X(22), -1. + -1. * wrap_len, WIRE_X(22), -1. + PIP_Y(id_N232)}, + {WIRE_X(22), -1. + PIP_Y(id_N232), WIRE_X(0), -1. + PIP_Y(id_N232)}}}, + {id_N23_loop1, + {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, + {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + PIP_Y(id_N231)}, + {WIRE_X(24), 1. + PIP_Y(id_N231), WIRE_X(0), 1. + PIP_Y(id_N231)}, + {WIRE_X(22), 1. + PIP_Y(id_N231), WIRE_X(22), 2. + 1. * wrap_len}, + {WIRE_X(22), 2. + 1. * wrap_len, WIRE_X(21), 2. + 1. * wrap_len}, + {WIRE_X(21), 2. + 1. * wrap_len, WIRE_X(21), 1. + PIP_Y(id_S232)}, + {WIRE_X(21), 1. + PIP_Y(id_S232), WIRE_X(0), 1. + PIP_Y(id_S232)}}}, + {id_S24, + {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, + {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. + PIP_Y(id_S241)}, + {WIRE_X(27), -1. + PIP_Y(id_S241), WIRE_X(0), -1. + PIP_Y(id_S241)}, + {WIRE_X(25), -1. + PIP_Y(id_S241), WIRE_X(25), -2. + PIP_Y(id_S242)}, + {WIRE_X(25), -2. + PIP_Y(id_S242), WIRE_X(0), -2. + PIP_Y(id_S242)}}}, + {id_N24, + {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, + {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + PIP_Y(id_N241)}, + {WIRE_X(28), 1. + PIP_Y(id_N241), WIRE_X(0), 1. + PIP_Y(id_N241)}, + {WIRE_X(26), 1. + PIP_Y(id_N241), WIRE_X(26), 2. + PIP_Y(id_N242)}, + {WIRE_X(26), 2. + PIP_Y(id_N242), WIRE_X(0), 2. + PIP_Y(id_N242)}}}, + {id_S24_loop0, + {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, + {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. * wrap_len}, + {WIRE_X(27), -1. * wrap_len, WIRE_X(28), -1. * wrap_len}, + {WIRE_X(28), -1. * wrap_len, WIRE_X(28), PIP_Y(id_N241)}, + {WIRE_X(28), PIP_Y(id_N241), WIRE_X(0), PIP_Y(id_N241)}, + {WIRE_X(26), PIP_Y(id_N241), WIRE_X(26), 1. + PIP_Y(id_N242)}, + {WIRE_X(26), 1. + PIP_Y(id_N242), WIRE_X(0), 1. + PIP_Y(id_N242)}}}, + {id_N24_loop0, + {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, + {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + 1. * wrap_len}, + {WIRE_X(28), 1. + 1. * wrap_len, WIRE_X(27), 1. + 1. * wrap_len}, + {WIRE_X(27), 1. + 1. * wrap_len, WIRE_X(27), PIP_Y(id_S241)}, + {WIRE_X(27), PIP_Y(id_S241), WIRE_X(0), PIP_Y(id_S241)}, + {WIRE_X(25), PIP_Y(id_S241), WIRE_X(25), -1. + PIP_Y(id_S242)}, + {WIRE_X(25), -1. + PIP_Y(id_S242), WIRE_X(0), -1. + PIP_Y(id_S242)}}}, + {id_S24_loop1, + {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, + {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. + PIP_Y(id_S241)}, + {WIRE_X(27), -1. + PIP_Y(id_S241), WIRE_X(0), -1. + PIP_Y(id_S241)}, + {WIRE_X(25), -1. + PIP_Y(id_S241), WIRE_X(25), -1. + -1. * wrap_len}, + {WIRE_X(25), -1. + -1. * wrap_len, WIRE_X(26), -1. + -1. * wrap_len}, + {WIRE_X(26), -1. + -1. * wrap_len, WIRE_X(26), -1. + PIP_Y(id_N242)}, + {WIRE_X(26), -1. + PIP_Y(id_N242), WIRE_X(0), -1. + PIP_Y(id_N242)}}}, + {id_N24_loop1, + {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, + {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + PIP_Y(id_N241)}, + {WIRE_X(28), 1. + PIP_Y(id_N241), WIRE_X(0), 1. + PIP_Y(id_N241)}, + {WIRE_X(26), 1. + PIP_Y(id_N241), WIRE_X(26), 2. + 1. * wrap_len}, + {WIRE_X(26), 2. + 1. * wrap_len, WIRE_X(25), 2. + 1. * wrap_len}, + {WIRE_X(25), 2. + 1. * wrap_len, WIRE_X(25), 1. + PIP_Y(id_S242)}, + {WIRE_X(25), 1. + PIP_Y(id_S242), WIRE_X(0), 1. + PIP_Y(id_S242)}}}, + {id_S25, + {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, + {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. + PIP_Y(id_S251)}, + {WIRE_X(31), -1. + PIP_Y(id_S251), WIRE_X(0), -1. + PIP_Y(id_S251)}, + {WIRE_X(29), -1. + PIP_Y(id_S251), WIRE_X(29), -2. + PIP_Y(id_S252)}, + {WIRE_X(29), -2. + PIP_Y(id_S252), WIRE_X(0), -2. + PIP_Y(id_S252)}}}, + {id_N25, + {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, + {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + PIP_Y(id_N251)}, + {WIRE_X(32), 1. + PIP_Y(id_N251), WIRE_X(0), 1. + PIP_Y(id_N251)}, + {WIRE_X(30), 1. + PIP_Y(id_N251), WIRE_X(30), 2. + PIP_Y(id_N252)}, + {WIRE_X(30), 2. + PIP_Y(id_N252), WIRE_X(0), 2. + PIP_Y(id_N252)}}}, + {id_S25_loop0, + {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, + {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. * wrap_len}, + {WIRE_X(31), -1. * wrap_len, WIRE_X(32), -1. * wrap_len}, + {WIRE_X(32), -1. * wrap_len, WIRE_X(32), PIP_Y(id_N251)}, + {WIRE_X(32), PIP_Y(id_N251), WIRE_X(0), PIP_Y(id_N251)}, + {WIRE_X(30), PIP_Y(id_N251), WIRE_X(30), 1. + PIP_Y(id_N252)}, + {WIRE_X(30), 1. + PIP_Y(id_N252), WIRE_X(0), 1. + PIP_Y(id_N252)}}}, + {id_N25_loop0, + {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, + {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + 1. * wrap_len}, + {WIRE_X(32), 1. + 1. * wrap_len, WIRE_X(31), 1. + 1. * wrap_len}, + {WIRE_X(31), 1. + 1. * wrap_len, WIRE_X(31), PIP_Y(id_S251)}, + {WIRE_X(31), PIP_Y(id_S251), WIRE_X(0), PIP_Y(id_S251)}, + {WIRE_X(29), PIP_Y(id_S251), WIRE_X(29), -1. + PIP_Y(id_S252)}, + {WIRE_X(29), -1. + PIP_Y(id_S252), WIRE_X(0), -1. + PIP_Y(id_S252)}}}, + {id_S25_loop1, + {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, + {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. + PIP_Y(id_S251)}, + {WIRE_X(31), -1. + PIP_Y(id_S251), WIRE_X(0), -1. + PIP_Y(id_S251)}, + {WIRE_X(29), -1. + PIP_Y(id_S251), WIRE_X(29), -1. + -1. * wrap_len}, + {WIRE_X(29), -1. + -1. * wrap_len, WIRE_X(30), -1. + -1. * wrap_len}, + {WIRE_X(30), -1. + -1. * wrap_len, WIRE_X(30), -1. + PIP_Y(id_N252)}, + {WIRE_X(30), -1. + PIP_Y(id_N252), WIRE_X(0), -1. + PIP_Y(id_N252)}}}, + {id_N25_loop1, + {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, + {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + PIP_Y(id_N251)}, + {WIRE_X(32), 1. + PIP_Y(id_N251), WIRE_X(0), 1. + PIP_Y(id_N251)}, + {WIRE_X(30), 1. + PIP_Y(id_N251), WIRE_X(30), 2. + 1. * wrap_len}, + {WIRE_X(30), 2. + 1. * wrap_len, WIRE_X(29), 2. + 1. * wrap_len}, + {WIRE_X(29), 2. + 1. * wrap_len, WIRE_X(29), 1. + PIP_Y(id_S252)}, + {WIRE_X(29), 1. + PIP_Y(id_S252), WIRE_X(0), 1. + PIP_Y(id_S252)}}}, + {id_S26, + {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, + {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. + PIP_Y(id_S261)}, + {WIRE_X(35), -1. + PIP_Y(id_S261), WIRE_X(0), -1. + PIP_Y(id_S261)}, + {WIRE_X(33), -1. + PIP_Y(id_S261), WIRE_X(33), -2. + PIP_Y(id_S262)}, + {WIRE_X(33), -2. + PIP_Y(id_S262), WIRE_X(0), -2. + PIP_Y(id_S262)}}}, + {id_N26, + {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, + {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + PIP_Y(id_N261)}, + {WIRE_X(36), 1. + PIP_Y(id_N261), WIRE_X(0), 1. + PIP_Y(id_N261)}, + {WIRE_X(34), 1. + PIP_Y(id_N261), WIRE_X(34), 2. + PIP_Y(id_N262)}, + {WIRE_X(34), 2. + PIP_Y(id_N262), WIRE_X(0), 2. + PIP_Y(id_N262)}}}, + {id_S26_loop0, + {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, + {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. * wrap_len}, + {WIRE_X(35), -1. * wrap_len, WIRE_X(36), -1. * wrap_len}, + {WIRE_X(36), -1. * wrap_len, WIRE_X(36), PIP_Y(id_N261)}, + {WIRE_X(36), PIP_Y(id_N261), WIRE_X(0), PIP_Y(id_N261)}, + {WIRE_X(34), PIP_Y(id_N261), WIRE_X(34), 1. + PIP_Y(id_N262)}, + {WIRE_X(34), 1. + PIP_Y(id_N262), WIRE_X(0), 1. + PIP_Y(id_N262)}}}, + {id_N26_loop0, + {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, + {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + 1. * wrap_len}, + {WIRE_X(36), 1. + 1. * wrap_len, WIRE_X(35), 1. + 1. * wrap_len}, + {WIRE_X(35), 1. + 1. * wrap_len, WIRE_X(35), PIP_Y(id_S261)}, + {WIRE_X(35), PIP_Y(id_S261), WIRE_X(0), PIP_Y(id_S261)}, + {WIRE_X(33), PIP_Y(id_S261), WIRE_X(33), -1. + PIP_Y(id_S262)}, + {WIRE_X(33), -1. + PIP_Y(id_S262), WIRE_X(0), -1. + PIP_Y(id_S262)}}}, + {id_S26_loop1, + {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, + {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. + PIP_Y(id_S261)}, + {WIRE_X(35), -1. + PIP_Y(id_S261), WIRE_X(0), -1. + PIP_Y(id_S261)}, + {WIRE_X(33), -1. + PIP_Y(id_S261), WIRE_X(33), -1. + -1. * wrap_len}, + {WIRE_X(33), -1. + -1. * wrap_len, WIRE_X(34), -1. + -1. * wrap_len}, + {WIRE_X(34), -1. + -1. * wrap_len, WIRE_X(34), -1. + PIP_Y(id_N262)}, + {WIRE_X(34), -1. + PIP_Y(id_N262), WIRE_X(0), -1. + PIP_Y(id_N262)}}}, + {id_N26_loop1, + {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, + {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + PIP_Y(id_N261)}, + {WIRE_X(36), 1. + PIP_Y(id_N261), WIRE_X(0), 1. + PIP_Y(id_N261)}, + {WIRE_X(34), 1. + PIP_Y(id_N261), WIRE_X(34), 2. + 1. * wrap_len}, + {WIRE_X(34), 2. + 1. * wrap_len, WIRE_X(33), 2. + 1. * wrap_len}, + {WIRE_X(33), 2. + 1. * wrap_len, WIRE_X(33), 1. + PIP_Y(id_S262)}, + {WIRE_X(33), 1. + PIP_Y(id_S262), WIRE_X(0), 1. + PIP_Y(id_S262)}}}, + {id_S27, + {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, + {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. + PIP_Y(id_S271)}, + {WIRE_X(39), -1. + PIP_Y(id_S271), WIRE_X(0), -1. + PIP_Y(id_S271)}, + {WIRE_X(37), -1. + PIP_Y(id_S271), WIRE_X(37), -2. + PIP_Y(id_S272)}, + {WIRE_X(37), -2. + PIP_Y(id_S272), WIRE_X(0), -2. + PIP_Y(id_S272)}}}, + {id_N27, + {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, + {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + PIP_Y(id_N271)}, + {WIRE_X(40), 1. + PIP_Y(id_N271), WIRE_X(0), 1. + PIP_Y(id_N271)}, + {WIRE_X(38), 1. + PIP_Y(id_N271), WIRE_X(38), 2. + PIP_Y(id_N272)}, + {WIRE_X(38), 2. + PIP_Y(id_N272), WIRE_X(0), 2. + PIP_Y(id_N272)}}}, + {id_S27_loop0, + {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, + {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. * wrap_len}, + {WIRE_X(39), -1. * wrap_len, WIRE_X(40), -1. * wrap_len}, + {WIRE_X(40), -1. * wrap_len, WIRE_X(40), PIP_Y(id_N271)}, + {WIRE_X(40), PIP_Y(id_N271), WIRE_X(0), PIP_Y(id_N271)}, + {WIRE_X(38), PIP_Y(id_N271), WIRE_X(38), 1. + PIP_Y(id_N272)}, + {WIRE_X(38), 1. + PIP_Y(id_N272), WIRE_X(0), 1. + PIP_Y(id_N272)}}}, + {id_N27_loop0, + {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, + {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + 1. * wrap_len}, + {WIRE_X(40), 1. + 1. * wrap_len, WIRE_X(39), 1. + 1. * wrap_len}, + {WIRE_X(39), 1. + 1. * wrap_len, WIRE_X(39), PIP_Y(id_S271)}, + {WIRE_X(39), PIP_Y(id_S271), WIRE_X(0), PIP_Y(id_S271)}, + {WIRE_X(37), PIP_Y(id_S271), WIRE_X(37), -1. + PIP_Y(id_S272)}, + {WIRE_X(37), -1. + PIP_Y(id_S272), WIRE_X(0), -1. + PIP_Y(id_S272)}}}, + {id_S27_loop1, + {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, + {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. + PIP_Y(id_S271)}, + {WIRE_X(39), -1. + PIP_Y(id_S271), WIRE_X(0), -1. + PIP_Y(id_S271)}, + {WIRE_X(37), -1. + PIP_Y(id_S271), WIRE_X(37), -1. + -1. * wrap_len}, + {WIRE_X(37), -1. + -1. * wrap_len, WIRE_X(38), -1. + -1. * wrap_len}, + {WIRE_X(38), -1. + -1. * wrap_len, WIRE_X(38), -1. + PIP_Y(id_N272)}, + {WIRE_X(38), -1. + PIP_Y(id_N272), WIRE_X(0), -1. + PIP_Y(id_N272)}}}, + {id_N27_loop1, + {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, + {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + PIP_Y(id_N271)}, + {WIRE_X(40), 1. + PIP_Y(id_N271), WIRE_X(0), 1. + PIP_Y(id_N271)}, + {WIRE_X(38), 1. + PIP_Y(id_N271), WIRE_X(38), 2. + 1. * wrap_len}, + {WIRE_X(38), 2. + 1. * wrap_len, WIRE_X(37), 2. + 1. * wrap_len}, + {WIRE_X(37), 2. + 1. * wrap_len, WIRE_X(37), 1. + PIP_Y(id_S272)}, + {WIRE_X(37), 1. + PIP_Y(id_S272), WIRE_X(0), 1. + PIP_Y(id_S272)}}}, +// clock taps +#define CLK_GT00_X 41.f +#define CLK_GT10_X 46.f +// 4 hop +#define HOP4X_START (CLK_GT00_X + 10.f) +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START) + {id_S80, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, + {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N808) - 7.}, + {HOP4X(2), PIP_Y(id_N808) - 7., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S808) - 8.}, + {HOP4X(0), PIP_Y(id_S808) - 8., WIRE_X(0), PIP_Y(id_S808) - 8.}}}, + {id_N80, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, + {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N800) + 7.}, + {HOP4X(3), PIP_Y(id_N800) + 7., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N808) + 8.}, + {HOP4X(1), PIP_Y(id_N808) + 8., WIRE_X(0), PIP_Y(id_N808) + 8.}}}, + {id_S80_loop0, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N800) - 0.}, + {HOP4X(15), PIP_Y(id_N800) - 0., HOP4X(13), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N800) + 1.}, + {HOP4X(13), PIP_Y(id_N800) + 1., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N800) + 2.}, + {HOP4X(11), PIP_Y(id_N800) + 2., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N800) + 3.}, + {HOP4X(9), PIP_Y(id_N800) + 3., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N804) + 3., WIRE_X(0), PIP_Y(id_N804) + 3.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N800) + 4.}, + {HOP4X(7), PIP_Y(id_N800) + 4., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N800) + 5.}, + {HOP4X(5), PIP_Y(id_N800) + 5., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N800) + 6.}, + {HOP4X(3), PIP_Y(id_N800) + 6., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N808) + 7.}, + {HOP4X(1), PIP_Y(id_N808) + 7., WIRE_X(0), PIP_Y(id_N808) + 7.}}}, + {id_S80_loop1, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N800) - 1.}, + {HOP4X(13), PIP_Y(id_N800) - 1., HOP4X(11), PIP_Y(id_N800) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N800) - 0.}, + {HOP4X(11), PIP_Y(id_N800) - 0., HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N800) + 1.}, + {HOP4X(9), PIP_Y(id_N800) + 1., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N804) + 1., WIRE_X(0), PIP_Y(id_N804) + 1.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N800) + 2.}, + {HOP4X(7), PIP_Y(id_N800) + 2., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N800) + 3.}, + {HOP4X(5), PIP_Y(id_N800) + 3., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N800) + 4.}, + {HOP4X(3), PIP_Y(id_N800) + 4., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N808) + 5.}, + {HOP4X(1), PIP_Y(id_N808) + 5., WIRE_X(0), PIP_Y(id_N808) + 5.}}}, + {id_S80_loop2, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N800) - 2.}, + {HOP4X(11), PIP_Y(id_N800) - 2., HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N800) - 1.}, + {HOP4X(9), PIP_Y(id_N800) - 1., HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N804) - 1., WIRE_X(0), PIP_Y(id_N804) - 1.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N800) - 0.}, + {HOP4X(7), PIP_Y(id_N800) - 0., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N800) + 1.}, + {HOP4X(5), PIP_Y(id_N800) + 1., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N800) + 2.}, + {HOP4X(3), PIP_Y(id_N800) + 2., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N808) + 3.}, + {HOP4X(1), PIP_Y(id_N808) + 3., WIRE_X(0), PIP_Y(id_N808) + 3.}}}, + {id_S80_loop3, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N800) - 3.}, + {HOP4X(9), PIP_Y(id_N804) - 3., WIRE_X(0), PIP_Y(id_N804) - 3.}, + {HOP4X(9), PIP_Y(id_N800) - 3., HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N800) - 2.}, + {HOP4X(7), PIP_Y(id_N800) - 2., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N800) - 1.}, + {HOP4X(5), PIP_Y(id_N800) - 1., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N800) - 0.}, + {HOP4X(3), PIP_Y(id_N800) - 0., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N808) + 1.}, + {HOP4X(1), PIP_Y(id_N808) + 1., WIRE_X(0), PIP_Y(id_N808) + 1.}}}, + {id_S80_loop4, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N800) - 4.}, + {HOP4X(7), PIP_Y(id_N800) - 4., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N800) - 3.}, + {HOP4X(5), PIP_Y(id_N800) - 3., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N800) - 2.}, + {HOP4X(3), PIP_Y(id_N800) - 2., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N808) - 1.}, + {HOP4X(1), PIP_Y(id_N808) - 1., WIRE_X(0), PIP_Y(id_N808) - 1.}}}, + {id_S80_loop5, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N800) - 5.}, + {HOP4X(5), PIP_Y(id_N800) - 5., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N800) - 4.}, + {HOP4X(3), PIP_Y(id_N800) - 4., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N808) - 3.}, + {HOP4X(1), PIP_Y(id_N808) - 3., WIRE_X(0), PIP_Y(id_N808) - 3.}}}, + {id_S80_loop6, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, + {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N800) - 6.}, + {HOP4X(3), PIP_Y(id_N800) - 6., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N808) - 5.}, + {HOP4X(1), PIP_Y(id_N808) - 5., WIRE_X(0), PIP_Y(id_N808) - 5.}}}, + {id_S80_loop7, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, + {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N808) - 7.}, + {HOP4X(2), PIP_Y(id_N808) - 7., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N808) - 7.}, + {HOP4X(1), PIP_Y(id_N808) - 7., WIRE_X(0), PIP_Y(id_N808) - 7.}}}, + {id_N80_loop0, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N808) + 0.}, + {HOP4X(14), PIP_Y(id_N808) + 0., HOP4X(12), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N808) - 1.}, + {HOP4X(12), PIP_Y(id_N808) - 1., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N808) - 2.}, + {HOP4X(10), PIP_Y(id_N808) - 2., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N808) - 3.}, + {HOP4X(8), PIP_Y(id_S804) - 3., WIRE_X(0), PIP_Y(id_S804) - 3.}, + {HOP4X(8), PIP_Y(id_N808) - 3., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N808) - 4.}, + {HOP4X(6), PIP_Y(id_N808) - 4., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N808) - 5.}, + {HOP4X(4), PIP_Y(id_N808) - 5., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N808) - 6.}, + {HOP4X(2), PIP_Y(id_N808) - 6., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S808) - 7.}, + {HOP4X(0), PIP_Y(id_S808) - 7., WIRE_X(0), PIP_Y(id_S808) - 7.}}}, + {id_N80_loop1, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N808) + 1.}, + {HOP4X(12), PIP_Y(id_N808) + 1., HOP4X(10), PIP_Y(id_N808) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N808) + 0.}, + {HOP4X(10), PIP_Y(id_N808) + 0., HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N808) - 1.}, + {HOP4X(8), PIP_Y(id_S804) - 1., WIRE_X(0), PIP_Y(id_S804) - 1.}, + {HOP4X(8), PIP_Y(id_N808) - 1., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N808) - 2.}, + {HOP4X(6), PIP_Y(id_N808) - 2., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N808) - 3.}, + {HOP4X(4), PIP_Y(id_N808) - 3., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N808) - 4.}, + {HOP4X(2), PIP_Y(id_N808) - 4., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S808) - 5.}, + {HOP4X(0), PIP_Y(id_S808) - 5., WIRE_X(0), PIP_Y(id_S808) - 5.}}}, + {id_N80_loop2, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N808) + 2.}, + {HOP4X(10), PIP_Y(id_N808) + 2., HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N808) + 1.}, + {HOP4X(8), PIP_Y(id_S804) + 1., WIRE_X(0), PIP_Y(id_S804) + 1.}, + {HOP4X(8), PIP_Y(id_N808) + 1., HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N808) + 0.}, + {HOP4X(6), PIP_Y(id_N808) + 0., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N808) - 1.}, + {HOP4X(4), PIP_Y(id_N808) - 1., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N808) - 2.}, + {HOP4X(2), PIP_Y(id_N808) - 2., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S808) - 3.}, + {HOP4X(0), PIP_Y(id_S808) - 3., WIRE_X(0), PIP_Y(id_S808) - 3.}}}, + {id_N80_loop3, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N808) + 3.}, + {HOP4X(8), PIP_Y(id_S804) + 3., WIRE_X(0), PIP_Y(id_S804) + 3.}, + {HOP4X(8), PIP_Y(id_N808) + 3., HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N808) + 2.}, + {HOP4X(6), PIP_Y(id_N808) + 2., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N808) + 1.}, + {HOP4X(4), PIP_Y(id_N808) + 1., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N808) + 0.}, + {HOP4X(2), PIP_Y(id_N808) + 0., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S808) - 1.}, + {HOP4X(0), PIP_Y(id_S808) - 1., WIRE_X(0), PIP_Y(id_S808) - 1.}}}, + {id_N80_loop4, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N808) + 4.}, + {HOP4X(6), PIP_Y(id_N808) + 4., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N808) + 3.}, + {HOP4X(4), PIP_Y(id_N808) + 3., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N808) + 2.}, + {HOP4X(2), PIP_Y(id_N808) + 2., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S808) + 1.}, + {HOP4X(0), PIP_Y(id_S808) + 1., WIRE_X(0), PIP_Y(id_S808) + 1.}}}, + {id_N80_loop5, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N808) + 5.}, + {HOP4X(4), PIP_Y(id_N808) + 5., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N808) + 4.}, + {HOP4X(2), PIP_Y(id_N808) + 4., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S808) + 3.}, + {HOP4X(0), PIP_Y(id_S808) + 3., WIRE_X(0), PIP_Y(id_S808) + 3.}}}, + {id_N80_loop6, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, + {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N808) + 6.}, + {HOP4X(2), PIP_Y(id_N808) + 6., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S808) + 5.}, + {HOP4X(0), PIP_Y(id_S808) + 5., WIRE_X(0), PIP_Y(id_S808) + 5.}}}, + {id_N80_loop7, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, + {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N800) + 7.}, + {HOP4X(3), PIP_Y(id_N800) + 7., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S808) + 7.}, + {HOP4X(0), PIP_Y(id_S808) + 7., WIRE_X(0), PIP_Y(id_S808) + 7.}}}, + +#undef HOP4X +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f) + {id_S81, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, + {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N818) - 7.}, + {HOP4X(2), PIP_Y(id_N818) - 7., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S818) - 8.}, + {HOP4X(0), PIP_Y(id_S818) - 8., WIRE_X(0), PIP_Y(id_S818) - 8.}}}, + {id_N81, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, + {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N810) + 7.}, + {HOP4X(3), PIP_Y(id_N810) + 7., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N818) + 8.}, + {HOP4X(1), PIP_Y(id_N818) + 8., WIRE_X(0), PIP_Y(id_N818) + 8.}}}, + {id_S81_loop0, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N810) - 0.}, + {HOP4X(15), PIP_Y(id_N810) - 0., HOP4X(13), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N810) + 1.}, + {HOP4X(13), PIP_Y(id_N810) + 1., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N810) + 2.}, + {HOP4X(11), PIP_Y(id_N810) + 2., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N810) + 3.}, + {HOP4X(9), PIP_Y(id_N810) + 3., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N814) + 3., WIRE_X(0), PIP_Y(id_N814) + 3.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N810) + 4.}, + {HOP4X(7), PIP_Y(id_N810) + 4., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N810) + 5.}, + {HOP4X(5), PIP_Y(id_N810) + 5., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N810) + 6.}, + {HOP4X(3), PIP_Y(id_N810) + 6., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N818) + 7.}, + {HOP4X(1), PIP_Y(id_N818) + 7., WIRE_X(0), PIP_Y(id_N818) + 7.}}}, + {id_S81_loop1, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N810) - 1.}, + {HOP4X(13), PIP_Y(id_N810) - 1., HOP4X(11), PIP_Y(id_N810) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N810) - 0.}, + {HOP4X(11), PIP_Y(id_N810) - 0., HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N810) + 1.}, + {HOP4X(9), PIP_Y(id_N810) + 1., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N814) + 1., WIRE_X(0), PIP_Y(id_N814) + 1.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N810) + 2.}, + {HOP4X(7), PIP_Y(id_N810) + 2., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N810) + 3.}, + {HOP4X(5), PIP_Y(id_N810) + 3., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N810) + 4.}, + {HOP4X(3), PIP_Y(id_N810) + 4., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N818) + 5.}, + {HOP4X(1), PIP_Y(id_N818) + 5., WIRE_X(0), PIP_Y(id_N818) + 5.}}}, + {id_S81_loop2, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N810) - 2.}, + {HOP4X(11), PIP_Y(id_N810) - 2., HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N810) - 1.}, + {HOP4X(9), PIP_Y(id_N810) - 1., HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N814) - 1., WIRE_X(0), PIP_Y(id_N814) - 1.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N810) - 0.}, + {HOP4X(7), PIP_Y(id_N810) - 0., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N810) + 1.}, + {HOP4X(5), PIP_Y(id_N810) + 1., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N810) + 2.}, + {HOP4X(3), PIP_Y(id_N810) + 2., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N818) + 3.}, + {HOP4X(1), PIP_Y(id_N818) + 3., WIRE_X(0), PIP_Y(id_N818) + 3.}}}, + {id_S81_loop3, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N810) - 3.}, + {HOP4X(9), PIP_Y(id_N814) - 3., WIRE_X(0), PIP_Y(id_N814) - 3.}, + {HOP4X(9), PIP_Y(id_N810) - 3., HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N810) - 2.}, + {HOP4X(7), PIP_Y(id_N810) - 2., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N810) - 1.}, + {HOP4X(5), PIP_Y(id_N810) - 1., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N810) - 0.}, + {HOP4X(3), PIP_Y(id_N810) - 0., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N818) + 1.}, + {HOP4X(1), PIP_Y(id_N818) + 1., WIRE_X(0), PIP_Y(id_N818) + 1.}}}, + {id_S81_loop4, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N810) - 4.}, + {HOP4X(7), PIP_Y(id_N810) - 4., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N810) - 3.}, + {HOP4X(5), PIP_Y(id_N810) - 3., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N810) - 2.}, + {HOP4X(3), PIP_Y(id_N810) - 2., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N818) - 1.}, + {HOP4X(1), PIP_Y(id_N818) - 1., WIRE_X(0), PIP_Y(id_N818) - 1.}}}, + {id_S81_loop5, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N810) - 5.}, + {HOP4X(5), PIP_Y(id_N810) - 5., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N810) - 4.}, + {HOP4X(3), PIP_Y(id_N810) - 4., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N818) - 3.}, + {HOP4X(1), PIP_Y(id_N818) - 3., WIRE_X(0), PIP_Y(id_N818) - 3.}}}, + {id_S81_loop6, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, + {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N810) - 6.}, + {HOP4X(3), PIP_Y(id_N810) - 6., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N818) - 5.}, + {HOP4X(1), PIP_Y(id_N818) - 5., WIRE_X(0), PIP_Y(id_N818) - 5.}}}, + {id_S81_loop7, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, + {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N818) - 7.}, + {HOP4X(2), PIP_Y(id_N818) - 7., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N818) - 7.}, + {HOP4X(1), PIP_Y(id_N818) - 7., WIRE_X(0), PIP_Y(id_N818) - 7.}}}, + {id_N81_loop0, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N818) + 0.}, + {HOP4X(14), PIP_Y(id_N818) + 0., HOP4X(12), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N818) - 1.}, + {HOP4X(12), PIP_Y(id_N818) - 1., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N818) - 2.}, + {HOP4X(10), PIP_Y(id_N818) - 2., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N818) - 3.}, + {HOP4X(8), PIP_Y(id_S814) - 3., WIRE_X(0), PIP_Y(id_S814) - 3.}, + {HOP4X(8), PIP_Y(id_N818) - 3., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N818) - 4.}, + {HOP4X(6), PIP_Y(id_N818) - 4., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N818) - 5.}, + {HOP4X(4), PIP_Y(id_N818) - 5., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N818) - 6.}, + {HOP4X(2), PIP_Y(id_N818) - 6., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S818) - 7.}, + {HOP4X(0), PIP_Y(id_S818) - 7., WIRE_X(0), PIP_Y(id_S818) - 7.}}}, + {id_N81_loop1, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N818) + 1.}, + {HOP4X(12), PIP_Y(id_N818) + 1., HOP4X(10), PIP_Y(id_N818) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N818) + 0.}, + {HOP4X(10), PIP_Y(id_N818) + 0., HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N818) - 1.}, + {HOP4X(8), PIP_Y(id_S814) - 1., WIRE_X(0), PIP_Y(id_S814) - 1.}, + {HOP4X(8), PIP_Y(id_N818) - 1., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N818) - 2.}, + {HOP4X(6), PIP_Y(id_N818) - 2., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N818) - 3.}, + {HOP4X(4), PIP_Y(id_N818) - 3., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N818) - 4.}, + {HOP4X(2), PIP_Y(id_N818) - 4., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S818) - 5.}, + {HOP4X(0), PIP_Y(id_S818) - 5., WIRE_X(0), PIP_Y(id_S818) - 5.}}}, + {id_N81_loop2, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N818) + 2.}, + {HOP4X(10), PIP_Y(id_N818) + 2., HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N818) + 1.}, + {HOP4X(8), PIP_Y(id_S814) + 1., WIRE_X(0), PIP_Y(id_S814) + 1.}, + {HOP4X(8), PIP_Y(id_N818) + 1., HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N818) + 0.}, + {HOP4X(6), PIP_Y(id_N818) + 0., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N818) - 1.}, + {HOP4X(4), PIP_Y(id_N818) - 1., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N818) - 2.}, + {HOP4X(2), PIP_Y(id_N818) - 2., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S818) - 3.}, + {HOP4X(0), PIP_Y(id_S818) - 3., WIRE_X(0), PIP_Y(id_S818) - 3.}}}, + {id_N81_loop3, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N818) + 3.}, + {HOP4X(8), PIP_Y(id_S814) + 3., WIRE_X(0), PIP_Y(id_S814) + 3.}, + {HOP4X(8), PIP_Y(id_N818) + 3., HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N818) + 2.}, + {HOP4X(6), PIP_Y(id_N818) + 2., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N818) + 1.}, + {HOP4X(4), PIP_Y(id_N818) + 1., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N818) + 0.}, + {HOP4X(2), PIP_Y(id_N818) + 0., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S818) - 1.}, + {HOP4X(0), PIP_Y(id_S818) - 1., WIRE_X(0), PIP_Y(id_S818) - 1.}}}, + {id_N81_loop4, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N818) + 4.}, + {HOP4X(6), PIP_Y(id_N818) + 4., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N818) + 3.}, + {HOP4X(4), PIP_Y(id_N818) + 3., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N818) + 2.}, + {HOP4X(2), PIP_Y(id_N818) + 2., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S818) + 1.}, + {HOP4X(0), PIP_Y(id_S818) + 1., WIRE_X(0), PIP_Y(id_S818) + 1.}}}, + {id_N81_loop5, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N818) + 5.}, + {HOP4X(4), PIP_Y(id_N818) + 5., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N818) + 4.}, + {HOP4X(2), PIP_Y(id_N818) + 4., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S818) + 3.}, + {HOP4X(0), PIP_Y(id_S818) + 3., WIRE_X(0), PIP_Y(id_S818) + 3.}}}, + {id_N81_loop6, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, + {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N818) + 6.}, + {HOP4X(2), PIP_Y(id_N818) + 6., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S818) + 5.}, + {HOP4X(0), PIP_Y(id_S818) + 5., WIRE_X(0), PIP_Y(id_S818) + 5.}}}, + {id_N81_loop7, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, + {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N810) + 7.}, + {HOP4X(3), PIP_Y(id_N810) + 7., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S818) + 7.}, + {HOP4X(0), PIP_Y(id_S818) + 7., WIRE_X(0), PIP_Y(id_S818) + 7.}}}, + +#undef HOP4X +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f + 18.f) + {id_S82, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, + {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N828) - 7.}, + {HOP4X(2), PIP_Y(id_N828) - 7., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S828) - 8.}, + {HOP4X(0), PIP_Y(id_S828) - 8., WIRE_X(0), PIP_Y(id_S828) - 8.}}}, + {id_N82, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, + {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N820) + 7.}, + {HOP4X(3), PIP_Y(id_N820) + 7., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N828) + 8.}, + {HOP4X(1), PIP_Y(id_N828) + 8., WIRE_X(0), PIP_Y(id_N828) + 8.}}}, + {id_S82_loop0, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N820) - 0.}, + {HOP4X(15), PIP_Y(id_N820) - 0., HOP4X(13), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N820) + 1.}, + {HOP4X(13), PIP_Y(id_N820) + 1., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N820) + 2.}, + {HOP4X(11), PIP_Y(id_N820) + 2., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N820) + 3.}, + {HOP4X(9), PIP_Y(id_N820) + 3., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N824) + 3., WIRE_X(0), PIP_Y(id_N824) + 3.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N820) + 4.}, + {HOP4X(7), PIP_Y(id_N820) + 4., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N820) + 5.}, + {HOP4X(5), PIP_Y(id_N820) + 5., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N820) + 6.}, + {HOP4X(3), PIP_Y(id_N820) + 6., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N828) + 7.}, + {HOP4X(1), PIP_Y(id_N828) + 7., WIRE_X(0), PIP_Y(id_N828) + 7.}}}, + {id_S82_loop1, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N820) - 1.}, + {HOP4X(13), PIP_Y(id_N820) - 1., HOP4X(11), PIP_Y(id_N820) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N820) - 0.}, + {HOP4X(11), PIP_Y(id_N820) - 0., HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N820) + 1.}, + {HOP4X(9), PIP_Y(id_N820) + 1., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N824) + 1., WIRE_X(0), PIP_Y(id_N824) + 1.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N820) + 2.}, + {HOP4X(7), PIP_Y(id_N820) + 2., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N820) + 3.}, + {HOP4X(5), PIP_Y(id_N820) + 3., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N820) + 4.}, + {HOP4X(3), PIP_Y(id_N820) + 4., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N828) + 5.}, + {HOP4X(1), PIP_Y(id_N828) + 5., WIRE_X(0), PIP_Y(id_N828) + 5.}}}, + {id_S82_loop2, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N820) - 2.}, + {HOP4X(11), PIP_Y(id_N820) - 2., HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N820) - 1.}, + {HOP4X(9), PIP_Y(id_N820) - 1., HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N824) - 1., WIRE_X(0), PIP_Y(id_N824) - 1.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N820) - 0.}, + {HOP4X(7), PIP_Y(id_N820) - 0., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N820) + 1.}, + {HOP4X(5), PIP_Y(id_N820) + 1., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N820) + 2.}, + {HOP4X(3), PIP_Y(id_N820) + 2., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N828) + 3.}, + {HOP4X(1), PIP_Y(id_N828) + 3., WIRE_X(0), PIP_Y(id_N828) + 3.}}}, + {id_S82_loop3, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N820) - 3.}, + {HOP4X(9), PIP_Y(id_N824) - 3., WIRE_X(0), PIP_Y(id_N824) - 3.}, + {HOP4X(9), PIP_Y(id_N820) - 3., HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N820) - 2.}, + {HOP4X(7), PIP_Y(id_N820) - 2., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N820) - 1.}, + {HOP4X(5), PIP_Y(id_N820) - 1., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N820) - 0.}, + {HOP4X(3), PIP_Y(id_N820) - 0., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N828) + 1.}, + {HOP4X(1), PIP_Y(id_N828) + 1., WIRE_X(0), PIP_Y(id_N828) + 1.}}}, + {id_S82_loop4, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N820) - 4.}, + {HOP4X(7), PIP_Y(id_N820) - 4., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N820) - 3.}, + {HOP4X(5), PIP_Y(id_N820) - 3., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N820) - 2.}, + {HOP4X(3), PIP_Y(id_N820) - 2., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N828) - 1.}, + {HOP4X(1), PIP_Y(id_N828) - 1., WIRE_X(0), PIP_Y(id_N828) - 1.}}}, + {id_S82_loop5, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N820) - 5.}, + {HOP4X(5), PIP_Y(id_N820) - 5., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N820) - 4.}, + {HOP4X(3), PIP_Y(id_N820) - 4., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N828) - 3.}, + {HOP4X(1), PIP_Y(id_N828) - 3., WIRE_X(0), PIP_Y(id_N828) - 3.}}}, + {id_S82_loop6, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, + {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N820) - 6.}, + {HOP4X(3), PIP_Y(id_N820) - 6., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N828) - 5.}, + {HOP4X(1), PIP_Y(id_N828) - 5., WIRE_X(0), PIP_Y(id_N828) - 5.}}}, + {id_S82_loop7, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, + {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N828) - 7.}, + {HOP4X(2), PIP_Y(id_N828) - 7., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N828) - 7.}, + {HOP4X(1), PIP_Y(id_N828) - 7., WIRE_X(0), PIP_Y(id_N828) - 7.}}}, + {id_N82_loop0, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N828) + 0.}, + {HOP4X(14), PIP_Y(id_N828) + 0., HOP4X(12), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N828) - 1.}, + {HOP4X(12), PIP_Y(id_N828) - 1., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N828) - 2.}, + {HOP4X(10), PIP_Y(id_N828) - 2., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N828) - 3.}, + {HOP4X(8), PIP_Y(id_S824) - 3., WIRE_X(0), PIP_Y(id_S824) - 3.}, + {HOP4X(8), PIP_Y(id_N828) - 3., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N828) - 4.}, + {HOP4X(6), PIP_Y(id_N828) - 4., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N828) - 5.}, + {HOP4X(4), PIP_Y(id_N828) - 5., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N828) - 6.}, + {HOP4X(2), PIP_Y(id_N828) - 6., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S828) - 7.}, + {HOP4X(0), PIP_Y(id_S828) - 7., WIRE_X(0), PIP_Y(id_S828) - 7.}}}, + {id_N82_loop1, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N828) + 1.}, + {HOP4X(12), PIP_Y(id_N828) + 1., HOP4X(10), PIP_Y(id_N828) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N828) + 0.}, + {HOP4X(10), PIP_Y(id_N828) + 0., HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N828) - 1.}, + {HOP4X(8), PIP_Y(id_S824) - 1., WIRE_X(0), PIP_Y(id_S824) - 1.}, + {HOP4X(8), PIP_Y(id_N828) - 1., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N828) - 2.}, + {HOP4X(6), PIP_Y(id_N828) - 2., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N828) - 3.}, + {HOP4X(4), PIP_Y(id_N828) - 3., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N828) - 4.}, + {HOP4X(2), PIP_Y(id_N828) - 4., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S828) - 5.}, + {HOP4X(0), PIP_Y(id_S828) - 5., WIRE_X(0), PIP_Y(id_S828) - 5.}}}, + {id_N82_loop2, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N828) + 2.}, + {HOP4X(10), PIP_Y(id_N828) + 2., HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N828) + 1.}, + {HOP4X(8), PIP_Y(id_S824) + 1., WIRE_X(0), PIP_Y(id_S824) + 1.}, + {HOP4X(8), PIP_Y(id_N828) + 1., HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N828) + 0.}, + {HOP4X(6), PIP_Y(id_N828) + 0., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N828) - 1.}, + {HOP4X(4), PIP_Y(id_N828) - 1., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N828) - 2.}, + {HOP4X(2), PIP_Y(id_N828) - 2., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S828) - 3.}, + {HOP4X(0), PIP_Y(id_S828) - 3., WIRE_X(0), PIP_Y(id_S828) - 3.}}}, + {id_N82_loop3, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N828) + 3.}, + {HOP4X(8), PIP_Y(id_S824) + 3., WIRE_X(0), PIP_Y(id_S824) + 3.}, + {HOP4X(8), PIP_Y(id_N828) + 3., HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N828) + 2.}, + {HOP4X(6), PIP_Y(id_N828) + 2., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N828) + 1.}, + {HOP4X(4), PIP_Y(id_N828) + 1., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N828) + 0.}, + {HOP4X(2), PIP_Y(id_N828) + 0., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S828) - 1.}, + {HOP4X(0), PIP_Y(id_S828) - 1., WIRE_X(0), PIP_Y(id_S828) - 1.}}}, + {id_N82_loop4, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N828) + 4.}, + {HOP4X(6), PIP_Y(id_N828) + 4., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N828) + 3.}, + {HOP4X(4), PIP_Y(id_N828) + 3., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N828) + 2.}, + {HOP4X(2), PIP_Y(id_N828) + 2., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S828) + 1.}, + {HOP4X(0), PIP_Y(id_S828) + 1., WIRE_X(0), PIP_Y(id_S828) + 1.}}}, + {id_N82_loop5, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N828) + 5.}, + {HOP4X(4), PIP_Y(id_N828) + 5., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N828) + 4.}, + {HOP4X(2), PIP_Y(id_N828) + 4., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S828) + 3.}, + {HOP4X(0), PIP_Y(id_S828) + 3., WIRE_X(0), PIP_Y(id_S828) + 3.}}}, + {id_N82_loop6, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, + {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N828) + 6.}, + {HOP4X(2), PIP_Y(id_N828) + 6., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S828) + 5.}, + {HOP4X(0), PIP_Y(id_S828) + 5., WIRE_X(0), PIP_Y(id_S828) + 5.}}}, + {id_N82_loop7, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, + {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N820) + 7.}, + {HOP4X(3), PIP_Y(id_N820) + 7., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S828) + 7.}, + {HOP4X(0), PIP_Y(id_S828) + 7., WIRE_X(0), PIP_Y(id_S828) + 7.}}}, + +#undef HOP4X +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f + 18.f + 18.f) + {id_S83, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, + {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N838) - 7.}, + {HOP4X(2), PIP_Y(id_N838) - 7., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S838) - 8.}, + {HOP4X(0), PIP_Y(id_S838) - 8., WIRE_X(0), PIP_Y(id_S838) - 8.}}}, + {id_N83, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, + {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N830) + 7.}, + {HOP4X(3), PIP_Y(id_N830) + 7., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N838) + 8.}, + {HOP4X(1), PIP_Y(id_N838) + 8., WIRE_X(0), PIP_Y(id_N838) + 8.}}}, + {id_S83_loop0, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N830) - 0.}, + {HOP4X(15), PIP_Y(id_N830) - 0., HOP4X(13), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N830) + 1.}, + {HOP4X(13), PIP_Y(id_N830) + 1., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N830) + 2.}, + {HOP4X(11), PIP_Y(id_N830) + 2., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N830) + 3.}, + {HOP4X(9), PIP_Y(id_N830) + 3., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N834) + 3., WIRE_X(0), PIP_Y(id_N834) + 3.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N830) + 4.}, + {HOP4X(7), PIP_Y(id_N830) + 4., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N830) + 5.}, + {HOP4X(5), PIP_Y(id_N830) + 5., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N830) + 6.}, + {HOP4X(3), PIP_Y(id_N830) + 6., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N838) + 7.}, + {HOP4X(1), PIP_Y(id_N838) + 7., WIRE_X(0), PIP_Y(id_N838) + 7.}}}, + {id_S83_loop1, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N830) - 1.}, + {HOP4X(13), PIP_Y(id_N830) - 1., HOP4X(11), PIP_Y(id_N830) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N830) - 0.}, + {HOP4X(11), PIP_Y(id_N830) - 0., HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N830) + 1.}, + {HOP4X(9), PIP_Y(id_N830) + 1., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N834) + 1., WIRE_X(0), PIP_Y(id_N834) + 1.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N830) + 2.}, + {HOP4X(7), PIP_Y(id_N830) + 2., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N830) + 3.}, + {HOP4X(5), PIP_Y(id_N830) + 3., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N830) + 4.}, + {HOP4X(3), PIP_Y(id_N830) + 4., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N838) + 5.}, + {HOP4X(1), PIP_Y(id_N838) + 5., WIRE_X(0), PIP_Y(id_N838) + 5.}}}, + {id_S83_loop2, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N830) - 2.}, + {HOP4X(11), PIP_Y(id_N830) - 2., HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N830) - 1.}, + {HOP4X(9), PIP_Y(id_N830) - 1., HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N834) - 1., WIRE_X(0), PIP_Y(id_N834) - 1.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N830) - 0.}, + {HOP4X(7), PIP_Y(id_N830) - 0., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N830) + 1.}, + {HOP4X(5), PIP_Y(id_N830) + 1., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N830) + 2.}, + {HOP4X(3), PIP_Y(id_N830) + 2., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N838) + 3.}, + {HOP4X(1), PIP_Y(id_N838) + 3., WIRE_X(0), PIP_Y(id_N838) + 3.}}}, + {id_S83_loop3, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N830) - 3.}, + {HOP4X(9), PIP_Y(id_N834) - 3., WIRE_X(0), PIP_Y(id_N834) - 3.}, + {HOP4X(9), PIP_Y(id_N830) - 3., HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N830) - 2.}, + {HOP4X(7), PIP_Y(id_N830) - 2., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N830) - 1.}, + {HOP4X(5), PIP_Y(id_N830) - 1., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N830) - 0.}, + {HOP4X(3), PIP_Y(id_N830) - 0., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N838) + 1.}, + {HOP4X(1), PIP_Y(id_N838) + 1., WIRE_X(0), PIP_Y(id_N838) + 1.}}}, + {id_S83_loop4, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N830) - 4.}, + {HOP4X(7), PIP_Y(id_N830) - 4., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N830) - 3.}, + {HOP4X(5), PIP_Y(id_N830) - 3., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N830) - 2.}, + {HOP4X(3), PIP_Y(id_N830) - 2., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N838) - 1.}, + {HOP4X(1), PIP_Y(id_N838) - 1., WIRE_X(0), PIP_Y(id_N838) - 1.}}}, + {id_S83_loop5, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N830) - 5.}, + {HOP4X(5), PIP_Y(id_N830) - 5., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N830) - 4.}, + {HOP4X(3), PIP_Y(id_N830) - 4., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N838) - 3.}, + {HOP4X(1), PIP_Y(id_N838) - 3., WIRE_X(0), PIP_Y(id_N838) - 3.}}}, + {id_S83_loop6, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, + {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N830) - 6.}, + {HOP4X(3), PIP_Y(id_N830) - 6., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N838) - 5.}, + {HOP4X(1), PIP_Y(id_N838) - 5., WIRE_X(0), PIP_Y(id_N838) - 5.}}}, + {id_S83_loop7, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, + {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N838) - 7.}, + {HOP4X(2), PIP_Y(id_N838) - 7., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N838) - 7.}, + {HOP4X(1), PIP_Y(id_N838) - 7., WIRE_X(0), PIP_Y(id_N838) - 7.}}}, + {id_N83_loop0, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N838) + 0.}, + {HOP4X(14), PIP_Y(id_N838) + 0., HOP4X(12), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N838) - 1.}, + {HOP4X(12), PIP_Y(id_N838) - 1., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N838) - 2.}, + {HOP4X(10), PIP_Y(id_N838) - 2., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N838) - 3.}, + {HOP4X(8), PIP_Y(id_S834) - 3., WIRE_X(0), PIP_Y(id_S834) - 3.}, + {HOP4X(8), PIP_Y(id_N838) - 3., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N838) - 4.}, + {HOP4X(6), PIP_Y(id_N838) - 4., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N838) - 5.}, + {HOP4X(4), PIP_Y(id_N838) - 5., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N838) - 6.}, + {HOP4X(2), PIP_Y(id_N838) - 6., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S838) - 7.}, + {HOP4X(0), PIP_Y(id_S838) - 7., WIRE_X(0), PIP_Y(id_S838) - 7.}}}, + {id_N83_loop1, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N838) + 1.}, + {HOP4X(12), PIP_Y(id_N838) + 1., HOP4X(10), PIP_Y(id_N838) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N838) + 0.}, + {HOP4X(10), PIP_Y(id_N838) + 0., HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N838) - 1.}, + {HOP4X(8), PIP_Y(id_S834) - 1., WIRE_X(0), PIP_Y(id_S834) - 1.}, + {HOP4X(8), PIP_Y(id_N838) - 1., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N838) - 2.}, + {HOP4X(6), PIP_Y(id_N838) - 2., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N838) - 3.}, + {HOP4X(4), PIP_Y(id_N838) - 3., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N838) - 4.}, + {HOP4X(2), PIP_Y(id_N838) - 4., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S838) - 5.}, + {HOP4X(0), PIP_Y(id_S838) - 5., WIRE_X(0), PIP_Y(id_S838) - 5.}}}, + {id_N83_loop2, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N838) + 2.}, + {HOP4X(10), PIP_Y(id_N838) + 2., HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N838) + 1.}, + {HOP4X(8), PIP_Y(id_S834) + 1., WIRE_X(0), PIP_Y(id_S834) + 1.}, + {HOP4X(8), PIP_Y(id_N838) + 1., HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N838) + 0.}, + {HOP4X(6), PIP_Y(id_N838) + 0., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N838) - 1.}, + {HOP4X(4), PIP_Y(id_N838) - 1., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N838) - 2.}, + {HOP4X(2), PIP_Y(id_N838) - 2., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S838) - 3.}, + {HOP4X(0), PIP_Y(id_S838) - 3., WIRE_X(0), PIP_Y(id_S838) - 3.}}}, + {id_N83_loop3, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N838) + 3.}, + {HOP4X(8), PIP_Y(id_S834) + 3., WIRE_X(0), PIP_Y(id_S834) + 3.}, + {HOP4X(8), PIP_Y(id_N838) + 3., HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N838) + 2.}, + {HOP4X(6), PIP_Y(id_N838) + 2., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N838) + 1.}, + {HOP4X(4), PIP_Y(id_N838) + 1., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N838) + 0.}, + {HOP4X(2), PIP_Y(id_N838) + 0., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S838) - 1.}, + {HOP4X(0), PIP_Y(id_S838) - 1., WIRE_X(0), PIP_Y(id_S838) - 1.}}}, + {id_N83_loop4, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N838) + 4.}, + {HOP4X(6), PIP_Y(id_N838) + 4., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N838) + 3.}, + {HOP4X(4), PIP_Y(id_N838) + 3., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N838) + 2.}, + {HOP4X(2), PIP_Y(id_N838) + 2., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S838) + 1.}, + {HOP4X(0), PIP_Y(id_S838) + 1., WIRE_X(0), PIP_Y(id_S838) + 1.}}}, + {id_N83_loop5, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N838) + 5.}, + {HOP4X(4), PIP_Y(id_N838) + 5., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N838) + 4.}, + {HOP4X(2), PIP_Y(id_N838) + 4., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S838) + 3.}, + {HOP4X(0), PIP_Y(id_S838) + 3., WIRE_X(0), PIP_Y(id_S838) + 3.}}}, + {id_N83_loop6, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, + {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N838) + 6.}, + {HOP4X(2), PIP_Y(id_N838) + 6., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S838) + 5.}, + {HOP4X(0), PIP_Y(id_S838) + 5., WIRE_X(0), PIP_Y(id_S838) + 5.}}}, + {id_N83_loop7, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, + {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N830) + 7.}, + {HOP4X(3), PIP_Y(id_N830) + 7., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S838) + 7.}, + {HOP4X(0), PIP_Y(id_S838) + 7., WIRE_X(0), PIP_Y(id_S838) + 7.}}}, + +#define PIP_X(pip_id) (pipPoint.at(pip_id).second) +#define WIRE_Y(offset) (cru_y + cru_h + ((float)offset) * ew_dist) + // 1 hop + {id_E10, + {{PIP_X(id_E100), WIRE_Y(0), PIP_X(id_E100), WIRE_Y(1)}, + {PIP_X(id_E100), WIRE_Y(1), PIP_X(id_E101) + 1., WIRE_Y(1)}, + {PIP_X(id_E101) + 1., WIRE_Y(1), PIP_X(id_E101) + 1., WIRE_Y(0)}}}, + {id_W10, + {{PIP_X(id_W100), WIRE_Y(0), PIP_X(id_W100), WIRE_Y(2)}, + {PIP_X(id_W100), WIRE_Y(2), PIP_X(id_W101) - 1., WIRE_Y(2)}, + {PIP_X(id_W101) - 1., WIRE_Y(2), PIP_X(id_W101) - 1., WIRE_Y(0)}}}, + {id_E10_loop0, + {{PIP_X(id_E100), WIRE_Y(0), PIP_X(id_E100), WIRE_Y(1)}, + {PIP_X(id_E100), WIRE_Y(1), 1. + wrap_len, WIRE_Y(1)}, + {1. + wrap_len, WIRE_Y(1), 1. + wrap_len, WIRE_Y(2)}, + {1. + wrap_len, WIRE_Y(2), PIP_X(id_W101), WIRE_Y(2)}, + {PIP_X(id_W101), WIRE_Y(2), PIP_X(id_W101), WIRE_Y(0)}}}, + {id_W10_loop0, + {{PIP_X(id_W100), WIRE_Y(0), PIP_X(id_W100), WIRE_Y(2)}, + {PIP_X(id_W100), WIRE_Y(2), -1. * wrap_len, WIRE_Y(2)}, + {-1. * wrap_len, WIRE_Y(2), -1. * wrap_len, WIRE_Y(1)}, + {-1. * wrap_len, WIRE_Y(1), PIP_X(id_E101), WIRE_Y(1)}, + {PIP_X(id_E101), WIRE_Y(1), PIP_X(id_E101), WIRE_Y(0)}}}, + {id_E13, + {{PIP_X(id_E130), WIRE_Y(0), PIP_X(id_E130), WIRE_Y(3)}, + {PIP_X(id_E130), WIRE_Y(3), PIP_X(id_E131) + 1., WIRE_Y(3)}, + {PIP_X(id_E131) + 1., WIRE_Y(3), PIP_X(id_E131) + 1., WIRE_Y(0)}}}, + {id_W13, + {{PIP_X(id_W130), WIRE_Y(0), PIP_X(id_W130), WIRE_Y(4)}, + {PIP_X(id_W130), WIRE_Y(4), PIP_X(id_W131) - 1., WIRE_Y(4)}, + {PIP_X(id_W131) - 1., WIRE_Y(4), PIP_X(id_W131) - 1., WIRE_Y(0)}}}, + {id_E13_loop0, + {{PIP_X(id_E130), WIRE_Y(0), PIP_X(id_E130), WIRE_Y(3)}, + {PIP_X(id_E130), WIRE_Y(3), 1. + wrap_len, WIRE_Y(3)}, + {1. + wrap_len, WIRE_Y(3), 1. + wrap_len, WIRE_Y(4)}, + {1. + wrap_len, WIRE_Y(4), PIP_X(id_W131), WIRE_Y(4)}, + {PIP_X(id_W131), WIRE_Y(4), PIP_X(id_W131), WIRE_Y(0)}}}, + {id_W13_loop0, + {{PIP_X(id_W130), WIRE_Y(0), PIP_X(id_W130), WIRE_Y(4)}, + {PIP_X(id_W130), WIRE_Y(4), -1. * wrap_len, WIRE_Y(4)}, + {-1. * wrap_len, WIRE_Y(4), -1. * wrap_len, WIRE_Y(3)}, + {-1. * wrap_len, WIRE_Y(3), PIP_X(id_E131), WIRE_Y(3)}, + {PIP_X(id_E131), WIRE_Y(3), PIP_X(id_E131), WIRE_Y(0)}}}, + // 1 hop EW + {id_EW10, + {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, + {PIP_X(id_EW10), WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(6)}, + {PIP_X(id_E111) + 1., WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(0)}, + {PIP_X(id_EW10), WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(5)}, + {PIP_X(id_W111) - 1., WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(0)}}}, + {id_EW10_loop_e, + {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, + {PIP_X(id_EW10), WIRE_Y(6), wrap_len + 1., WIRE_Y(6)}, + {wrap_len + 1., WIRE_Y(6), wrap_len + 1., WIRE_Y(5)}, + {wrap_len + 1., WIRE_Y(5), PIP_X(id_W111), WIRE_Y(5)}, + {PIP_X(id_W111), WIRE_Y(5), PIP_X(id_W111), WIRE_Y(0)}, + {PIP_X(id_EW10), WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(5)}, + {PIP_X(id_W111) - 1., WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(0)}}}, + {id_EW10_loop_w, + {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, + {PIP_X(id_EW10), WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(6)}, + {PIP_X(id_E111) + 1., WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(0)}, + {PIP_X(id_EW10), WIRE_Y(5), -wrap_len, WIRE_Y(5)}, + {-wrap_len, WIRE_Y(5), -wrap_len, WIRE_Y(6)}, + {-wrap_len, WIRE_Y(6), PIP_X(id_E111), WIRE_Y(6)}, + {PIP_X(id_E111), WIRE_Y(6), PIP_X(id_E111), WIRE_Y(0)}}}, + {id_EW20, + {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, + {PIP_X(id_EW20), WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(8)}, + {PIP_X(id_E121) + 1., WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(0)}, + {PIP_X(id_EW20), WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(7)}, + {PIP_X(id_W121) - 1., WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(0)}}}, + {id_EW20_loop_e, + {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, + {PIP_X(id_EW20), WIRE_Y(8), wrap_len + 1., WIRE_Y(8)}, + {wrap_len + 1., WIRE_Y(8), wrap_len + 1., WIRE_Y(7)}, + {wrap_len + 1., WIRE_Y(7), PIP_X(id_W121), WIRE_Y(7)}, + {PIP_X(id_W121), WIRE_Y(7), PIP_X(id_W121), WIRE_Y(0)}, + {PIP_X(id_EW20), WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(7)}, + {PIP_X(id_W121) - 1., WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(0)}}}, + {id_EW20_loop_w, + {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, + {PIP_X(id_EW20), WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(8)}, + {PIP_X(id_E121) + 1., WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(0)}, + {PIP_X(id_EW20), WIRE_Y(7), -wrap_len, WIRE_Y(7)}, + {-wrap_len, WIRE_Y(7), -wrap_len, WIRE_Y(8)}, + {-wrap_len, WIRE_Y(8), PIP_X(id_E121), WIRE_Y(8)}, + {PIP_X(id_E121), WIRE_Y(8), PIP_X(id_E121), WIRE_Y(0)}}}, +// 2 hop +#define HOP2Y(offset) WIRE_Y(offset + 9) + {id_E20, + {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, + {PIP_X(id_E200), HOP2Y(2), PIP_X(id_E201) + 1., HOP2Y(2)}, + {PIP_X(id_E201) + 1., HOP2Y(2), PIP_X(id_E201) + 1., WIRE_Y(0)}, + {PIP_X(id_E201) + 1., HOP2Y(0), PIP_X(id_E202) + 2., HOP2Y(0)}, + {PIP_X(id_E202) + 2., HOP2Y(0), PIP_X(id_E202) + 2., WIRE_Y(0)}}}, + {id_W20, + {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, + {PIP_X(id_W200), HOP2Y(3), PIP_X(id_W201) - 1., HOP2Y(3)}, + {PIP_X(id_W201) - 1., HOP2Y(3), PIP_X(id_W201) - 1., WIRE_Y(0)}, + {PIP_X(id_W201) - 1., HOP2Y(1), PIP_X(id_W202) - 2., HOP2Y(1)}, + {PIP_X(id_W202) - 2., HOP2Y(1), PIP_X(id_W202) - 2., WIRE_Y(0)}}}, + {id_E20_loop0, + {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, + {PIP_X(id_E200), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W201), HOP2Y(3)}, + {PIP_X(id_W201), HOP2Y(3), PIP_X(id_W201), WIRE_Y(0)}, + {PIP_X(id_W201), HOP2Y(1), PIP_X(id_W202) - 1., HOP2Y(1)}, + {PIP_X(id_W202) - 1., HOP2Y(1), PIP_X(id_W202) - 1., WIRE_Y(0)}}}, + {id_W20_loop0, + {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, + {PIP_X(id_W200), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E201), HOP2Y(2)}, + {PIP_X(id_E201), HOP2Y(2), PIP_X(id_E201), WIRE_Y(0)}, + {PIP_X(id_E201), HOP2Y(0), PIP_X(id_E202) + 1., HOP2Y(0)}, + {PIP_X(id_E202) + 1., HOP2Y(0), PIP_X(id_E202) + 1., WIRE_Y(0)}}}, + {id_E20_loop1, + {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, + {PIP_X(id_E200), HOP2Y(2), PIP_X(id_E201) + 1., HOP2Y(2)}, + {PIP_X(id_E201) + 1., HOP2Y(2), PIP_X(id_E201) + 1., WIRE_Y(0)}, + {PIP_X(id_E201) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W202) + 1., HOP2Y(1)}, + {PIP_X(id_W202) + 1., HOP2Y(1), PIP_X(id_W202) + 1., WIRE_Y(0)}}}, + {id_W20_loop1, + {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, + {PIP_X(id_W200), HOP2Y(3), PIP_X(id_W201) - 1., HOP2Y(3)}, + {PIP_X(id_W201) - 1., HOP2Y(3), PIP_X(id_W201) - 1., WIRE_Y(0)}, + {PIP_X(id_W201) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E202) - 1., HOP2Y(0)}, + {PIP_X(id_E202) - 1., HOP2Y(0), PIP_X(id_E202) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 1) + {id_E21, + {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, + {PIP_X(id_E210), HOP2Y(2), PIP_X(id_E211) + 1., HOP2Y(2)}, + {PIP_X(id_E211) + 1., HOP2Y(2), PIP_X(id_E211) + 1., WIRE_Y(0)}, + {PIP_X(id_E211) + 1., HOP2Y(0), PIP_X(id_E212) + 2., HOP2Y(0)}, + {PIP_X(id_E212) + 2., HOP2Y(0), PIP_X(id_E212) + 2., WIRE_Y(0)}}}, + {id_W21, + {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, + {PIP_X(id_W210), HOP2Y(3), PIP_X(id_W211) - 1., HOP2Y(3)}, + {PIP_X(id_W211) - 1., HOP2Y(3), PIP_X(id_W211) - 1., WIRE_Y(0)}, + {PIP_X(id_W211) - 1., HOP2Y(1), PIP_X(id_W212) - 2., HOP2Y(1)}, + {PIP_X(id_W212) - 2., HOP2Y(1), PIP_X(id_W212) - 2., WIRE_Y(0)}}}, + {id_E21_loop0, + {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, + {PIP_X(id_E210), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W211), HOP2Y(3)}, + {PIP_X(id_W211), HOP2Y(3), PIP_X(id_W211), WIRE_Y(0)}, + {PIP_X(id_W211), HOP2Y(1), PIP_X(id_W212) - 1., HOP2Y(1)}, + {PIP_X(id_W212) - 1., HOP2Y(1), PIP_X(id_W212) - 1., WIRE_Y(0)}}}, + {id_W21_loop0, + {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, + {PIP_X(id_W210), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E211), HOP2Y(2)}, + {PIP_X(id_E211), HOP2Y(2), PIP_X(id_E211), WIRE_Y(0)}, + {PIP_X(id_E211), HOP2Y(0), PIP_X(id_E212) + 1., HOP2Y(0)}, + {PIP_X(id_E212) + 1., HOP2Y(0), PIP_X(id_E212) + 1., WIRE_Y(0)}}}, + {id_E21_loop1, + {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, + {PIP_X(id_E210), HOP2Y(2), PIP_X(id_E211) + 1., HOP2Y(2)}, + {PIP_X(id_E211) + 1., HOP2Y(2), PIP_X(id_E211) + 1., WIRE_Y(0)}, + {PIP_X(id_E211) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W212) + 1., HOP2Y(1)}, + {PIP_X(id_W212) + 1., HOP2Y(1), PIP_X(id_W212) + 1., WIRE_Y(0)}}}, + {id_W21_loop1, + {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, + {PIP_X(id_W210), HOP2Y(3), PIP_X(id_W211) - 1., HOP2Y(3)}, + {PIP_X(id_W211) - 1., HOP2Y(3), PIP_X(id_W211) - 1., WIRE_Y(0)}, + {PIP_X(id_W211) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E212) - 1., HOP2Y(0)}, + {PIP_X(id_E212) - 1., HOP2Y(0), PIP_X(id_E212) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 2) + {id_E22, + {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, + {PIP_X(id_E220), HOP2Y(2), PIP_X(id_E221) + 1., HOP2Y(2)}, + {PIP_X(id_E221) + 1., HOP2Y(2), PIP_X(id_E221) + 1., WIRE_Y(0)}, + {PIP_X(id_E221) + 1., HOP2Y(0), PIP_X(id_E222) + 2., HOP2Y(0)}, + {PIP_X(id_E222) + 2., HOP2Y(0), PIP_X(id_E222) + 2., WIRE_Y(0)}}}, + {id_W22, + {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, + {PIP_X(id_W220), HOP2Y(3), PIP_X(id_W221) - 1., HOP2Y(3)}, + {PIP_X(id_W221) - 1., HOP2Y(3), PIP_X(id_W221) - 1., WIRE_Y(0)}, + {PIP_X(id_W221) - 1., HOP2Y(1), PIP_X(id_W222) - 2., HOP2Y(1)}, + {PIP_X(id_W222) - 2., HOP2Y(1), PIP_X(id_W222) - 2., WIRE_Y(0)}}}, + {id_E22_loop0, + {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, + {PIP_X(id_E220), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W221), HOP2Y(3)}, + {PIP_X(id_W221), HOP2Y(3), PIP_X(id_W221), WIRE_Y(0)}, + {PIP_X(id_W221), HOP2Y(1), PIP_X(id_W222) - 1., HOP2Y(1)}, + {PIP_X(id_W222) - 1., HOP2Y(1), PIP_X(id_W222) - 1., WIRE_Y(0)}}}, + {id_W22_loop0, + {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, + {PIP_X(id_W220), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E221), HOP2Y(2)}, + {PIP_X(id_E221), HOP2Y(2), PIP_X(id_E221), WIRE_Y(0)}, + {PIP_X(id_E221), HOP2Y(0), PIP_X(id_E222) + 1., HOP2Y(0)}, + {PIP_X(id_E222) + 1., HOP2Y(0), PIP_X(id_E222) + 1., WIRE_Y(0)}}}, + {id_E22_loop1, + {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, + {PIP_X(id_E220), HOP2Y(2), PIP_X(id_E221) + 1., HOP2Y(2)}, + {PIP_X(id_E221) + 1., HOP2Y(2), PIP_X(id_E221) + 1., WIRE_Y(0)}, + {PIP_X(id_E221) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W222) + 1., HOP2Y(1)}, + {PIP_X(id_W222) + 1., HOP2Y(1), PIP_X(id_W222) + 1., WIRE_Y(0)}}}, + {id_W22_loop1, + {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, + {PIP_X(id_W220), HOP2Y(3), PIP_X(id_W221) - 1., HOP2Y(3)}, + {PIP_X(id_W221) - 1., HOP2Y(3), PIP_X(id_W221) - 1., WIRE_Y(0)}, + {PIP_X(id_W221) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E222) - 1., HOP2Y(0)}, + {PIP_X(id_E222) - 1., HOP2Y(0), PIP_X(id_E222) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 3) + {id_E23, + {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, + {PIP_X(id_E230), HOP2Y(2), PIP_X(id_E231) + 1., HOP2Y(2)}, + {PIP_X(id_E231) + 1., HOP2Y(2), PIP_X(id_E231) + 1., WIRE_Y(0)}, + {PIP_X(id_E231) + 1., HOP2Y(0), PIP_X(id_E232) + 2., HOP2Y(0)}, + {PIP_X(id_E232) + 2., HOP2Y(0), PIP_X(id_E232) + 2., WIRE_Y(0)}}}, + {id_W23, + {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, + {PIP_X(id_W230), HOP2Y(3), PIP_X(id_W231) - 1., HOP2Y(3)}, + {PIP_X(id_W231) - 1., HOP2Y(3), PIP_X(id_W231) - 1., WIRE_Y(0)}, + {PIP_X(id_W231) - 1., HOP2Y(1), PIP_X(id_W232) - 2., HOP2Y(1)}, + {PIP_X(id_W232) - 2., HOP2Y(1), PIP_X(id_W232) - 2., WIRE_Y(0)}}}, + {id_E23_loop0, + {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, + {PIP_X(id_E230), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W231), HOP2Y(3)}, + {PIP_X(id_W231), HOP2Y(3), PIP_X(id_W231), WIRE_Y(0)}, + {PIP_X(id_W231), HOP2Y(1), PIP_X(id_W232) - 1., HOP2Y(1)}, + {PIP_X(id_W232) - 1., HOP2Y(1), PIP_X(id_W232) - 1., WIRE_Y(0)}}}, + {id_W23_loop0, + {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, + {PIP_X(id_W230), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E231), HOP2Y(2)}, + {PIP_X(id_E231), HOP2Y(2), PIP_X(id_E231), WIRE_Y(0)}, + {PIP_X(id_E231), HOP2Y(0), PIP_X(id_E232) + 1., HOP2Y(0)}, + {PIP_X(id_E232) + 1., HOP2Y(0), PIP_X(id_E232) + 1., WIRE_Y(0)}}}, + {id_E23_loop1, + {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, + {PIP_X(id_E230), HOP2Y(2), PIP_X(id_E231) + 1., HOP2Y(2)}, + {PIP_X(id_E231) + 1., HOP2Y(2), PIP_X(id_E231) + 1., WIRE_Y(0)}, + {PIP_X(id_E231) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W232) + 1., HOP2Y(1)}, + {PIP_X(id_W232) + 1., HOP2Y(1), PIP_X(id_W232) + 1., WIRE_Y(0)}}}, + {id_W23_loop1, + {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, + {PIP_X(id_W230), HOP2Y(3), PIP_X(id_W231) - 1., HOP2Y(3)}, + {PIP_X(id_W231) - 1., HOP2Y(3), PIP_X(id_W231) - 1., WIRE_Y(0)}, + {PIP_X(id_W231) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E232) - 1., HOP2Y(0)}, + {PIP_X(id_E232) - 1., HOP2Y(0), PIP_X(id_E232) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 4) + {id_E24, + {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, + {PIP_X(id_E240), HOP2Y(2), PIP_X(id_E241) + 1., HOP2Y(2)}, + {PIP_X(id_E241) + 1., HOP2Y(2), PIP_X(id_E241) + 1., WIRE_Y(0)}, + {PIP_X(id_E241) + 1., HOP2Y(0), PIP_X(id_E242) + 2., HOP2Y(0)}, + {PIP_X(id_E242) + 2., HOP2Y(0), PIP_X(id_E242) + 2., WIRE_Y(0)}}}, + {id_W24, + {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, + {PIP_X(id_W240), HOP2Y(3), PIP_X(id_W241) - 1., HOP2Y(3)}, + {PIP_X(id_W241) - 1., HOP2Y(3), PIP_X(id_W241) - 1., WIRE_Y(0)}, + {PIP_X(id_W241) - 1., HOP2Y(1), PIP_X(id_W242) - 2., HOP2Y(1)}, + {PIP_X(id_W242) - 2., HOP2Y(1), PIP_X(id_W242) - 2., WIRE_Y(0)}}}, + {id_E24_loop0, + {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, + {PIP_X(id_E240), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W241), HOP2Y(3)}, + {PIP_X(id_W241), HOP2Y(3), PIP_X(id_W241), WIRE_Y(0)}, + {PIP_X(id_W241), HOP2Y(1), PIP_X(id_W242) - 1., HOP2Y(1)}, + {PIP_X(id_W242) - 1., HOP2Y(1), PIP_X(id_W242) - 1., WIRE_Y(0)}}}, + {id_W24_loop0, + {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, + {PIP_X(id_W240), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E241), HOP2Y(2)}, + {PIP_X(id_E241), HOP2Y(2), PIP_X(id_E241), WIRE_Y(0)}, + {PIP_X(id_E241), HOP2Y(0), PIP_X(id_E242) + 1., HOP2Y(0)}, + {PIP_X(id_E242) + 1., HOP2Y(0), PIP_X(id_E242) + 1., WIRE_Y(0)}}}, + {id_E24_loop1, + {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, + {PIP_X(id_E240), HOP2Y(2), PIP_X(id_E241) + 1., HOP2Y(2)}, + {PIP_X(id_E241) + 1., HOP2Y(2), PIP_X(id_E241) + 1., WIRE_Y(0)}, + {PIP_X(id_E241) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W242) + 1., HOP2Y(1)}, + {PIP_X(id_W242) + 1., HOP2Y(1), PIP_X(id_W242) + 1., WIRE_Y(0)}}}, + {id_W24_loop1, + {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, + {PIP_X(id_W240), HOP2Y(3), PIP_X(id_W241) - 1., HOP2Y(3)}, + {PIP_X(id_W241) - 1., HOP2Y(3), PIP_X(id_W241) - 1., WIRE_Y(0)}, + {PIP_X(id_W241) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E242) - 1., HOP2Y(0)}, + {PIP_X(id_E242) - 1., HOP2Y(0), PIP_X(id_E242) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 5) + {id_E25, + {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, + {PIP_X(id_E250), HOP2Y(2), PIP_X(id_E251) + 1., HOP2Y(2)}, + {PIP_X(id_E251) + 1., HOP2Y(2), PIP_X(id_E251) + 1., WIRE_Y(0)}, + {PIP_X(id_E251) + 1., HOP2Y(0), PIP_X(id_E252) + 2., HOP2Y(0)}, + {PIP_X(id_E252) + 2., HOP2Y(0), PIP_X(id_E252) + 2., WIRE_Y(0)}}}, + {id_W25, + {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, + {PIP_X(id_W250), HOP2Y(3), PIP_X(id_W251) - 1., HOP2Y(3)}, + {PIP_X(id_W251) - 1., HOP2Y(3), PIP_X(id_W251) - 1., WIRE_Y(0)}, + {PIP_X(id_W251) - 1., HOP2Y(1), PIP_X(id_W252) - 2., HOP2Y(1)}, + {PIP_X(id_W252) - 2., HOP2Y(1), PIP_X(id_W252) - 2., WIRE_Y(0)}}}, + {id_E25_loop0, + {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, + {PIP_X(id_E250), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W251), HOP2Y(3)}, + {PIP_X(id_W251), HOP2Y(3), PIP_X(id_W251), WIRE_Y(0)}, + {PIP_X(id_W251), HOP2Y(1), PIP_X(id_W252) - 1., HOP2Y(1)}, + {PIP_X(id_W252) - 1., HOP2Y(1), PIP_X(id_W252) - 1., WIRE_Y(0)}}}, + {id_W25_loop0, + {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, + {PIP_X(id_W250), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E251), HOP2Y(2)}, + {PIP_X(id_E251), HOP2Y(2), PIP_X(id_E251), WIRE_Y(0)}, + {PIP_X(id_E251), HOP2Y(0), PIP_X(id_E252) + 1., HOP2Y(0)}, + {PIP_X(id_E252) + 1., HOP2Y(0), PIP_X(id_E252) + 1., WIRE_Y(0)}}}, + {id_E25_loop1, + {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, + {PIP_X(id_E250), HOP2Y(2), PIP_X(id_E251) + 1., HOP2Y(2)}, + {PIP_X(id_E251) + 1., HOP2Y(2), PIP_X(id_E251) + 1., WIRE_Y(0)}, + {PIP_X(id_E251) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W252) + 1., HOP2Y(1)}, + {PIP_X(id_W252) + 1., HOP2Y(1), PIP_X(id_W252) + 1., WIRE_Y(0)}}}, + {id_W25_loop1, + {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, + {PIP_X(id_W250), HOP2Y(3), PIP_X(id_W251) - 1., HOP2Y(3)}, + {PIP_X(id_W251) - 1., HOP2Y(3), PIP_X(id_W251) - 1., WIRE_Y(0)}, + {PIP_X(id_W251) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E252) - 1., HOP2Y(0)}, + {PIP_X(id_E252) - 1., HOP2Y(0), PIP_X(id_E252) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 6) + {id_E26, + {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, + {PIP_X(id_E260), HOP2Y(2), PIP_X(id_E261) + 1., HOP2Y(2)}, + {PIP_X(id_E261) + 1., HOP2Y(2), PIP_X(id_E261) + 1., WIRE_Y(0)}, + {PIP_X(id_E261) + 1., HOP2Y(0), PIP_X(id_E262) + 2., HOP2Y(0)}, + {PIP_X(id_E262) + 2., HOP2Y(0), PIP_X(id_E262) + 2., WIRE_Y(0)}}}, + {id_W26, + {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, + {PIP_X(id_W260), HOP2Y(3), PIP_X(id_W261) - 1., HOP2Y(3)}, + {PIP_X(id_W261) - 1., HOP2Y(3), PIP_X(id_W261) - 1., WIRE_Y(0)}, + {PIP_X(id_W261) - 1., HOP2Y(1), PIP_X(id_W262) - 2., HOP2Y(1)}, + {PIP_X(id_W262) - 2., HOP2Y(1), PIP_X(id_W262) - 2., WIRE_Y(0)}}}, + {id_E26_loop0, + {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, + {PIP_X(id_E260), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W261), HOP2Y(3)}, + {PIP_X(id_W261), HOP2Y(3), PIP_X(id_W261), WIRE_Y(0)}, + {PIP_X(id_W261), HOP2Y(1), PIP_X(id_W262) - 1., HOP2Y(1)}, + {PIP_X(id_W262) - 1., HOP2Y(1), PIP_X(id_W262) - 1., WIRE_Y(0)}}}, + {id_W26_loop0, + {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, + {PIP_X(id_W260), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E261), HOP2Y(2)}, + {PIP_X(id_E261), HOP2Y(2), PIP_X(id_E261), WIRE_Y(0)}, + {PIP_X(id_E261), HOP2Y(0), PIP_X(id_E262) + 1., HOP2Y(0)}, + {PIP_X(id_E262) + 1., HOP2Y(0), PIP_X(id_E262) + 1., WIRE_Y(0)}}}, + {id_E26_loop1, + {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, + {PIP_X(id_E260), HOP2Y(2), PIP_X(id_E261) + 1., HOP2Y(2)}, + {PIP_X(id_E261) + 1., HOP2Y(2), PIP_X(id_E261) + 1., WIRE_Y(0)}, + {PIP_X(id_E261) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W262) + 1., HOP2Y(1)}, + {PIP_X(id_W262) + 1., HOP2Y(1), PIP_X(id_W262) + 1., WIRE_Y(0)}}}, + {id_W26_loop1, + {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, + {PIP_X(id_W260), HOP2Y(3), PIP_X(id_W261) - 1., HOP2Y(3)}, + {PIP_X(id_W261) - 1., HOP2Y(3), PIP_X(id_W261) - 1., WIRE_Y(0)}, + {PIP_X(id_W261) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E262) - 1., HOP2Y(0)}, + {PIP_X(id_E262) - 1., HOP2Y(0), PIP_X(id_E262) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 7) + {id_E27, + {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, + {PIP_X(id_E270), HOP2Y(2), PIP_X(id_E271) + 1., HOP2Y(2)}, + {PIP_X(id_E271) + 1., HOP2Y(2), PIP_X(id_E271) + 1., WIRE_Y(0)}, + {PIP_X(id_E271) + 1., HOP2Y(0), PIP_X(id_E272) + 2., HOP2Y(0)}, + {PIP_X(id_E272) + 2., HOP2Y(0), PIP_X(id_E272) + 2., WIRE_Y(0)}}}, + {id_W27, + {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, + {PIP_X(id_W270), HOP2Y(3), PIP_X(id_W271) - 1., HOP2Y(3)}, + {PIP_X(id_W271) - 1., HOP2Y(3), PIP_X(id_W271) - 1., WIRE_Y(0)}, + {PIP_X(id_W271) - 1., HOP2Y(1), PIP_X(id_W272) - 2., HOP2Y(1)}, + {PIP_X(id_W272) - 2., HOP2Y(1), PIP_X(id_W272) - 2., WIRE_Y(0)}}}, + {id_E27_loop0, + {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, + {PIP_X(id_E270), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W271), HOP2Y(3)}, + {PIP_X(id_W271), HOP2Y(3), PIP_X(id_W271), WIRE_Y(0)}, + {PIP_X(id_W271), HOP2Y(1), PIP_X(id_W272) - 1., HOP2Y(1)}, + {PIP_X(id_W272) - 1., HOP2Y(1), PIP_X(id_W272) - 1., WIRE_Y(0)}}}, + {id_W27_loop0, + {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, + {PIP_X(id_W270), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E271), HOP2Y(2)}, + {PIP_X(id_E271), HOP2Y(2), PIP_X(id_E271), WIRE_Y(0)}, + {PIP_X(id_E271), HOP2Y(0), PIP_X(id_E272) + 1., HOP2Y(0)}, + {PIP_X(id_E272) + 1., HOP2Y(0), PIP_X(id_E272) + 1., WIRE_Y(0)}}}, + {id_E27_loop1, + {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, + {PIP_X(id_E270), HOP2Y(2), PIP_X(id_E271) + 1., HOP2Y(2)}, + {PIP_X(id_E271) + 1., HOP2Y(2), PIP_X(id_E271) + 1., WIRE_Y(0)}, + {PIP_X(id_E271) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W272) + 1., HOP2Y(1)}, + {PIP_X(id_W272) + 1., HOP2Y(1), PIP_X(id_W272) + 1., WIRE_Y(0)}}}, + {id_W27_loop1, + {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, + {PIP_X(id_W270), HOP2Y(3), PIP_X(id_W271) - 1., HOP2Y(3)}, + {PIP_X(id_W271) - 1., HOP2Y(3), PIP_X(id_W271) - 1., WIRE_Y(0)}, + {PIP_X(id_W271) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E272) - 1., HOP2Y(0)}, + {PIP_X(id_E272) - 1., HOP2Y(0), PIP_X(id_E272) - 1., WIRE_Y(0)}}}, + +// clock branches +#define CLK_GBO0_Y 41.f +#define CLK_GBO1_Y 46.f +// 4 hop +#define HOP4Y_START (CLK_GBO0_Y + 10.f) +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START) + {id_E80, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, + {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W808) + 7., HOP4Y(2)}, + {PIP_X(id_W808) + 7., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E808) + 8., HOP4Y(0)}, + {PIP_X(id_E808) + 8, HOP4Y(0), PIP_X(id_E808) + 8., WIRE_Y(0)}}}, + {id_W80, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, + {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W800) - 7., HOP4Y(3)}, + {PIP_X(id_W800) - 7., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W808) - 8., HOP4Y(1)}, + {PIP_X(id_W808) - 8, HOP4Y(1), PIP_X(id_W808) - 8., WIRE_Y(0)}}}, + {id_E80_loop0, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W800) - 0., HOP4Y(15)}, + {PIP_X(id_W800) - 0., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W800) - 1., HOP4Y(13)}, + {PIP_X(id_W800) - 1., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W800) - 2., HOP4Y(11)}, + {PIP_X(id_W800) - 2., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W800) - 3., HOP4Y(9)}, + {PIP_X(id_W800) - 3., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W804) - 3., HOP4Y(9), PIP_X(id_W804) - 3., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W800) - 4., HOP4Y(7)}, + {PIP_X(id_W800) - 4., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W800) - 5., HOP4Y(5)}, + {PIP_X(id_W800) - 5., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W800) - 6., HOP4Y(3)}, + {PIP_X(id_W800) - 6., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W808) - 7., HOP4Y(1)}, + {PIP_X(id_W808) - 7, HOP4Y(1), PIP_X(id_W808) - 7., WIRE_Y(0)}}}, + {id_E80_loop1, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W800) + 1., HOP4Y(13)}, + {PIP_X(id_W800) + 1., HOP4Y(13), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W800) - 0., HOP4Y(11)}, + {PIP_X(id_W800) - 0., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W800) - 1., HOP4Y(9)}, + {PIP_X(id_W804) - 1., HOP4Y(9), PIP_X(id_W804) - 1., WIRE_Y(0)}, + {PIP_X(id_W800) - 1., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W800) - 2., HOP4Y(7)}, + {PIP_X(id_W800) - 2., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W800) - 3., HOP4Y(5)}, + {PIP_X(id_W800) - 3., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W800) - 4., HOP4Y(3)}, + {PIP_X(id_W800) - 4., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W808) - 5., HOP4Y(1)}, + {PIP_X(id_W808) - 5., HOP4Y(1), PIP_X(id_W808) - 5., WIRE_Y(0)}}}, + {id_E80_loop2, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W800) + 2., HOP4Y(11)}, + {PIP_X(id_W800) + 2., HOP4Y(11), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W800) + 1., HOP4Y(9)}, + {PIP_X(id_W804) + 1., HOP4Y(9), PIP_X(id_W804) + 1., WIRE_Y(0)}, + {PIP_X(id_W800) + 1., HOP4Y(9), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W800) + 0., HOP4Y(7)}, + {PIP_X(id_W800) + 0., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W800) - 1., HOP4Y(5)}, + {PIP_X(id_W800) - 1., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W800) - 2., HOP4Y(3)}, + {PIP_X(id_W800) - 2., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W808) - 3., HOP4Y(1)}, + {PIP_X(id_W808) - 3., HOP4Y(1), PIP_X(id_W808) - 3., WIRE_Y(0)}}}, + {id_E80_loop3, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W800) + 3., HOP4Y(9)}, + {PIP_X(id_W804) + 3., HOP4Y(9), PIP_X(id_W804) + 3., WIRE_Y(0)}, + {PIP_X(id_W800) + 3., HOP4Y(9), PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W800) + 2., HOP4Y(7)}, + {PIP_X(id_W800) + 2., HOP4Y(7), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W800) + 1., HOP4Y(5)}, + {PIP_X(id_W800) + 1., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W800) - 0., HOP4Y(3)}, + {PIP_X(id_W800) - 0., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W808) - 1., HOP4Y(1)}, + {PIP_X(id_W808) - 1., HOP4Y(1), PIP_X(id_W808) - 1., WIRE_Y(0)}}}, + {id_E80_loop4, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W800) + 4., HOP4Y(7)}, + {PIP_X(id_W800) + 4., HOP4Y(7), PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W800) + 3., HOP4Y(5)}, + {PIP_X(id_W800) + 3., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W800) + 2., HOP4Y(3)}, + {PIP_X(id_W800) + 2., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W808) + 1., HOP4Y(1)}, + {PIP_X(id_W808) + 1., HOP4Y(1), PIP_X(id_W808) + 1., WIRE_Y(0)}}}, + {id_E80_loop5, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W800) + 5., HOP4Y(5)}, + {PIP_X(id_W800) + 5., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W800) + 4., HOP4Y(3)}, + {PIP_X(id_W800) + 4., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W808) + 3., HOP4Y(1)}, + {PIP_X(id_W808) + 3., HOP4Y(1), PIP_X(id_W808) + 3., WIRE_Y(0)}}}, + {id_E80_loop6, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, + {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W800) + 6., HOP4Y(3)}, + {PIP_X(id_W800) + 6., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W808) + 5., HOP4Y(1)}, + {PIP_X(id_W808) + 5., HOP4Y(1), PIP_X(id_W808) + 5., WIRE_Y(0)}}}, + {id_E80_loop7, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, + {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W808) + 7., HOP4Y(2)}, + {PIP_X(id_W808) + 7., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W808) + 7., HOP4Y(1)}, + {PIP_X(id_W808) + 7., HOP4Y(1), PIP_X(id_W808) + 7., WIRE_Y(0)}}}, + {id_W80_loop0, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W808) + 0., HOP4Y(14)}, + {PIP_X(id_W808) + 0., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W808) + 1., HOP4Y(12)}, + {PIP_X(id_W808) + 1., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W808) + 2., HOP4Y(10)}, + {PIP_X(id_W808) + 2., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W808) + 3., HOP4Y(8)}, + {PIP_X(id_W808) + 3., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E804) + 3., HOP4Y(8), PIP_X(id_E804) + 3., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W808) + 4., HOP4Y(6)}, + {PIP_X(id_W808) + 4., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W808) + 5., HOP4Y(4)}, + {PIP_X(id_W808) + 5., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W808) + 6., HOP4Y(2)}, + {PIP_X(id_W808) + 6., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E808) + 7., HOP4Y(0)}, + {PIP_X(id_E808) + 7., HOP4Y(0), PIP_X(id_E808) + 7., WIRE_Y(0)}}}, + {id_W80_loop1, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W808) - 1., HOP4Y(12)}, + {PIP_X(id_W808) - 1., HOP4Y(12), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W808) + 0., HOP4Y(10)}, + {PIP_X(id_W808) + 0., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W808) + 1., HOP4Y(8)}, + {PIP_X(id_W808) + 1., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E804) + 1., HOP4Y(8), PIP_X(id_E804) + 1., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W808) + 2., HOP4Y(6)}, + {PIP_X(id_W808) + 2., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W808) + 3., HOP4Y(4)}, + {PIP_X(id_W808) + 3., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W808) + 4., HOP4Y(2)}, + {PIP_X(id_W808) + 4., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E808) + 5., HOP4Y(0)}, + {PIP_X(id_E808) + 5., HOP4Y(0), PIP_X(id_E808) + 5., WIRE_Y(0)}}}, + {id_W80_loop2, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W808) - 2., HOP4Y(10)}, + {PIP_X(id_W808) - 2., HOP4Y(10), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W808) - 1., HOP4Y(8)}, + {PIP_X(id_W808) - 1., HOP4Y(8), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E804) - 1., HOP4Y(8), PIP_X(id_E804) - 1., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W808) + 0., HOP4Y(6)}, + {PIP_X(id_W808) + 0., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W808) + 1., HOP4Y(4)}, + {PIP_X(id_W808) + 1., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W808) + 2., HOP4Y(2)}, + {PIP_X(id_W808) + 2., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E808) + 3., HOP4Y(0)}, + {PIP_X(id_E808) + 3., HOP4Y(0), PIP_X(id_E808) + 3., WIRE_Y(0)}}}, + {id_W80_loop3, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W808) - 3., HOP4Y(8)}, + {PIP_X(id_W808) - 3., HOP4Y(8), PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E804) - 3., HOP4Y(8), PIP_X(id_E804) - 3., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W808) - 2., HOP4Y(6)}, + {PIP_X(id_W808) - 2., HOP4Y(6), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W808) - 1., HOP4Y(4)}, + {PIP_X(id_W808) - 1., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W808) + 0., HOP4Y(2)}, + {PIP_X(id_W808) + 0., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E808) + 1., HOP4Y(0)}, + {PIP_X(id_E808) + 1., HOP4Y(0), PIP_X(id_E808) + 1., WIRE_Y(0)}}}, + {id_W80_loop4, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W804) - 4., HOP4Y(6), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W808) - 4., HOP4Y(6)}, + {PIP_X(id_W808) - 4., HOP4Y(6), PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W808) - 3., HOP4Y(4)}, + {PIP_X(id_W808) - 3., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W808) - 2., HOP4Y(2)}, + {PIP_X(id_W808) - 2., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E808) - 1., HOP4Y(0)}, + {PIP_X(id_E808) - 1., HOP4Y(0), PIP_X(id_E808) - 1., WIRE_Y(0)}}}, + {id_W80_loop5, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W808) - 5., HOP4Y(4)}, + {PIP_X(id_W808) - 5., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W808) - 4., HOP4Y(2)}, + {PIP_X(id_W808) - 4., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E808) - 3., HOP4Y(0)}, + {PIP_X(id_E808) - 3., HOP4Y(0), PIP_X(id_E808) - 3., WIRE_Y(0)}}}, + {id_W80_loop6, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, + {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W808) - 6., HOP4Y(2)}, + {PIP_X(id_W808) - 6., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E808) - 5., HOP4Y(0)}, + {PIP_X(id_E808) - 5., HOP4Y(0), PIP_X(id_E808) - 5., WIRE_Y(0)}}}, + {id_W80_loop7, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, + {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W800) - 7., HOP4Y(3)}, + {PIP_X(id_W800) - 7., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E808) - 7., HOP4Y(0)}, + {PIP_X(id_E808) - 7., HOP4Y(0), PIP_X(id_E808) - 7., WIRE_Y(0)}}}, + +#undef HOP4Y +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f) + {id_E81, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, + {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W818) + 7., HOP4Y(2)}, + {PIP_X(id_W818) + 7., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E818) + 8., HOP4Y(0)}, + {PIP_X(id_E818) + 8, HOP4Y(0), PIP_X(id_E818) + 8., WIRE_Y(0)}}}, + {id_W81, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, + {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W810) - 7., HOP4Y(3)}, + {PIP_X(id_W810) - 7., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W818) - 8., HOP4Y(1)}, + {PIP_X(id_W818) - 8, HOP4Y(1), PIP_X(id_W818) - 8., WIRE_Y(0)}}}, + {id_E81_loop0, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W810) - 0., HOP4Y(15)}, + {PIP_X(id_W810) - 0., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W810) - 1., HOP4Y(13)}, + {PIP_X(id_W810) - 1., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W810) - 2., HOP4Y(11)}, + {PIP_X(id_W810) - 2., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W810) - 3., HOP4Y(9)}, + {PIP_X(id_W810) - 3., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W814) - 3., HOP4Y(9), PIP_X(id_W814) - 3., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W810) - 4., HOP4Y(7)}, + {PIP_X(id_W810) - 4., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W810) - 5., HOP4Y(5)}, + {PIP_X(id_W810) - 5., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W810) - 6., HOP4Y(3)}, + {PIP_X(id_W810) - 6., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W818) - 7., HOP4Y(1)}, + {PIP_X(id_W818) - 7, HOP4Y(1), PIP_X(id_W818) - 7., WIRE_Y(0)}}}, + {id_E81_loop1, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W810) + 1., HOP4Y(13)}, + {PIP_X(id_W810) + 1., HOP4Y(13), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W810) - 0., HOP4Y(11)}, + {PIP_X(id_W810) - 0., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W810) - 1., HOP4Y(9)}, + {PIP_X(id_W814) - 1., HOP4Y(9), PIP_X(id_W814) - 1., WIRE_Y(0)}, + {PIP_X(id_W810) - 1., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W810) - 2., HOP4Y(7)}, + {PIP_X(id_W810) - 2., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W810) - 3., HOP4Y(5)}, + {PIP_X(id_W810) - 3., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W810) - 4., HOP4Y(3)}, + {PIP_X(id_W810) - 4., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W818) - 5., HOP4Y(1)}, + {PIP_X(id_W818) - 5., HOP4Y(1), PIP_X(id_W818) - 5., WIRE_Y(0)}}}, + {id_E81_loop2, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W810) + 2., HOP4Y(11)}, + {PIP_X(id_W810) + 2., HOP4Y(11), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W810) + 1., HOP4Y(9)}, + {PIP_X(id_W814) + 1., HOP4Y(9), PIP_X(id_W814) + 1., WIRE_Y(0)}, + {PIP_X(id_W810) + 1., HOP4Y(9), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W810) + 0., HOP4Y(7)}, + {PIP_X(id_W810) + 0., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W810) - 1., HOP4Y(5)}, + {PIP_X(id_W810) - 1., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W810) - 2., HOP4Y(3)}, + {PIP_X(id_W810) - 2., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W818) - 3., HOP4Y(1)}, + {PIP_X(id_W818) - 3., HOP4Y(1), PIP_X(id_W818) - 3., WIRE_Y(0)}}}, + {id_E81_loop3, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W810) + 3., HOP4Y(9)}, + {PIP_X(id_W814) + 3., HOP4Y(9), PIP_X(id_W814) + 3., WIRE_Y(0)}, + {PIP_X(id_W810) + 3., HOP4Y(9), PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W810) + 2., HOP4Y(7)}, + {PIP_X(id_W810) + 2., HOP4Y(7), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W810) + 1., HOP4Y(5)}, + {PIP_X(id_W810) + 1., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W810) - 0., HOP4Y(3)}, + {PIP_X(id_W810) - 0., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W818) - 1., HOP4Y(1)}, + {PIP_X(id_W818) - 1., HOP4Y(1), PIP_X(id_W818) - 1., WIRE_Y(0)}}}, + {id_E81_loop4, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W810) + 4., HOP4Y(7)}, + {PIP_X(id_W810) + 4., HOP4Y(7), PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W810) + 3., HOP4Y(5)}, + {PIP_X(id_W810) + 3., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W810) + 2., HOP4Y(3)}, + {PIP_X(id_W810) + 2., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W818) + 1., HOP4Y(1)}, + {PIP_X(id_W818) + 1., HOP4Y(1), PIP_X(id_W818) + 1., WIRE_Y(0)}}}, + {id_E81_loop5, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W810) + 5., HOP4Y(5)}, + {PIP_X(id_W810) + 5., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W810) + 4., HOP4Y(3)}, + {PIP_X(id_W810) + 4., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W818) + 3., HOP4Y(1)}, + {PIP_X(id_W818) + 3., HOP4Y(1), PIP_X(id_W818) + 3., WIRE_Y(0)}}}, + {id_E81_loop6, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, + {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W810) + 6., HOP4Y(3)}, + {PIP_X(id_W810) + 6., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W818) + 5., HOP4Y(1)}, + {PIP_X(id_W818) + 5., HOP4Y(1), PIP_X(id_W818) + 5., WIRE_Y(0)}}}, + {id_E81_loop7, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, + {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W818) + 7., HOP4Y(2)}, + {PIP_X(id_W818) + 7., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W818) + 7., HOP4Y(1)}, + {PIP_X(id_W818) + 7., HOP4Y(1), PIP_X(id_W818) + 7., WIRE_Y(0)}}}, + {id_W81_loop0, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W818) + 0., HOP4Y(14)}, + {PIP_X(id_W818) + 0., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W818) + 1., HOP4Y(12)}, + {PIP_X(id_W818) + 1., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W818) + 2., HOP4Y(10)}, + {PIP_X(id_W818) + 2., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W818) + 3., HOP4Y(8)}, + {PIP_X(id_W818) + 3., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E814) + 3., HOP4Y(8), PIP_X(id_E814) + 3., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W818) + 4., HOP4Y(6)}, + {PIP_X(id_W818) + 4., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W818) + 5., HOP4Y(4)}, + {PIP_X(id_W818) + 5., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W818) + 6., HOP4Y(2)}, + {PIP_X(id_W818) + 6., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E818) + 7., HOP4Y(0)}, + {PIP_X(id_E818) + 7., HOP4Y(0), PIP_X(id_E818) + 7., WIRE_Y(0)}}}, + {id_W81_loop1, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W818) - 1., HOP4Y(12)}, + {PIP_X(id_W818) - 1., HOP4Y(12), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W818) + 0., HOP4Y(10)}, + {PIP_X(id_W818) + 0., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W818) + 1., HOP4Y(8)}, + {PIP_X(id_W818) + 1., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E814) + 1., HOP4Y(8), PIP_X(id_E814) + 1., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W818) + 2., HOP4Y(6)}, + {PIP_X(id_W818) + 2., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W818) + 3., HOP4Y(4)}, + {PIP_X(id_W818) + 3., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W818) + 4., HOP4Y(2)}, + {PIP_X(id_W818) + 4., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E818) + 5., HOP4Y(0)}, + {PIP_X(id_E818) + 5., HOP4Y(0), PIP_X(id_E818) + 5., WIRE_Y(0)}}}, + {id_W81_loop2, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W818) - 2., HOP4Y(10)}, + {PIP_X(id_W818) - 2., HOP4Y(10), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W818) - 1., HOP4Y(8)}, + {PIP_X(id_W818) - 1., HOP4Y(8), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E814) - 1., HOP4Y(8), PIP_X(id_E814) - 1., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W818) + 0., HOP4Y(6)}, + {PIP_X(id_W818) + 0., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W818) + 1., HOP4Y(4)}, + {PIP_X(id_W818) + 1., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W818) + 2., HOP4Y(2)}, + {PIP_X(id_W818) + 2., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E818) + 3., HOP4Y(0)}, + {PIP_X(id_E818) + 3., HOP4Y(0), PIP_X(id_E818) + 3., WIRE_Y(0)}}}, + {id_W81_loop3, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W818) - 3., HOP4Y(8)}, + {PIP_X(id_W818) - 3., HOP4Y(8), PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E814) - 3., HOP4Y(8), PIP_X(id_E814) - 3., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W818) - 2., HOP4Y(6)}, + {PIP_X(id_W818) - 2., HOP4Y(6), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W818) - 1., HOP4Y(4)}, + {PIP_X(id_W818) - 1., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W818) + 0., HOP4Y(2)}, + {PIP_X(id_W818) + 0., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E818) + 1., HOP4Y(0)}, + {PIP_X(id_E818) + 1., HOP4Y(0), PIP_X(id_E818) + 1., WIRE_Y(0)}}}, + {id_W81_loop4, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W814) - 4., HOP4Y(6), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W818) - 4., HOP4Y(6)}, + {PIP_X(id_W818) - 4., HOP4Y(6), PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W818) - 3., HOP4Y(4)}, + {PIP_X(id_W818) - 3., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W818) - 2., HOP4Y(2)}, + {PIP_X(id_W818) - 2., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E818) - 1., HOP4Y(0)}, + {PIP_X(id_E818) - 1., HOP4Y(0), PIP_X(id_E818) - 1., WIRE_Y(0)}}}, + {id_W81_loop5, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W818) - 5., HOP4Y(4)}, + {PIP_X(id_W818) - 5., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W818) - 4., HOP4Y(2)}, + {PIP_X(id_W818) - 4., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E818) - 3., HOP4Y(0)}, + {PIP_X(id_E818) - 3., HOP4Y(0), PIP_X(id_E818) - 3., WIRE_Y(0)}}}, + {id_W81_loop6, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, + {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W818) - 6., HOP4Y(2)}, + {PIP_X(id_W818) - 6., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E818) - 5., HOP4Y(0)}, + {PIP_X(id_E818) - 5., HOP4Y(0), PIP_X(id_E818) - 5., WIRE_Y(0)}}}, + {id_W81_loop7, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, + {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W810) - 7., HOP4Y(3)}, + {PIP_X(id_W810) - 7., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E818) - 7., HOP4Y(0)}, + {PIP_X(id_E818) - 7., HOP4Y(0), PIP_X(id_E818) - 7., WIRE_Y(0)}}}, + +#undef HOP4Y +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f + 18.f) + {id_E82, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, + {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W828) + 7., HOP4Y(2)}, + {PIP_X(id_W828) + 7., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E828) + 8., HOP4Y(0)}, + {PIP_X(id_E828) + 8, HOP4Y(0), PIP_X(id_E828) + 8., WIRE_Y(0)}}}, + {id_W82, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, + {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W820) - 7., HOP4Y(3)}, + {PIP_X(id_W820) - 7., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W828) - 8., HOP4Y(1)}, + {PIP_X(id_W828) - 8, HOP4Y(1), PIP_X(id_W828) - 8., WIRE_Y(0)}}}, + {id_E82_loop0, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W820) - 0., HOP4Y(15)}, + {PIP_X(id_W820) - 0., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W820) - 1., HOP4Y(13)}, + {PIP_X(id_W820) - 1., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W820) - 2., HOP4Y(11)}, + {PIP_X(id_W820) - 2., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W820) - 3., HOP4Y(9)}, + {PIP_X(id_W820) - 3., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W824) - 3., HOP4Y(9), PIP_X(id_W824) - 3., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W820) - 4., HOP4Y(7)}, + {PIP_X(id_W820) - 4., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W820) - 5., HOP4Y(5)}, + {PIP_X(id_W820) - 5., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W820) - 6., HOP4Y(3)}, + {PIP_X(id_W820) - 6., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W828) - 7., HOP4Y(1)}, + {PIP_X(id_W828) - 7, HOP4Y(1), PIP_X(id_W828) - 7., WIRE_Y(0)}}}, + {id_E82_loop1, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W820) + 1., HOP4Y(13)}, + {PIP_X(id_W820) + 1., HOP4Y(13), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W820) - 0., HOP4Y(11)}, + {PIP_X(id_W820) - 0., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W820) - 1., HOP4Y(9)}, + {PIP_X(id_W824) - 1., HOP4Y(9), PIP_X(id_W824) - 1., WIRE_Y(0)}, + {PIP_X(id_W820) - 1., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W820) - 2., HOP4Y(7)}, + {PIP_X(id_W820) - 2., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W820) - 3., HOP4Y(5)}, + {PIP_X(id_W820) - 3., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W820) - 4., HOP4Y(3)}, + {PIP_X(id_W820) - 4., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W828) - 5., HOP4Y(1)}, + {PIP_X(id_W828) - 5., HOP4Y(1), PIP_X(id_W828) - 5., WIRE_Y(0)}}}, + {id_E82_loop2, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W820) + 2., HOP4Y(11)}, + {PIP_X(id_W820) + 2., HOP4Y(11), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W820) + 1., HOP4Y(9)}, + {PIP_X(id_W824) + 1., HOP4Y(9), PIP_X(id_W824) + 1., WIRE_Y(0)}, + {PIP_X(id_W820) + 1., HOP4Y(9), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W820) + 0., HOP4Y(7)}, + {PIP_X(id_W820) + 0., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W820) - 1., HOP4Y(5)}, + {PIP_X(id_W820) - 1., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W820) - 2., HOP4Y(3)}, + {PIP_X(id_W820) - 2., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W828) - 3., HOP4Y(1)}, + {PIP_X(id_W828) - 3., HOP4Y(1), PIP_X(id_W828) - 3., WIRE_Y(0)}}}, + {id_E82_loop3, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W820) + 3., HOP4Y(9)}, + {PIP_X(id_W824) + 3., HOP4Y(9), PIP_X(id_W824) + 3., WIRE_Y(0)}, + {PIP_X(id_W820) + 3., HOP4Y(9), PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W820) + 2., HOP4Y(7)}, + {PIP_X(id_W820) + 2., HOP4Y(7), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W820) + 1., HOP4Y(5)}, + {PIP_X(id_W820) + 1., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W820) - 0., HOP4Y(3)}, + {PIP_X(id_W820) - 0., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W828) - 1., HOP4Y(1)}, + {PIP_X(id_W828) - 1., HOP4Y(1), PIP_X(id_W828) - 1., WIRE_Y(0)}}}, + {id_E82_loop4, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W820) + 4., HOP4Y(7)}, + {PIP_X(id_W820) + 4., HOP4Y(7), PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W820) + 3., HOP4Y(5)}, + {PIP_X(id_W820) + 3., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W820) + 2., HOP4Y(3)}, + {PIP_X(id_W820) + 2., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W828) + 1., HOP4Y(1)}, + {PIP_X(id_W828) + 1., HOP4Y(1), PIP_X(id_W828) + 1., WIRE_Y(0)}}}, + {id_E82_loop5, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W820) + 5., HOP4Y(5)}, + {PIP_X(id_W820) + 5., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W820) + 4., HOP4Y(3)}, + {PIP_X(id_W820) + 4., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W828) + 3., HOP4Y(1)}, + {PIP_X(id_W828) + 3., HOP4Y(1), PIP_X(id_W828) + 3., WIRE_Y(0)}}}, + {id_E82_loop6, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, + {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W820) + 6., HOP4Y(3)}, + {PIP_X(id_W820) + 6., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W828) + 5., HOP4Y(1)}, + {PIP_X(id_W828) + 5., HOP4Y(1), PIP_X(id_W828) + 5., WIRE_Y(0)}}}, + {id_E82_loop7, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, + {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W828) + 7., HOP4Y(2)}, + {PIP_X(id_W828) + 7., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W828) + 7., HOP4Y(1)}, + {PIP_X(id_W828) + 7., HOP4Y(1), PIP_X(id_W828) + 7., WIRE_Y(0)}}}, + {id_W82_loop0, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W828) + 0., HOP4Y(14)}, + {PIP_X(id_W828) + 0., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W828) + 1., HOP4Y(12)}, + {PIP_X(id_W828) + 1., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W828) + 2., HOP4Y(10)}, + {PIP_X(id_W828) + 2., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W828) + 3., HOP4Y(8)}, + {PIP_X(id_W828) + 3., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E824) + 3., HOP4Y(8), PIP_X(id_E824) + 3., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W828) + 4., HOP4Y(6)}, + {PIP_X(id_W828) + 4., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W828) + 5., HOP4Y(4)}, + {PIP_X(id_W828) + 5., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W828) + 6., HOP4Y(2)}, + {PIP_X(id_W828) + 6., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E828) + 7., HOP4Y(0)}, + {PIP_X(id_E828) + 7., HOP4Y(0), PIP_X(id_E828) + 7., WIRE_Y(0)}}}, + {id_W82_loop1, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W828) - 1., HOP4Y(12)}, + {PIP_X(id_W828) - 1., HOP4Y(12), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W828) + 0., HOP4Y(10)}, + {PIP_X(id_W828) + 0., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W828) + 1., HOP4Y(8)}, + {PIP_X(id_W828) + 1., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E824) + 1., HOP4Y(8), PIP_X(id_E824) + 1., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W828) + 2., HOP4Y(6)}, + {PIP_X(id_W828) + 2., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W828) + 3., HOP4Y(4)}, + {PIP_X(id_W828) + 3., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W828) + 4., HOP4Y(2)}, + {PIP_X(id_W828) + 4., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E828) + 5., HOP4Y(0)}, + {PIP_X(id_E828) + 5., HOP4Y(0), PIP_X(id_E828) + 5., WIRE_Y(0)}}}, + {id_W82_loop2, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W828) - 2., HOP4Y(10)}, + {PIP_X(id_W828) - 2., HOP4Y(10), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W828) - 1., HOP4Y(8)}, + {PIP_X(id_W828) - 1., HOP4Y(8), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E824) - 1., HOP4Y(8), PIP_X(id_E824) - 1., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W828) + 0., HOP4Y(6)}, + {PIP_X(id_W828) + 0., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W828) + 1., HOP4Y(4)}, + {PIP_X(id_W828) + 1., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W828) + 2., HOP4Y(2)}, + {PIP_X(id_W828) + 2., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E828) + 3., HOP4Y(0)}, + {PIP_X(id_E828) + 3., HOP4Y(0), PIP_X(id_E828) + 3., WIRE_Y(0)}}}, + {id_W82_loop3, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W828) - 3., HOP4Y(8)}, + {PIP_X(id_W828) - 3., HOP4Y(8), PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E824) - 3., HOP4Y(8), PIP_X(id_E824) - 3., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W828) - 2., HOP4Y(6)}, + {PIP_X(id_W828) - 2., HOP4Y(6), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W828) - 1., HOP4Y(4)}, + {PIP_X(id_W828) - 1., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W828) + 0., HOP4Y(2)}, + {PIP_X(id_W828) + 0., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E828) + 1., HOP4Y(0)}, + {PIP_X(id_E828) + 1., HOP4Y(0), PIP_X(id_E828) + 1., WIRE_Y(0)}}}, + {id_W82_loop4, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W824) - 4., HOP4Y(6), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W828) - 4., HOP4Y(6)}, + {PIP_X(id_W828) - 4., HOP4Y(6), PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W828) - 3., HOP4Y(4)}, + {PIP_X(id_W828) - 3., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W828) - 2., HOP4Y(2)}, + {PIP_X(id_W828) - 2., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E828) - 1., HOP4Y(0)}, + {PIP_X(id_E828) - 1., HOP4Y(0), PIP_X(id_E828) - 1., WIRE_Y(0)}}}, + {id_W82_loop5, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W828) - 5., HOP4Y(4)}, + {PIP_X(id_W828) - 5., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W828) - 4., HOP4Y(2)}, + {PIP_X(id_W828) - 4., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E828) - 3., HOP4Y(0)}, + {PIP_X(id_E828) - 3., HOP4Y(0), PIP_X(id_E828) - 3., WIRE_Y(0)}}}, + {id_W82_loop6, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, + {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W828) - 6., HOP4Y(2)}, + {PIP_X(id_W828) - 6., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E828) - 5., HOP4Y(0)}, + {PIP_X(id_E828) - 5., HOP4Y(0), PIP_X(id_E828) - 5., WIRE_Y(0)}}}, + {id_W82_loop7, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, + {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W820) - 7., HOP4Y(3)}, + {PIP_X(id_W820) - 7., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E828) - 7., HOP4Y(0)}, + {PIP_X(id_E828) - 7., HOP4Y(0), PIP_X(id_E828) - 7., WIRE_Y(0)}}}, + +#undef HOP4Y +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f + 18.f + 18.f) + {id_E83, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, + {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W838) + 7., HOP4Y(2)}, + {PIP_X(id_W838) + 7., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E838) + 8., HOP4Y(0)}, + {PIP_X(id_E838) + 8, HOP4Y(0), PIP_X(id_E838) + 8., WIRE_Y(0)}}}, + {id_W83, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, + {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W830) - 7., HOP4Y(3)}, + {PIP_X(id_W830) - 7., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W838) - 8., HOP4Y(1)}, + {PIP_X(id_W838) - 8, HOP4Y(1), PIP_X(id_W838) - 8., WIRE_Y(0)}}}, + {id_E83_loop0, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W830) - 0., HOP4Y(15)}, + {PIP_X(id_W830) - 0., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W830) - 1., HOP4Y(13)}, + {PIP_X(id_W830) - 1., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W830) - 2., HOP4Y(11)}, + {PIP_X(id_W830) - 2., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W830) - 3., HOP4Y(9)}, + {PIP_X(id_W830) - 3., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W834) - 3., HOP4Y(9), PIP_X(id_W834) - 3., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W830) - 4., HOP4Y(7)}, + {PIP_X(id_W830) - 4., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W830) - 5., HOP4Y(5)}, + {PIP_X(id_W830) - 5., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W830) - 6., HOP4Y(3)}, + {PIP_X(id_W830) - 6., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W838) - 7., HOP4Y(1)}, + {PIP_X(id_W838) - 7, HOP4Y(1), PIP_X(id_W838) - 7., WIRE_Y(0)}}}, + {id_E83_loop1, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W830) + 1., HOP4Y(13)}, + {PIP_X(id_W830) + 1., HOP4Y(13), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W830) - 0., HOP4Y(11)}, + {PIP_X(id_W830) - 0., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W830) - 1., HOP4Y(9)}, + {PIP_X(id_W834) - 1., HOP4Y(9), PIP_X(id_W834) - 1., WIRE_Y(0)}, + {PIP_X(id_W830) - 1., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W830) - 2., HOP4Y(7)}, + {PIP_X(id_W830) - 2., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W830) - 3., HOP4Y(5)}, + {PIP_X(id_W830) - 3., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W830) - 4., HOP4Y(3)}, + {PIP_X(id_W830) - 4., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W838) - 5., HOP4Y(1)}, + {PIP_X(id_W838) - 5., HOP4Y(1), PIP_X(id_W838) - 5., WIRE_Y(0)}}}, + {id_E83_loop2, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W830) + 2., HOP4Y(11)}, + {PIP_X(id_W830) + 2., HOP4Y(11), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W830) + 1., HOP4Y(9)}, + {PIP_X(id_W834) + 1., HOP4Y(9), PIP_X(id_W834) + 1., WIRE_Y(0)}, + {PIP_X(id_W830) + 1., HOP4Y(9), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W830) + 0., HOP4Y(7)}, + {PIP_X(id_W830) + 0., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W830) - 1., HOP4Y(5)}, + {PIP_X(id_W830) - 1., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W830) - 2., HOP4Y(3)}, + {PIP_X(id_W830) - 2., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W838) - 3., HOP4Y(1)}, + {PIP_X(id_W838) - 3., HOP4Y(1), PIP_X(id_W838) - 3., WIRE_Y(0)}}}, + {id_E83_loop3, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W830) + 3., HOP4Y(9)}, + {PIP_X(id_W834) + 3., HOP4Y(9), PIP_X(id_W834) + 3., WIRE_Y(0)}, + {PIP_X(id_W830) + 3., HOP4Y(9), PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W830) + 2., HOP4Y(7)}, + {PIP_X(id_W830) + 2., HOP4Y(7), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W830) + 1., HOP4Y(5)}, + {PIP_X(id_W830) + 1., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W830) - 0., HOP4Y(3)}, + {PIP_X(id_W830) - 0., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W838) - 1., HOP4Y(1)}, + {PIP_X(id_W838) - 1., HOP4Y(1), PIP_X(id_W838) - 1., WIRE_Y(0)}}}, + {id_E83_loop4, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W830) + 4., HOP4Y(7)}, + {PIP_X(id_W830) + 4., HOP4Y(7), PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W830) + 3., HOP4Y(5)}, + {PIP_X(id_W830) + 3., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W830) + 2., HOP4Y(3)}, + {PIP_X(id_W830) + 2., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W838) + 1., HOP4Y(1)}, + {PIP_X(id_W838) + 1., HOP4Y(1), PIP_X(id_W838) + 1., WIRE_Y(0)}}}, + {id_E83_loop5, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W830) + 5., HOP4Y(5)}, + {PIP_X(id_W830) + 5., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W830) + 4., HOP4Y(3)}, + {PIP_X(id_W830) + 4., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W838) + 3., HOP4Y(1)}, + {PIP_X(id_W838) + 3., HOP4Y(1), PIP_X(id_W838) + 3., WIRE_Y(0)}}}, + {id_E83_loop6, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, + {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W830) + 6., HOP4Y(3)}, + {PIP_X(id_W830) + 6., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W838) + 5., HOP4Y(1)}, + {PIP_X(id_W838) + 5., HOP4Y(1), PIP_X(id_W838) + 5., WIRE_Y(0)}}}, + {id_E83_loop7, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, + {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W838) + 7., HOP4Y(2)}, + {PIP_X(id_W838) + 7., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W838) + 7., HOP4Y(1)}, + {PIP_X(id_W838) + 7., HOP4Y(1), PIP_X(id_W838) + 7., WIRE_Y(0)}}}, + {id_W83_loop0, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W838) + 0., HOP4Y(14)}, + {PIP_X(id_W838) + 0., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W838) + 1., HOP4Y(12)}, + {PIP_X(id_W838) + 1., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W838) + 2., HOP4Y(10)}, + {PIP_X(id_W838) + 2., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W838) + 3., HOP4Y(8)}, + {PIP_X(id_W838) + 3., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E834) + 3., HOP4Y(8), PIP_X(id_E834) + 3., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W838) + 4., HOP4Y(6)}, + {PIP_X(id_W838) + 4., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W838) + 5., HOP4Y(4)}, + {PIP_X(id_W838) + 5., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W838) + 6., HOP4Y(2)}, + {PIP_X(id_W838) + 6., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E838) + 7., HOP4Y(0)}, + {PIP_X(id_E838) + 7., HOP4Y(0), PIP_X(id_E838) + 7., WIRE_Y(0)}}}, + {id_W83_loop1, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W838) - 1., HOP4Y(12)}, + {PIP_X(id_W838) - 1., HOP4Y(12), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W838) + 0., HOP4Y(10)}, + {PIP_X(id_W838) + 0., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W838) + 1., HOP4Y(8)}, + {PIP_X(id_W838) + 1., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E834) + 1., HOP4Y(8), PIP_X(id_E834) + 1., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W838) + 2., HOP4Y(6)}, + {PIP_X(id_W838) + 2., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W838) + 3., HOP4Y(4)}, + {PIP_X(id_W838) + 3., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W838) + 4., HOP4Y(2)}, + {PIP_X(id_W838) + 4., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E838) + 5., HOP4Y(0)}, + {PIP_X(id_E838) + 5., HOP4Y(0), PIP_X(id_E838) + 5., WIRE_Y(0)}}}, + {id_W83_loop2, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W838) - 2., HOP4Y(10)}, + {PIP_X(id_W838) - 2., HOP4Y(10), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W838) - 1., HOP4Y(8)}, + {PIP_X(id_W838) - 1., HOP4Y(8), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E834) - 1., HOP4Y(8), PIP_X(id_E834) - 1., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W838) + 0., HOP4Y(6)}, + {PIP_X(id_W838) + 0., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W838) + 1., HOP4Y(4)}, + {PIP_X(id_W838) + 1., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W838) + 2., HOP4Y(2)}, + {PIP_X(id_W838) + 2., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E838) + 3., HOP4Y(0)}, + {PIP_X(id_E838) + 3., HOP4Y(0), PIP_X(id_E838) + 3., WIRE_Y(0)}}}, + {id_W83_loop3, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W838) - 3., HOP4Y(8)}, + {PIP_X(id_W838) - 3., HOP4Y(8), PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E834) - 3., HOP4Y(8), PIP_X(id_E834) - 3., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W838) - 2., HOP4Y(6)}, + {PIP_X(id_W838) - 2., HOP4Y(6), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W838) - 1., HOP4Y(4)}, + {PIP_X(id_W838) - 1., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W838) + 0., HOP4Y(2)}, + {PIP_X(id_W838) + 0., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E838) + 1., HOP4Y(0)}, + {PIP_X(id_E838) + 1., HOP4Y(0), PIP_X(id_E838) + 1., WIRE_Y(0)}}}, + {id_W83_loop4, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W834) - 4., HOP4Y(6), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W838) - 4., HOP4Y(6)}, + {PIP_X(id_W838) - 4., HOP4Y(6), PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W838) - 3., HOP4Y(4)}, + {PIP_X(id_W838) - 3., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W838) - 2., HOP4Y(2)}, + {PIP_X(id_W838) - 2., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E838) - 1., HOP4Y(0)}, + {PIP_X(id_E838) - 1., HOP4Y(0), PIP_X(id_E838) - 1., WIRE_Y(0)}}}, + {id_W83_loop5, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W838) - 5., HOP4Y(4)}, + {PIP_X(id_W838) - 5., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W838) - 4., HOP4Y(2)}, + {PIP_X(id_W838) - 4., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E838) - 3., HOP4Y(0)}, + {PIP_X(id_E838) - 3., HOP4Y(0), PIP_X(id_E838) - 3., WIRE_Y(0)}}}, + {id_W83_loop6, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, + {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W838) - 6., HOP4Y(2)}, + {PIP_X(id_W838) - 6., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E838) - 5., HOP4Y(0)}, + {PIP_X(id_E838) - 5., HOP4Y(0), PIP_X(id_E838) - 5., WIRE_Y(0)}}}, + {id_W83_loop7, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, + {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W830) - 7., HOP4Y(3)}, + {PIP_X(id_W830) - 7., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E838) - 7., HOP4Y(0)}, + {PIP_X(id_E838) - 7., HOP4Y(0), PIP_X(id_E838) - 7., WIRE_Y(0)}}}, +}; + +void gfxCreateBelDecals(Arch *arch); +void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel); +void gfxSetIOBWireDecals(Arch *arch, BelInfo &bel); +void gfxSetIOBSWireDecals(Arch *arch, BelInfo &bel); +void gfxSetPipDefaultDecal(Arch *arch, PipInfo &pip); +void gfxSetWireDefaultDecal(Arch *arch, WireInfo &wire); +DecalXY gfxGetLutGroupDecalXY(int x, int y, int z); +DecalXY gfxGetCruGroupDecalXY(int x, int y); + +NEXTPNR_NAMESPACE_END + +#endif // GFX_H diff --git a/gowin/main.cc b/gowin/main.cc index 66de26aa34..fb9df48d20 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -61,6 +61,7 @@ std::unique_ptr GowinCommandHandler::createContext(dictgetWires()) { wireMap[std::pair(wire.location.x, wire.location.y)].push_back(wire); } +#endif +#ifdef ARCH_GOWIN + for (const auto &wire : ctx->getWires()) { + WireInfo wi = ctx->wire_info(wire); + wireMap[std::pair(wi.x, wi.y)].push_back(wire); + } #endif auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; getTreeByElementType(ElementType::WIRE) diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index b99c2bfc49..692eb27beb 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -129,40 +129,47 @@ float FPGAViewWidget::PickedElement::distance(Context *ctx, float wx, float wy) // Go over its' GraphicElements, and calculate the distance to them. std::vector distances; - std::transform(graphics.begin(), graphics.end(), std::back_inserter(distances), - [&](const GraphicElement &ge) -> float { - switch (ge.type) { - case GraphicElement::TYPE_BOX: { - // If outside the box, return unit distance to closest border. - float outside_x = -1, outside_y = -1; - if (dx < ge.x1 || dx > ge.x2) { - outside_x = std::min(std::abs(dx - ge.x1), std::abs(dx - ge.x2)); - } - if (dy < ge.y1 || dy > ge.y2) { - outside_y = std::min(std::abs(dy - ge.y1), std::abs(dy - ge.y2)); - } - if (outside_x != -1 && outside_y != -1) - return std::min(outside_x, outside_y); - - // If in box, return 0. - return 0; - } - case GraphicElement::TYPE_LINE: - case GraphicElement::TYPE_ARROW: { - // Return somewhat primitively calculated distance to segment. - // TODO(q3k): consider coming up with a better algorithm - QVector2D w(wx, wy); - QVector2D a(ge.x1, ge.y1); - QVector2D b(ge.x2, ge.y2); - float dw = a.distanceToPoint(w) + b.distanceToPoint(w); - float dab = a.distanceToPoint(b); - return std::abs(dw - dab) / dab; - } - default: - // Not close to anything. - return -1; - } - }); + std::transform( + graphics.begin(), graphics.end(), std::back_inserter(distances), [&](const GraphicElement &ge) -> float { + switch (ge.type) { + case GraphicElement::TYPE_BOX: { + // If outside the box, return unit distance to closest border. + float outside_x = -1, outside_y = -1; + if (dx < ge.x1 || dx > ge.x2) { + outside_x = std::min(std::abs(dx - ge.x1), std::abs(dx - ge.x2)); + } + if (dy < ge.y1 || dy > ge.y2) { + outside_y = std::min(std::abs(dy - ge.y1), std::abs(dy - ge.y2)); + } + if (outside_x != -1 && outside_y != -1) + return std::min(outside_x, outside_y); + + // If in box, return 0. + return 0; + } + case GraphicElement::TYPE_LOCAL_LINE: + case GraphicElement::TYPE_LOCAL_ARROW: + case GraphicElement::TYPE_LINE: + case GraphicElement::TYPE_ARROW: { + // Return somewhat primitively calculated distance to segment. + // TODO(q3k): consider coming up with a better algorithm + QVector2D w; + if (ge.type == GraphicElement::TYPE_LOCAL_LINE || ge.type == GraphicElement::TYPE_LOCAL_ARROW) { + w = QVector2D(dx, dy); + } else { + w = QVector2D(wx, wy); + } + QVector2D a(ge.x1, ge.y1); + QVector2D b(ge.x2, ge.y2); + float dw = a.distanceToPoint(w) + b.distanceToPoint(w); + float dab = a.distanceToPoint(b); + return std::abs(dw - dab) / dab; + } + default: + // Not close to anything. + return -1; + } + }); // Find smallest non -1 distance. // Find closest element. @@ -193,7 +200,8 @@ void FPGAViewWidget::renderGraphicElement(LineShaderData &out, PickQuadTree::Bou return; } - if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) { + if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW || + el.type == GraphicElement::TYPE_LOCAL_LINE || el.type == GraphicElement::TYPE_LOCAL_ARROW) { PolyLine(x + el.x1, y + el.y1, x + el.x2, y + el.y2).build(out); bb.setX0(std::min(bb.x0(), x + el.x1)); bb.setY0(std::min(bb.y0(), y + el.y1)); @@ -251,7 +259,8 @@ void FPGAViewWidget::populateQuadTree(RendererData *data, const DecalXY &decal, res = data->qt->insert(PickQuadTree::BoundingBox(x + el.x1, y + el.y1, x + el.x2, y + el.y2), element); } - if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) { + if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW || + el.type == GraphicElement::TYPE_LOCAL_LINE || el.type == GraphicElement::TYPE_LOCAL_ARROW) { // Lines are bounded by their AABB slightly enlarged. float x0 = x + el.x1; float y0 = y + el.y1; @@ -540,7 +549,7 @@ void FPGAViewWidget::renderLines(void) QMutexLocker lock(&rendererDataLock_); // If we're not re-rendering any highlights/selections, let's - // copy them over from teh current object. + // copy them over from the current object. data->gfxGrid = rendererData_->gfxGrid; if (!highlightedOrSelectedChanged) { data->gfxSelected = rendererData_->gfxSelected; diff --git a/gui/gowin/mainwindow.cc b/gui/gowin/mainwindow.cc index 9dafcef5fa..c7ba44ab60 100644 --- a/gui/gowin/mainwindow.cc +++ b/gui/gowin/mainwindow.cc @@ -19,9 +19,12 @@ #include "mainwindow.h" +#include #include #include +#include "cst.h" + static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN @@ -30,8 +33,10 @@ MainWindow::MainWindow(std::unique_ptr context, CommandHandler *handler : BaseMainWindow(std::move(context), handler, parent) { initMainResource(); - QMessageBox::critical(0, "Error - FIXME", "No GUI support for nextpnr-gowin"); - std::exit(1); + std::string title = "nextpnr-gowin - [EMPTY]"; + setWindowTitle(title.c_str()); + connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext); + createMenu(); } MainWindow::~MainWindow() {} @@ -42,8 +47,57 @@ void MainWindow::newContext(Context *ctx) setWindowTitle(title.c_str()); } -void MainWindow::createMenu() {} +void MainWindow::load_cst(std::string filename) +{ + disableActions(); + std::ifstream f(filename); + if (read_cst(ctx.get(), f)) { + log("Loading CST successful.\n"); + actionPack->setEnabled(true); + } else { + actionLoadCST->setEnabled(true); + log("Loading CST failed.\n"); + } +} + +void MainWindow::createMenu() +{ + actionLoadCST = new QAction("Open CST", this); + actionLoadCST->setIcon(QIcon(":/icons/resources/open_cst.png")); + actionLoadCST->setStatusTip("Open CST file"); + actionLoadCST->setEnabled(false); + connect(actionLoadCST, &QAction::triggered, this, &MainWindow::open_cst); + + // Add actions in menus + mainActionBar->addSeparator(); + mainActionBar->addAction(actionLoadCST); + + menuDesign->addSeparator(); + menuDesign->addAction(actionLoadCST); +} void MainWindow::new_proj() {} +void MainWindow::open_cst() +{ + QString fileName = QFileDialog::getOpenFileName(this, QString("Open CST"), QString(), QString("*.cst")); + if (!fileName.isEmpty()) { + load_cst(fileName.toStdString()); + } +} + +void MainWindow::onDisableActions() { actionLoadCST->setEnabled(false); } + +void MainWindow::onUpdateActions() +{ + if (ctx->settings.find(ctx->id("synth")) != ctx->settings.end()) { + actionLoadCST->setEnabled(true); + } + if (ctx->settings.find(ctx->id("cst")) != ctx->settings.end()) { + actionLoadCST->setEnabled(false); + } + if (ctx->settings.find(ctx->id("pack")) != ctx->settings.end()) { + actionLoadCST->setEnabled(false); + } +} NEXTPNR_NAMESPACE_END diff --git a/gui/gowin/mainwindow.h b/gui/gowin/mainwindow.h index 1e39b63f63..0d65ed1c01 100644 --- a/gui/gowin/mainwindow.h +++ b/gui/gowin/mainwindow.h @@ -35,9 +35,21 @@ class MainWindow : public BaseMainWindow public: void createMenu(); + protected: + void onDisableActions() override; + void onUpdateActions() override; + + void load_cst(std::string filename); + protected Q_SLOTS: void new_proj() override; + + void open_cst(); + void newContext(Context *ctx); + + private: + QAction *actionLoadCST; }; NEXTPNR_NAMESPACE_END diff --git a/gui/gowin/nextpnr.qrc b/gui/gowin/nextpnr.qrc index 03585ec030..921cfdd194 100644 --- a/gui/gowin/nextpnr.qrc +++ b/gui/gowin/nextpnr.qrc @@ -1,2 +1,5 @@ + + resources/open_cst.png + diff --git a/gui/gowin/resources/open_cst.png b/gui/gowin/resources/open_cst.png new file mode 100644 index 0000000000000000000000000000000000000000..23fe9cf88c151c0f8a4492be8e2de554715b1eb9 GIT binary patch literal 9399 zcmeHrc{r49*!PTmOZF(sn8-SevCde>mL-wKzRre;L1Qdg3LzA-Mr6yHJzF9>k+l$o zWC^8&QY5^iXFtC8pXWHf_rGV3sno<4Ih~4ViK~~khl)aqicH&NV_7{WB1j@q_ z{5`L%$-cQK`<<%g0KRyNKFwc7MZkgohEH#{-6w(Da|>S&%F5l`OXnvuTXF9~aMv`w zT3w|ct?(Q|`rhyFu3f<{HZmWmC+w;Dh>s-h1zxRFU0?pe6S6-Ylos-BP{!6E0->H6 zNPNeC*~yj3#DT@w*lO7c7ai=FY6YC279VuCzc*Pufq>NztH+d@KOu6uSkriCU@|^$ zXS@%xq&1p9^!DRM2R!eDY>%DTLKrl!lm}JyWE0jD2Qw<5U+QK)NME;^UodrAHWiw( zD|G&3@umjjFf1`8t&=gP+L*}RoRizj`F3(`FK6pRzcVVu0-W0ZEht*>6FuinvfD^p z;z%DU;>hNX)Ja)l|$1~vGc>-}=Y zSw8)WjSI%&H3dn=67_W-9^6pCnkY`1yA$K!r%o$pd(Mp7`pr=>Iai?jT?v z`SN43qCc$g)E8Cvtf5`S2=05j?`}oFIi>kVjDVqj&t6%0Fr3f0I9&OmBzDyrhZ-)j z=pCgKJ{oc%-!rBZaN!<_!Jv*t2M~=>S}vJMVQ|s+t}>wrF&l*CLPB< z^{`zP60|;1V|~+ka8*AxDDH{9$LSHDlIg+vV8j<$4D!+lrc(1z;qYCQK(JjCyH5T& z^JVMH5$_lR)tq!P%N4gre?a?Xx5lEoW{63~4acE0CB0ETWr-~=e!00Kq|`O;)99P^ zo1pLU)w2rt=23mzk6BBSfOofkQB~6=()qK-$Q)-KQynL%E3NNV*IpmH#LPaLi6XP* z2lvhG4Gwh%(>c4}MA=tO2iYw+C)nFHEiRKzO@4G=?x8Mie)Yv8C^V|K#AUC}wBQA1;Ywt zo@x)SOmkPPL@btEa48!aKf#CT35(9J*^D2)>r3BvSVf1?rpsh-O+|egs~4YoIFq<* zd5klmL&+)wX}od2Mo;+18xh)`fBpC+m_u!`;+Y|fXDc`f>>M|RebSaB> z9lDe|xdb+=rr~ilSbON|d6niwB~`pbck@tx?nJrD=`w!z29j4`{E`Q%Pi;Dk;i*Bp zd{DF23hs3$tNdc8AMLPO3s8tSb5?)(c7uBo%N=WLS5ZKU0>Fxtc=JX;P`Vo3GCemm zTFpLUKX0dOKD)mhGazx&tYY))hr_b-`B}1N9=!V-NnU-|1gZ6jzRT9gK*;P5 zn%tMT;;}|n-{fS1xzMV@5`^9cPBUXfwyH>uu6ZUsLVA z^$IBtp#20Ka~RX?(H;dx=>%H zMa@inWL&KqF??+FR%Ajn9dCbWZC|>o-w7a7h(&{oRp_d<$bD#!16SXLN2PS7S2aGx zpTs_RO`^#xA8$}YFy`pPMNGurFmW11U;Vu96{6MEc~@TiU8?PK+m6%a*JUP4>Fuf9 zd%WRh?7>3Gbvaxr)q%dUiJk<(b#KtI(A+z5TgbWcd!W|ixK=Y$fH~KjR~BV{oO<13 z#eVg}1@fZ^TJJYZsm7ziTnphX3$nSFd%|Nqk{E9BoSOsPfFqixXF?HD)=>GVGx?L_ zoS^pRoiLev`W|!aG-tLz4#un^QizGU-EO49X-UMl0Z}b_)xy5~w9?=$!{~st3F$IC z_1N3UGpD?5#Q3ZDw2Mo`YG|H){P0Y06^i6g?cxkY5Ap@|>LHWqL*ASt$W>auFt6cD zVN|BJ;$mx8Ql0lXn+*bunz%e$Ux0EN`xsa`vD%~j}UiHh8n(s4)CpQzhLxKAi>~@zu*f~B(B%QGSN%RW~Z!EnzcE?VN)rR`8R-1D6?*%rgaClL2QvFLV1;*wiZ#C3Gg^UznjDec{8GxR82 zGX5C8S#qP);?+%$Mlg%dGq~t`W)m+EP&E-qCj`52`>ccXYqk%GeEF=JR)*6V46t>qZbOqpvk7 zmhjk*)LXBXK2eFyJC^Z7)cCIB*?D)WA#lyJ>cY~G(NA!t&IQi=Y8N^>=r{S^jDBbT zKCG|NzgTU+x*tvBWsq@mW7DsXE2}{jN)1Y4t zr@HZwZig9}0=C}5j8zTuUB)CM!EIB|dm(nUdA>%i;XZe|k-T<4Z|GguMyhz~xd@Xb z?bJz6v?QT)G^5=RZkq0tEt}Kb#Sv_GRMN~IHZf*&RCADCv3Ptcmui2zH0pS}!4D9Fs(ZB@SNrnsHjILa=RJOgJe_r(hAM+* z1#6ilM`ju)H1lwXuc@Lz%!h9~pOcNwTzF^!5#W=FiYl0p9O)RZR<3p8Ji(YvIA;1- zZ2aBTyM^9A59REe^iN;CpD(Ex^Hf(^71ca=0B+=Tn$ zgNYtqc+tIyjtV5rw|gVdBzr5nnHULiE|`Qnx?3`WgcWUfO7 zMT2Oq)N+#h3_c2n&weTrk<~)$m1DT(vm(G*Kg)H<8GaDDai52B8g8UoUsqJjl%hxl^;| zGEI5N^ToE?4Dt{qwy1%IO3UoJj|9f{=CK3BEz?tR!Mtt*V;RU}B~7z$4eyA7RJ5WD zH4Z(Z!5yN2uv8c64}46Ir|!Tti&aphYX=={U<1;POU=OZYR3VQnY&dXrv9?}hfT@F zyRH0!JJzg;qZxbt17EJc$9QZ4XXoUv-yULF#>OV>Uuyn#t1V~q%lGGVZ86VMwN?8| z_!k$s;HDXeQw>`*7ur5AfRkPE@Tsi<#np zff%Q@Qllh|)4UFsFbmoUQ=}mlD-rN=Qr-9BY|oW6|GAurvLzF`9hwE3qEkGZbems` z3Vq&)#aAa4u;p+joFpR`&Q=b{v|W}#d|b(3Kb@Q{7SG1q!-Q(dYKCTtzlKL3P z${Lmk##aTkG#C4L9)7D^{!X25PI{cfOD{s{^?3=Dp5EHz90Kb_ltJUXFnE~&qBo`I z2LRMG1H92#cRU${!MhSX)xn>e+rS_KP91EeXaY6y*1@|G3<62`i-G4+*g$tI90%6a zU{VV}PymQ{G8z;>^zigS1gL|5;UXyGBe5(P^vi_ot`4>~F$3v%k?;!xr_bplh~F@D@jh4*!JAC*@&p}WqA^~+WOXo@q6hsEAJN;y*#hxj$bVwCX8P>dWz*+o$iq5#7w zK+sqhB?y+%@3=tWifFik(%+yAJ$=Y%Pb~fjiUKY}py0@(m2faL1`mNl;c$q8ydoZg zk;h>n$`~{jgI9pUl$BwBgD@o#C{>B}_FUk6mT#(2wWKp zgfob@Ku4axsvD&C+nN{A^W3Ac%&;OQ%W|J`uUX& zNc@ONiGLdVyWx*$f>L%iR1N}DLBXI11v!MAij*7_0fmBPf6rI;Xixu}T(Sp81f5sUwapWm0_|8NEh^na85D}Mi@>p!~w6$Agu_`mA< zkFI~kz`rv7ue$!f(Z%#vyMyG&2T}rwWuthz?_XR`*v+1lZMSzD5}_dK=jI z007LKM;8!qH;b1dq$eAi=+Q3&`I#g*5?OaPljHM z)ds2w_F`Q>io8jgiec>A3eP$LHscCE7roVaN*EKvCIX7U3A0V>gmZzxVPH_2w*6=k#Em@ROlcoXVD;AZI$p+bb=dN`ds;CmD*C+CN%Yx)RU#U9`SvjTo zZ1J2Br23&^@NNo_rD?k?dRq>)KgR>AG`+Pe@Rc>!;$5 zQrA!FjROnakJqMmS5Q(srgb>mby<^?Gh41(#$94J29lEPT`Mp8D)^6Pe8PQsJ;aq6 zYqydqn{?`9BdSM;0Nc?A=NLOxdSfj1ge>iUG1{A-hI z+tl;p`Dg0=t-KmUjoq|g2oN6VOw4weZJ_l7Xw$6;QYXP1U&T0fvsHzr$D8*-pVkD6 zLNeXKe@#EA-i{3mTXolzWsYJAIJ zdwcDBo^A^=nCK}kw^>gUZc9bfmZrf=lov#fWzGjYqq0l9q&&NRYTfYdx{CZs;4NX2 z=Ryy4*j-R-XdC|{1NHof7G^8;^bM{#$uqZ`Z7;-AQKu2-72x7_^LK(0*dC4sDU6jv>o!aQo&d`W~T{<*-I9w!MKLZB#FKB|iEYJd_GG#?hbKhob7rYd$cnoS&3z}7_XTnvpA zkEB}4wj4LD>X@VtW!s$>@i6W3yB;h`dtS;dE-;vD_sR&m2jZ|^;HwjaO4JZerQ+SfHCMc>YJ+5PU% z0xs;z1osc%70}p6T|N+9A&R_|x#NAs@0fP^2bFi-4T+x>^BCCgS4}eGK|$=D+olTpEtg(kJQcoq-Dmdo0bXWoK_&|Jm=_)cGRoS zb38E|*H4yi52V(Dy)PejO0S@?<*2Fh=OgXA?#_YZMu3IwUlKV_4Aq|kdirf?ILlUV zrtL2T3EgkM@RlRji_VSeaah{)4$}Un-PaG-w%CKFdo_V<_G8^=&}yYePhvy8^SYJV H&f)(9dXLLK literal 0 HcmV?d00001 From 676e56e5d44e09f3ff816c5efbb85e2b2ac1086b Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Tue, 25 Jan 2022 17:46:54 +0100 Subject: [PATCH 048/712] nexus: add option to modify the mult factor of the estimate delay Signed-off-by: Alessandro Comodi --- nexus/arch.cc | 7 ++++++- nexus/arch.h | 1 + nexus/main.cc | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index a7751425e3..06a901cdf3 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -601,7 +601,8 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const int dst_x = dst.tile % chip_info->width, dst_y = dst.tile / chip_info->width; int dist_x = std::abs(src_x - dst_x); int dist_y = std::abs(src_y - dst_y); - return 75 * dist_x + 75 * dist_y + 250; + + return estimate_delay_mult * (dist_x + dist_y) + 250; } delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { @@ -655,6 +656,10 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bool Arch::place() { + estimate_delay_mult = 75; + if (getCtx()->settings.count(getCtx()->id("estimate-delay-mult"))) + estimate_delay_mult = getCtx()->setting("estimate-delay-mult"); + std::string placer = str_or_default(settings, id("placer"), defaultPlacer); if (placer == "heap") { diff --git a/nexus/arch.h b/nexus/arch.h index 0bd1b62cd7..ea4e9f5bbe 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1290,6 +1290,7 @@ struct Arch : BaseArch // ------------------------------------------------- + int32_t estimate_delay_mult; delay_t estimateDelay(WireId src, WireId dst) const override; delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; delay_t getDelayEpsilon() const override { return 20; } diff --git a/nexus/main.cc b/nexus/main.cc index 9fec8d5e80..b02dfa998c 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -54,6 +54,8 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("no-pack-lutff", "disable packing (clustering) LUTs and FFs together"); specific.add_options()("carry-lutff-ratio", po::value(), "ratio of FFs to be added to carry-chain LUT clusters"); + specific.add_options()("estimate-delay-mult", po::value(), + "multiplier for the estimate delay"); return specific; } @@ -88,6 +90,8 @@ std::unique_ptr NexusCommandHandler::createContext(dictsettings[ctx->id("carry_lutff_ratio")] = ratio; } + if (vm.count("estimate-delay-mult")) + ctx->settings[ctx->id("estimate-delay-mult")] = vm["estimate-delay-mult"].as(); return ctx; } From 3042f9e792985cd204e1239d727541e566ddfcef Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 2 Feb 2022 09:24:28 +0100 Subject: [PATCH 049/712] Fixed correction of Nexus OSCA frequency constraints Signed-off-by: Maciej Kurc --- nexus/pack.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index 41f9d80664..5e9da3042f 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1988,8 +1988,8 @@ struct NexusPacker } else if (ci->type == id_OSC_CORE) { int div = int_or_default(ci->params, ctx->id("HF_CLK_DIV"), 128); const float tol = 1.15f; // OSCA has +/-15% frequency tolerance, assume the worst case. - set_period(ci, id_HFCLKOUT, delay_t(tol * (1.0e6 / 450) * (div + 1))); - set_period(ci, id_LFCLKOUT, delay_t(tol * (1.0e3 / 10))); + set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1) / tol)); + set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10) / tol)); } else if (ci->type == id_PLL_CORE) { static const std::array div{id_DIVA, id_DIVB, id_DIVC, id_DIVD, id_DIVE, id_DIVF}; static const std::array output{id_CLKOP, id_CLKOS, id_CLKOS2, From 368299d1437e4476e1ee41450194869e1ac6cf6b Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 3 Feb 2022 06:24:40 +1000 Subject: [PATCH 050/712] gowin: Rearrange the GUI constants All internal constants for describing the graphics have been moved to the .cc file. Signed-off-by: YRabbit --- gowin/gfx.cc | 4896 ++++++++++++++++++++++++++++++++++++++++++++++++++ gowin/gfx.h | 4896 -------------------------------------------------- 2 files changed, 4896 insertions(+), 4896 deletions(-) diff --git a/gowin/gfx.cc b/gowin/gfx.cc index ad2c7eadef..a851f53a69 100644 --- a/gowin/gfx.cc +++ b/gowin/gfx.cc @@ -24,6 +24,4902 @@ NEXTPNR_NAMESPACE_BEGIN +// LUTs +const float lut_w = 0.6732 - 0.6386; +const float lut_h = 0.9392 - 0.9074; +const float lut_x = 0.6386; +const float lut_y[] = {1. - 0.9392, 1. - 0.8870, 1. - 0.7864, 1. - 0.7321, + 1. - 0.6399, 1. - 0.5847, 1. - 0.5068, 1. - 0.4503}; +const float dff_w = 0.0580; +const float dff_x = 0.6821; +const float grp_lut_w = 0.1399; +const float grp_lut_h = 0.0954; +const float grp_lut_x = 0.6284; +const float grp_lut_y[] = {1. - 0.9440, 1. - 0.7915, 1. - 0.6442, 1. - 0.5101}; + +// mux +const float mux_w = 0.8134 - 0.7899; +const float mux_f = 0.9450 - 0.9358; +const float mux_h = grp_lut_h; + +const float mux2lut5_x = 0.7900; +const float mux2lut5_y[] = {grp_lut_y[0], grp_lut_y[1], grp_lut_y[2], grp_lut_y[3]}; +const float mux2lut6_x = 0.8378; +const float mux2lut6_y[] = {1. - 0.9261, 1. - 0.6205}; +const float mux2lut7_x = 0.8859; +const float mux2lut7_y = 1. - 0.7870; +const float mux2lut8_x = 0.9337; +const float mux2lut8_y = 1. - 0.8098; + +// pip +enum CruSide +{ + Top, + Bottom, + Left, + Right, + Center +}; +const float cru_x = 0.2568; +const float cru_y = 1. - 0.9783; +const float cru_w = 0.6010 - cru_x; +const float cru_h = 1. - cru_y - 0.3742; + +const float lut_A_off = 1. - 0.9107 - lut_y[0]; +const float lut_D_off = lut_h - lut_A_off; +const float lut_B_off = lut_A_off - (lut_h - lut_D_off) / 3.; +const float lut_C_off = lut_D_off + (lut_h - lut_D_off) / 3.; + +const float right_wire_dist = (grp_lut_y[1] - grp_lut_y[0] - grp_lut_h) / 11.; +const float left_wire_dist = cru_h / 100.; +const float top_wire_dist = cru_w / 100.; +const float clk_ce_set_vdist = (lut_y[1] - lut_y[0] - lut_h) / 4.; + +const float sn_dist = cru_x / 125.; +const float ew_dist = (1. - cru_y - cru_h) / 130.; +const float wrap_len = 0.02f; +const float spine_pip_off = 0.11f; + +const float io_x = cru_x + cru_w + 0.1; +const float io_w = (1. - io_x) / 3.; +const float io_gap = 0.03; +const float io_h = (cru_h - 4. * io_gap) / 2.; +const float io_y = cru_y + io_gap; + +const float ios_scl = 0.5; +const float ios_h = ios_scl * io_h; +const float ios_w = ios_scl * io_w; +const float ios_gap_y = io_gap; +const float ios_gap_x = io_gap * 1.4; +const float ios_x = io_x; +const float ios_y = ios_scl * io_y; + +const dict portPoint = { + {id_O, 3. * io_h / 4.}, + {id_I, 2. * io_h / 4.}, + {id_OEN, 1. * io_h / 4.}, +}; + +const dict>> portSign = { + {id_O, + {{io_h / 14. * 1.33, portPoint.at(id_O) + io_h / 14., io_h / 14. * 1.66, portPoint.at(id_O) + io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_O) + io_h / 14., io_h / 14. * 2., portPoint.at(id_O) + io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_O) + io_h / 14. * 0.6, io_h / 14. * 2., + portPoint.at(id_O) - io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_O) - io_h / 14. * 0.6, io_h / 14. * 1.66, portPoint.at(id_O) - io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_O) - io_h / 14., io_h / 14. * 1.33, portPoint.at(id_O) - io_h / 14.}, + {io_h / 14. * 1.33, portPoint.at(id_O) - io_h / 14., io_h / 14. * 1., portPoint.at(id_O) - io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_O) - io_h / 14. * 0.6, io_h / 14. * 1., + portPoint.at(id_O) + io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_O) + io_h / 14. * 0.6, io_h / 14. * 1.33, + portPoint.at(id_O) + io_h / 14.}}}, + {id_I, + {{io_h / 14., portPoint.at(id_I) + io_h / 14., 2. * io_h / 14., portPoint.at(id_I) + io_h / 14.}, + {io_h / 14. * 1.5, portPoint.at(id_I) + io_h / 14., 1. * io_h / 14. * 1.5, portPoint.at(id_I) - io_h / 14.}, + {io_h / 14., portPoint.at(id_I) - io_h / 14., 2. * io_h / 14., portPoint.at(id_I) - io_h / 14.}}}, + {id_OEN, + {{io_h / 14. * 1.33, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 1.66, portPoint.at(id_OEN) + io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 2., + portPoint.at(id_OEN) + io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_OEN) + io_h / 14. * 0.6, io_h / 14. * 2., + portPoint.at(id_OEN) - io_h / 14. * 0.6}, + {io_h / 14. * 2., portPoint.at(id_OEN) - io_h / 14. * 0.6, io_h / 14. * 1.66, + portPoint.at(id_OEN) - io_h / 14.}, + {io_h / 14. * 1.66, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 1.33, portPoint.at(id_OEN) - io_h / 14.}, + {io_h / 14. * 1.33, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 1., + portPoint.at(id_OEN) - io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_OEN) - io_h / 14. * 0.6, io_h / 14. * 1., + portPoint.at(id_OEN) + io_h / 14. * 0.6}, + {io_h / 14. * 1., portPoint.at(id_OEN) + io_h / 14. * 0.6, io_h / 14. * 1.33, + portPoint.at(id_OEN) + io_h / 14.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 3.2, portPoint.at(id_OEN) + io_h / 14.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) + 0., io_h / 14. * 3.2, portPoint.at(id_OEN) + 0.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 3.2, portPoint.at(id_OEN) - io_h / 14.}, + {io_h / 14. * 2.2, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 2.2, portPoint.at(id_OEN) - io_h / 14.}}}, +}; + +const dict spineY = { + {id_SPINE0, 1. - 1. * ew_dist}, {id_SPINE1, 1. - 2. * ew_dist}, {id_SPINE2, 1. - 3. * ew_dist}, + {id_SPINE3, 1. - 4. * ew_dist}, {id_SPINE4, 1. - 5. * ew_dist}, {id_SPINE5, 1. - 6. * ew_dist}, + {id_SPINE6, 1. - 7. * ew_dist}, {id_SPINE7, 1. - 8. * ew_dist}, {id_SPINE8, 1. - 1. * ew_dist}, + {id_SPINE9, 1. - 2. * ew_dist}, {id_SPINE10, 1. - 3. * ew_dist}, {id_SPINE11, 1. - 4. * ew_dist}, + {id_SPINE12, 1. - 5. * ew_dist}, {id_SPINE13, 1. - 6. * ew_dist}, {id_SPINE14, 1. - 7. * ew_dist}, + {id_SPINE15, 1. - 8. * ew_dist}, {id_SPINE16, 1. - 1. * ew_dist}, {id_SPINE17, 1. - 2. * ew_dist}, + {id_SPINE18, 1. - 3. * ew_dist}, {id_SPINE19, 1. - 4. * ew_dist}, {id_SPINE20, 1. - 5. * ew_dist}, + {id_SPINE21, 1. - 6. * ew_dist}, {id_SPINE22, 1. - 7. * ew_dist}, {id_SPINE23, 1. - 8. * ew_dist}, + {id_SPINE24, 1. - 1. * ew_dist}, {id_SPINE25, 1. - 2. * ew_dist}, {id_SPINE26, 1. - 3. * ew_dist}, + {id_SPINE27, 1. - 4. * ew_dist}, {id_SPINE28, 1. - 5. * ew_dist}, {id_SPINE29, 1. - 6. * ew_dist}, + {id_SPINE30, 1. - 7. * ew_dist}, {id_SPINE31, 1. - 8. * ew_dist}, +}; + +const dict> pipPoint = { + {id_X01, {Center, cru_y + 1. * cru_h / 9.}}, + {id_X02, {Center, cru_y + 2. * cru_h / 9.}}, + {id_X03, {Center, cru_y + 3. * cru_h / 9.}}, + {id_X04, {Center, cru_y + 4. * cru_h / 9.}}, + {id_X05, {Center, cru_y + 5. * cru_h / 9.}}, + {id_X06, {Center, cru_y + 6. * cru_h / 9.}}, + {id_X07, {Center, cru_y + 7. * cru_h / 9.}}, + {id_X08, {Center, cru_y + 8. * cru_h / 9.}}, + // LUT inputs + {id_A0, {Right, lut_y[0] + lut_A_off}}, + {id_B0, {Right, lut_y[0] + lut_B_off}}, + {id_C0, {Right, lut_y[0] + lut_C_off}}, + {id_D0, {Right, lut_y[0] + lut_D_off}}, + {id_A1, {Right, lut_y[1] + lut_A_off}}, + {id_B1, {Right, lut_y[1] + lut_B_off}}, + {id_C1, {Right, lut_y[1] + lut_C_off}}, + {id_D1, {Right, lut_y[1] + lut_D_off}}, + {id_A2, {Right, lut_y[2] + lut_A_off}}, + {id_B2, {Right, lut_y[2] + lut_B_off}}, + {id_C2, {Right, lut_y[2] + lut_C_off}}, + {id_D2, {Right, lut_y[2] + lut_D_off}}, + {id_A3, {Right, lut_y[3] + lut_A_off}}, + {id_B3, {Right, lut_y[3] + lut_B_off}}, + {id_C3, {Right, lut_y[3] + lut_C_off}}, + {id_D3, {Right, lut_y[3] + lut_D_off}}, + {id_A4, {Right, lut_y[4] + lut_A_off}}, + {id_B4, {Right, lut_y[4] + lut_B_off}}, + {id_C4, {Right, lut_y[4] + lut_C_off}}, + {id_D4, {Right, lut_y[4] + lut_D_off}}, + {id_A5, {Right, lut_y[5] + lut_A_off}}, + {id_B5, {Right, lut_y[5] + lut_B_off}}, + {id_C5, {Right, lut_y[5] + lut_C_off}}, + {id_D5, {Right, lut_y[5] + lut_D_off}}, + {id_A6, {Right, lut_y[6] + lut_A_off}}, + {id_B6, {Right, lut_y[6] + lut_B_off}}, + {id_C6, {Right, lut_y[6] + lut_C_off}}, + {id_D6, {Right, lut_y[6] + lut_D_off}}, + {id_A7, {Right, lut_y[7] + lut_A_off}}, + {id_B7, {Right, lut_y[7] + lut_B_off}}, + {id_C7, {Right, lut_y[7] + lut_C_off}}, + {id_D7, {Right, lut_y[7] + lut_D_off}}, + // wires below LUT0 + {id_Q0, {Right, grp_lut_y[0] - right_wire_dist}}, + {id_F0, {Right, grp_lut_y[0] - 2. * right_wire_dist}}, + {id_OF3, {Right, grp_lut_y[0] - 3. * right_wire_dist}}, + // wires between LUT1 and LUT2 + {id_Q2, {Right, grp_lut_y[1] - right_wire_dist}}, + {id_F2, {Right, grp_lut_y[1] - 2. * right_wire_dist}}, + {id_OF2, {Right, grp_lut_y[1] - 3. * right_wire_dist}}, + {id_OF1, {Right, grp_lut_y[1] - 4. * right_wire_dist}}, + {id_OF0, {Right, grp_lut_y[1] - 5. * right_wire_dist}}, + {id_SEL1, {Right, grp_lut_y[1] - 6. * right_wire_dist}}, + {id_OF7, {Right, grp_lut_y[1] - 7. * right_wire_dist}}, + {id_SEL0, {Right, grp_lut_y[1] - 8. * right_wire_dist}}, + {id_F1, {Right, grp_lut_y[1] - 9. * right_wire_dist}}, + {id_Q1, {Right, grp_lut_y[1] - 10. * right_wire_dist}}, + // wires between LUT3 and LUT4 + {id_Q4, {Right, grp_lut_y[2] - right_wire_dist}}, + {id_F4, {Right, grp_lut_y[2] - 2. * right_wire_dist}}, + {id_OF4, {Right, grp_lut_y[2] - 3. * right_wire_dist}}, + {id_OF5, {Right, grp_lut_y[2] - 4. * right_wire_dist}}, + {id_SEL7, {Right, grp_lut_y[2] - 5. * right_wire_dist}}, + {id_SEL3, {Right, grp_lut_y[2] - 6. * right_wire_dist}}, + {id_SEL2, {Right, grp_lut_y[2] - 7. * right_wire_dist}}, + {id_F3, {Right, grp_lut_y[2] - 8. * right_wire_dist}}, + {id_Q3, {Right, grp_lut_y[2] - 9. * right_wire_dist}}, + // wires between LUT5 and LUT6 + {id_F6, {Right, grp_lut_y[3] - right_wire_dist}}, + {id_SEL5, {Right, grp_lut_y[3] - 2. * right_wire_dist}}, + {id_SEL4, {Right, grp_lut_y[3] - 4. * right_wire_dist}}, + {id_F5, {Right, grp_lut_y[3] - 5. * right_wire_dist}}, + {id_Q5, {Right, grp_lut_y[3] - 6. * right_wire_dist}}, + // Q6, Q7 --- IOB + {id_Q6, {Right, grp_lut_y[3] + grp_lut_h * 0.33}}, + {id_Q7, {Right, grp_lut_y[3] + grp_lut_h * 0.66}}, + // wires above LUT7 + {id_F7, {Right, grp_lut_y[3] + grp_lut_h + right_wire_dist}}, + {id_SEL6, {Right, grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist}}, + {id_OF6, {Right, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist}}, + // DI0-5 + {id_DI5, {Right, cru_y + cru_h - 0.5 * right_wire_dist}}, + {id_DI4, {Right, cru_y + cru_h - 1. * right_wire_dist}}, + {id_DI3, {Right, cru_y + cru_h - 1.5 * right_wire_dist}}, + {id_DI2, {Right, cru_y + cru_h - 2. * right_wire_dist}}, + {id_DI1, {Right, cru_y + cru_h - 2.5 * right_wire_dist}}, + {id_DI0, {Right, cru_y + cru_h - 3. * right_wire_dist}}, + // Q6 + // CLK, CE, SET-RESET + {id_CLK0, {Right, lut_y[1] - clk_ce_set_vdist}}, + {id_CE0, {Right, lut_y[1] - 2. * clk_ce_set_vdist}}, + {id_LSR0, {Right, lut_y[1] - 3. * clk_ce_set_vdist}}, + {id_CLK1, {Right, lut_y[3] - clk_ce_set_vdist}}, + {id_CE1, {Right, lut_y[3] - 2. * clk_ce_set_vdist}}, + {id_LSR1, {Right, lut_y[3] - 3. * clk_ce_set_vdist}}, + {id_CLK2, {Right, lut_y[5] - clk_ce_set_vdist}}, + {id_CE2, {Right, lut_y[5] - 2. * clk_ce_set_vdist}}, + {id_LSR2, {Right, lut_y[5] - 3. * clk_ce_set_vdist}}, + // SN + // 1 hop + {id_S100, {Left, cru_y + 1. * left_wire_dist}}, + {id_S101, {Left, cru_y + 2. * left_wire_dist}}, + {id_S130, {Left, cru_y + 3. * left_wire_dist}}, + {id_S131, {Left, cru_y + 4. * left_wire_dist}}, + {id_N101, {Left, cru_y + 5. * left_wire_dist}}, + {id_N100, {Left, cru_y + 6. * left_wire_dist}}, + {id_N131, {Left, cru_y + 7. * left_wire_dist}}, + {id_N130, {Left, cru_y + 8. * left_wire_dist}}, + // 1 hop SN + {id_N111, {Left, cru_y + 9. * left_wire_dist}}, + {id_SN10, {Left, cru_y + 10. * left_wire_dist}}, + {id_S111, {Left, cru_y + 11. * left_wire_dist}}, + {id_N121, {Left, cru_y + 12. * left_wire_dist}}, + {id_SN20, {Left, cru_y + 13. * left_wire_dist}}, + {id_S121, {Left, cru_y + 14. * left_wire_dist}}, + // 2 hop + {id_S200, {Left, cru_y + 15. * left_wire_dist}}, + {id_S201, {Left, cru_y + 16. * left_wire_dist}}, + {id_N202, {Left, cru_y + 17. * left_wire_dist}}, + {id_S202, {Left, cru_y + 18. * left_wire_dist}}, + {id_N201, {Left, cru_y + 19. * left_wire_dist}}, + {id_N200, {Left, cru_y + 20. * left_wire_dist}}, + + {id_S210, {Left, cru_y + 21. * left_wire_dist}}, + {id_S211, {Left, cru_y + 22. * left_wire_dist}}, + {id_N212, {Left, cru_y + 23. * left_wire_dist}}, + {id_S212, {Left, cru_y + 24. * left_wire_dist}}, + {id_N211, {Left, cru_y + 25. * left_wire_dist}}, + {id_N210, {Left, cru_y + 26. * left_wire_dist}}, + + {id_S220, {Left, cru_y + 27. * left_wire_dist}}, + {id_S221, {Left, cru_y + 28. * left_wire_dist}}, + {id_N222, {Left, cru_y + 29. * left_wire_dist}}, + {id_S222, {Left, cru_y + 30. * left_wire_dist}}, + {id_N221, {Left, cru_y + 31. * left_wire_dist}}, + {id_N220, {Left, cru_y + 32. * left_wire_dist}}, + + {id_S230, {Left, cru_y + 33. * left_wire_dist}}, + {id_S231, {Left, cru_y + 34. * left_wire_dist}}, + {id_N232, {Left, cru_y + 35. * left_wire_dist}}, + {id_S232, {Left, cru_y + 36. * left_wire_dist}}, + {id_N231, {Left, cru_y + 37. * left_wire_dist}}, + {id_N230, {Left, cru_y + 38. * left_wire_dist}}, + + {id_S240, {Left, cru_y + 39. * left_wire_dist}}, + {id_S241, {Left, cru_y + 40. * left_wire_dist}}, + {id_N242, {Left, cru_y + 41. * left_wire_dist}}, + {id_S242, {Left, cru_y + 42. * left_wire_dist}}, + {id_N241, {Left, cru_y + 43. * left_wire_dist}}, + {id_N240, {Left, cru_y + 44. * left_wire_dist}}, + + {id_S250, {Left, cru_y + 45. * left_wire_dist}}, + {id_S251, {Left, cru_y + 46. * left_wire_dist}}, + {id_N252, {Left, cru_y + 47. * left_wire_dist}}, + {id_S252, {Left, cru_y + 48. * left_wire_dist}}, + {id_N251, {Left, cru_y + 49. * left_wire_dist}}, + {id_N250, {Left, cru_y + 50. * left_wire_dist}}, + + {id_S260, {Left, cru_y + 51. * left_wire_dist}}, + {id_S261, {Left, cru_y + 52. * left_wire_dist}}, + {id_N262, {Left, cru_y + 53. * left_wire_dist}}, + {id_S262, {Left, cru_y + 54. * left_wire_dist}}, + {id_N261, {Left, cru_y + 55. * left_wire_dist}}, + {id_N260, {Left, cru_y + 56. * left_wire_dist}}, + + {id_S270, {Left, cru_y + 57. * left_wire_dist}}, + {id_S271, {Left, cru_y + 58. * left_wire_dist}}, + {id_N272, {Left, cru_y + 59. * left_wire_dist}}, + {id_S272, {Left, cru_y + 60. * left_wire_dist}}, + {id_N271, {Left, cru_y + 61. * left_wire_dist}}, + {id_N270, {Left, cru_y + 62. * left_wire_dist}}, + + // Clocks + {id_GT10, {Left, cru_y + 63. * left_wire_dist}}, + {id_GT00, {Left, cru_y + 68. * left_wire_dist}}, + + // 4 hop + {id_N808, {Left, cru_y + 73. * left_wire_dist}}, + {id_S800, {Left, cru_y + 74. * left_wire_dist}}, + {id_S804, {Left, cru_y + 75. * left_wire_dist}}, + {id_N804, {Left, cru_y + 76. * left_wire_dist}}, + {id_N800, {Left, cru_y + 77. * left_wire_dist}}, + {id_S808, {Left, cru_y + 78. * left_wire_dist}}, + + {id_N818, {Left, cru_y + 79. * left_wire_dist}}, + {id_S810, {Left, cru_y + 80. * left_wire_dist}}, + {id_S814, {Left, cru_y + 81. * left_wire_dist}}, + {id_N814, {Left, cru_y + 82. * left_wire_dist}}, + {id_N810, {Left, cru_y + 83. * left_wire_dist}}, + {id_S818, {Left, cru_y + 84. * left_wire_dist}}, + + {id_N828, {Left, cru_y + 85. * left_wire_dist}}, + {id_S820, {Left, cru_y + 86. * left_wire_dist}}, + {id_S824, {Left, cru_y + 87. * left_wire_dist}}, + {id_N824, {Left, cru_y + 88. * left_wire_dist}}, + {id_N820, {Left, cru_y + 89. * left_wire_dist}}, + {id_S828, {Left, cru_y + 90. * left_wire_dist}}, + + {id_N838, {Left, cru_y + 91. * left_wire_dist}}, + {id_S830, {Left, cru_y + 92. * left_wire_dist}}, + {id_S834, {Left, cru_y + 93. * left_wire_dist}}, + {id_N834, {Left, cru_y + 94. * left_wire_dist}}, + {id_N830, {Left, cru_y + 95. * left_wire_dist}}, + {id_S838, {Left, cru_y + 96. * left_wire_dist}}, + + // EW + // 1 hop + {id_E101, {Top, cru_x + 1. * top_wire_dist}}, + {id_E100, {Top, cru_x + 2. * top_wire_dist}}, + {id_E131, {Top, cru_x + 3. * top_wire_dist}}, + {id_E130, {Top, cru_x + 4. * top_wire_dist}}, + {id_W100, {Top, cru_x + 5. * top_wire_dist}}, + {id_W101, {Top, cru_x + 6. * top_wire_dist}}, + {id_W130, {Top, cru_x + 7. * top_wire_dist}}, + {id_W131, {Top, cru_x + 8. * top_wire_dist}}, + // 1 hop EW + {id_E111, {Top, cru_x + 9. * top_wire_dist}}, + {id_EW10, {Top, cru_x + 10. * top_wire_dist}}, + {id_W111, {Top, cru_x + 11. * top_wire_dist}}, + {id_E121, {Top, cru_x + 12. * top_wire_dist}}, + {id_EW20, {Top, cru_x + 13. * top_wire_dist}}, + {id_W121, {Top, cru_x + 14. * top_wire_dist}}, + // 2 hop + {id_E202, {Top, cru_x + 15. * top_wire_dist}}, + {id_E201, {Top, cru_x + 16. * top_wire_dist}}, + {id_W200, {Top, cru_x + 17. * top_wire_dist}}, + {id_E200, {Top, cru_x + 18. * top_wire_dist}}, + {id_W201, {Top, cru_x + 19. * top_wire_dist}}, + {id_W202, {Top, cru_x + 20. * top_wire_dist}}, + + {id_E212, {Top, cru_x + 21. * top_wire_dist}}, + {id_E211, {Top, cru_x + 22. * top_wire_dist}}, + {id_W210, {Top, cru_x + 23. * top_wire_dist}}, + {id_E210, {Top, cru_x + 24. * top_wire_dist}}, + {id_W211, {Top, cru_x + 25. * top_wire_dist}}, + {id_W212, {Top, cru_x + 26. * top_wire_dist}}, + + {id_E222, {Top, cru_x + 27. * top_wire_dist}}, + {id_E221, {Top, cru_x + 28. * top_wire_dist}}, + {id_W220, {Top, cru_x + 29. * top_wire_dist}}, + {id_E220, {Top, cru_x + 30. * top_wire_dist}}, + {id_W221, {Top, cru_x + 31. * top_wire_dist}}, + {id_W222, {Top, cru_x + 32. * top_wire_dist}}, + + {id_E232, {Top, cru_x + 33. * top_wire_dist}}, + {id_E231, {Top, cru_x + 34. * top_wire_dist}}, + {id_W230, {Top, cru_x + 35. * top_wire_dist}}, + {id_E230, {Top, cru_x + 36. * top_wire_dist}}, + {id_W231, {Top, cru_x + 37. * top_wire_dist}}, + {id_W232, {Top, cru_x + 38. * top_wire_dist}}, + + {id_E242, {Top, cru_x + 39. * top_wire_dist}}, + {id_E241, {Top, cru_x + 40. * top_wire_dist}}, + {id_W240, {Top, cru_x + 41. * top_wire_dist}}, + {id_E240, {Top, cru_x + 42. * top_wire_dist}}, + {id_W241, {Top, cru_x + 43. * top_wire_dist}}, + {id_W242, {Top, cru_x + 44. * top_wire_dist}}, + + {id_E252, {Top, cru_x + 45. * top_wire_dist}}, + {id_E251, {Top, cru_x + 46. * top_wire_dist}}, + {id_W250, {Top, cru_x + 47. * top_wire_dist}}, + {id_E250, {Top, cru_x + 48. * top_wire_dist}}, + {id_W251, {Top, cru_x + 49. * top_wire_dist}}, + {id_W252, {Top, cru_x + 50. * top_wire_dist}}, + + {id_E262, {Top, cru_x + 51. * top_wire_dist}}, + {id_E261, {Top, cru_x + 52. * top_wire_dist}}, + {id_W260, {Top, cru_x + 53. * top_wire_dist}}, + {id_E260, {Top, cru_x + 54. * top_wire_dist}}, + {id_W261, {Top, cru_x + 55. * top_wire_dist}}, + {id_W262, {Top, cru_x + 56. * top_wire_dist}}, + + {id_E272, {Top, cru_x + 57. * top_wire_dist}}, + {id_E271, {Top, cru_x + 58. * top_wire_dist}}, + {id_W270, {Top, cru_x + 59. * top_wire_dist}}, + {id_E270, {Top, cru_x + 60. * top_wire_dist}}, + {id_W271, {Top, cru_x + 61. * top_wire_dist}}, + {id_W272, {Top, cru_x + 62. * top_wire_dist}}, + + // Global taps -> bracnhes + {id_GBO0, {Top, cru_x + 63. * top_wire_dist}}, + {id_GB00, {Top, cru_x + 64. * top_wire_dist}}, + {id_GB10, {Top, cru_x + 65. * top_wire_dist}}, + {id_GB20, {Top, cru_x + 66. * top_wire_dist}}, + {id_GB30, {Top, cru_x + 67. * top_wire_dist}}, + {id_GBO1, {Top, cru_x + 68. * top_wire_dist}}, + {id_GB40, {Top, cru_x + 68. * top_wire_dist}}, + {id_GB50, {Top, cru_x + 69. * top_wire_dist}}, + {id_GB60, {Top, cru_x + 70. * top_wire_dist}}, + {id_GB70, {Top, cru_x + 71. * top_wire_dist}}, + + // 4 hop + {id_E808, {Top, cru_x + 72. * top_wire_dist}}, + {id_W800, {Top, cru_x + 73. * top_wire_dist}}, + {id_W804, {Top, cru_x + 74. * top_wire_dist}}, + {id_E804, {Top, cru_x + 75. * top_wire_dist}}, + {id_E800, {Top, cru_x + 76. * top_wire_dist}}, + {id_W808, {Top, cru_x + 77. * top_wire_dist}}, + + {id_E818, {Top, cru_x + 78. * top_wire_dist}}, + {id_W810, {Top, cru_x + 79. * top_wire_dist}}, + {id_W814, {Top, cru_x + 80. * top_wire_dist}}, + {id_E814, {Top, cru_x + 81. * top_wire_dist}}, + {id_E810, {Top, cru_x + 82. * top_wire_dist}}, + {id_W818, {Top, cru_x + 83. * top_wire_dist}}, + + {id_E828, {Top, cru_x + 84. * top_wire_dist}}, + {id_W820, {Top, cru_x + 85. * top_wire_dist}}, + {id_W824, {Top, cru_x + 86. * top_wire_dist}}, + {id_E824, {Top, cru_x + 87. * top_wire_dist}}, + {id_E820, {Top, cru_x + 88. * top_wire_dist}}, + {id_W828, {Top, cru_x + 89. * top_wire_dist}}, + + {id_E838, {Top, cru_x + 90. * top_wire_dist}}, + {id_W830, {Top, cru_x + 91. * top_wire_dist}}, + {id_W834, {Top, cru_x + 92. * top_wire_dist}}, + {id_E834, {Top, cru_x + 93. * top_wire_dist}}, + {id_E830, {Top, cru_x + 94. * top_wire_dist}}, + {id_W838, {Top, cru_x + 95. * top_wire_dist}}, + +}; + +// wire +const std::vector decalless_wires = {id_X01, id_X02, id_X03, id_X04, id_X05, id_X06, id_X07}; + +const float clk_ce_set_hdist = dff_w / 4.; +const float dff_f_x = (grp_lut_x + grp_lut_w + dff_x + dff_w) / 2.; +const float mux5i_x = (grp_lut_x + grp_lut_w + mux2lut5_x) / 2.; + +// id, {x1, y1, x2, y2} +const dict>> sliceLocalWires = { + // dff + {id_CLK0, + {{cru_x + cru_w, pipPoint.at(id_CLK0).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK0).second}, + {dff_x + clk_ce_set_hdist, lut_y[1], dff_x + clk_ce_set_hdist, lut_y[0] + lut_h}}}, + {id_CE0, + {{cru_x + cru_w, pipPoint.at(id_CE0).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE0).second}, + {dff_x + 2 * clk_ce_set_hdist, lut_y[1], dff_x + 2. * clk_ce_set_hdist, lut_y[0] + lut_h}}}, + {id_LSR0, + {{cru_x + cru_w, pipPoint.at(id_LSR0).second, dff_x + 3. * clk_ce_set_hdist, pipPoint.at(id_LSR0).second}, + {dff_x + 3 * clk_ce_set_hdist, lut_y[1], dff_x + 3. * clk_ce_set_hdist, lut_y[0] + lut_h}}}, + {id_CLK1, + {{cru_x + cru_w, pipPoint.at(id_CLK1).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK1).second}, + {dff_x + clk_ce_set_hdist, lut_y[3], dff_x + clk_ce_set_hdist, lut_y[2] + lut_h}}}, + {id_CE1, + {{cru_x + cru_w, pipPoint.at(id_CE1).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE1).second}, + {dff_x + 2 * clk_ce_set_hdist, lut_y[3], dff_x + 2. * clk_ce_set_hdist, lut_y[2] + lut_h}}}, + {id_LSR1, + {{cru_x + cru_w, pipPoint.at(id_LSR1).second, dff_x + 3. * clk_ce_set_hdist, pipPoint.at(id_LSR1).second}, + {dff_x + 3 * clk_ce_set_hdist, lut_y[3], dff_x + 3. * clk_ce_set_hdist, lut_y[2] + lut_h}}}, + {id_CLK2, + {{cru_x + cru_w, pipPoint.at(id_CLK2).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK2).second}, + {dff_x + clk_ce_set_hdist, lut_y[5], dff_x + clk_ce_set_hdist, lut_y[4] + lut_h}}}, + {id_CE2, + {{cru_x + cru_w, pipPoint.at(id_CE2).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE2).second}, + {dff_x + 2 * clk_ce_set_hdist, lut_y[5], dff_x + 2. * clk_ce_set_hdist, lut_y[4] + lut_h}}}, + {id_LSR2, + {{cru_x + cru_w, pipPoint.at(id_LSR2).second, dff_x + 3 * clk_ce_set_hdist, pipPoint.at(id_LSR2).second}, + {dff_x + 3 * clk_ce_set_hdist, lut_y[5], dff_x + 3. * clk_ce_set_hdist, lut_y[4] + lut_h}}}, + // lut + {id_A0, {{cru_x + cru_w, lut_y[0] + lut_A_off, lut_x, lut_y[0] + lut_A_off}}}, + {id_B0, {{cru_x + cru_w, lut_y[0] + lut_B_off, lut_x, lut_y[0] + lut_B_off}}}, + {id_C0, {{cru_x + cru_w, lut_y[0] + lut_C_off, lut_x, lut_y[0] + lut_C_off}}}, + {id_D0, {{cru_x + cru_w, lut_y[0] + lut_D_off, lut_x, lut_y[0] + lut_D_off}}}, + {id_A1, {{cru_x + cru_w, lut_y[1] + lut_A_off, lut_x, lut_y[1] + lut_A_off}}}, + {id_B1, {{cru_x + cru_w, lut_y[1] + lut_B_off, lut_x, lut_y[1] + lut_B_off}}}, + {id_C1, {{cru_x + cru_w, lut_y[1] + lut_C_off, lut_x, lut_y[1] + lut_C_off}}}, + {id_D1, {{cru_x + cru_w, lut_y[1] + lut_D_off, lut_x, lut_y[1] + lut_D_off}}}, + {id_A2, {{cru_x + cru_w, lut_y[2] + lut_A_off, lut_x, lut_y[2] + lut_A_off}}}, + {id_B2, {{cru_x + cru_w, lut_y[2] + lut_B_off, lut_x, lut_y[2] + lut_B_off}}}, + {id_C2, {{cru_x + cru_w, lut_y[2] + lut_C_off, lut_x, lut_y[2] + lut_C_off}}}, + {id_D2, {{cru_x + cru_w, lut_y[2] + lut_D_off, lut_x, lut_y[2] + lut_D_off}}}, + {id_A3, {{cru_x + cru_w, lut_y[3] + lut_A_off, lut_x, lut_y[3] + lut_A_off}}}, + {id_B3, {{cru_x + cru_w, lut_y[3] + lut_B_off, lut_x, lut_y[3] + lut_B_off}}}, + {id_C3, {{cru_x + cru_w, lut_y[3] + lut_C_off, lut_x, lut_y[3] + lut_C_off}}}, + {id_D3, {{cru_x + cru_w, lut_y[3] + lut_D_off, lut_x, lut_y[3] + lut_D_off}}}, + {id_A4, {{cru_x + cru_w, lut_y[4] + lut_A_off, lut_x, lut_y[4] + lut_A_off}}}, + {id_B4, {{cru_x + cru_w, lut_y[4] + lut_B_off, lut_x, lut_y[4] + lut_B_off}}}, + {id_C4, {{cru_x + cru_w, lut_y[4] + lut_C_off, lut_x, lut_y[4] + lut_C_off}}}, + {id_D4, {{cru_x + cru_w, lut_y[4] + lut_D_off, lut_x, lut_y[4] + lut_D_off}}}, + {id_A5, {{cru_x + cru_w, lut_y[5] + lut_A_off, lut_x, lut_y[5] + lut_A_off}}}, + {id_B5, {{cru_x + cru_w, lut_y[5] + lut_B_off, lut_x, lut_y[5] + lut_B_off}}}, + {id_C5, {{cru_x + cru_w, lut_y[5] + lut_C_off, lut_x, lut_y[5] + lut_C_off}}}, + {id_D5, {{cru_x + cru_w, lut_y[5] + lut_D_off, lut_x, lut_y[5] + lut_D_off}}}, + {id_A6, {{cru_x + cru_w, lut_y[6] + lut_A_off, lut_x, lut_y[6] + lut_A_off}}}, + {id_B6, {{cru_x + cru_w, lut_y[6] + lut_B_off, lut_x, lut_y[6] + lut_B_off}}}, + {id_C6, {{cru_x + cru_w, lut_y[6] + lut_C_off, lut_x, lut_y[6] + lut_C_off}}}, + {id_D6, {{cru_x + cru_w, lut_y[6] + lut_D_off, lut_x, lut_y[6] + lut_D_off}}}, + {id_A7, {{cru_x + cru_w, lut_y[7] + lut_A_off, lut_x, lut_y[7] + lut_A_off}}}, + {id_B7, {{cru_x + cru_w, lut_y[7] + lut_B_off, lut_x, lut_y[7] + lut_B_off}}}, + {id_C7, {{cru_x + cru_w, lut_y[7] + lut_C_off, lut_x, lut_y[7] + lut_C_off}}}, + {id_D7, {{cru_x + cru_w, lut_y[7] + lut_D_off, lut_x, lut_y[7] + lut_D_off}}}, + // wires below LUT0 + {id_Q0, + {{cru_x + cru_w, grp_lut_y[0] - right_wire_dist, dff_f_x, grp_lut_y[0] - right_wire_dist}, + {dff_f_x, grp_lut_y[0] - right_wire_dist, dff_f_x, lut_y[0] + lut_h / 2.}, + {dff_f_x, lut_y[0] + lut_h / 2., dff_x + dff_w, lut_y[0] + lut_h / 2.}}}, + {id_F0, + {{cru_x + cru_w, grp_lut_y[0] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[0] - 2. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[0] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[0] + lut_h / 2.}, + {lut_x + lut_w, lut_y[0] + lut_h / 2., dff_x, lut_y[0] + lut_h / 2.}}}, + {id_I0MUX0, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[0] - 2. * right_wire_dist, mux5i_x, + grp_lut_y[0] - 2. * right_wire_dist}, + {mux5i_x, grp_lut_y[0] - 2. * right_wire_dist, mux5i_x, lut_y[0] + lut_h / 2.}, + {mux5i_x, lut_y[0] + lut_h / 2., mux2lut5_x, lut_y[0] + lut_h / 2.}}}, + {id_OF3, + {{cru_x + cru_w, grp_lut_y[0] - 3. * right_wire_dist, mux2lut7_x + 4. / 3. * mux_w, + grp_lut_y[0] - 3. * right_wire_dist}, + {mux2lut7_x + 4. / 3. * mux_w, grp_lut_y[0] - 3. * right_wire_dist, mux2lut7_x + 4. / 3. * mux_w, + mux2lut7_y + mux_h / 2.}, + {mux2lut7_x + 4. / 3. * mux_w, mux2lut7_y + mux_h / 2., mux2lut7_x + mux_w, mux2lut7_y + mux_h / 2.}}}, + {id_I1MUX7, {{mux2lut7_x + 4. / 3. * mux_w, mux2lut7_y + mux_h / 2., mux2lut8_x, mux2lut7_y + mux_h / 2.}}}, + // wires between LUT1 and LUT2 + {id_Q1, + {{cru_x + cru_w, grp_lut_y[1] - 10. * right_wire_dist, dff_f_x, grp_lut_y[1] - 10. * right_wire_dist}, + {dff_f_x, grp_lut_y[1] - 10. * right_wire_dist, dff_f_x, lut_y[1] + lut_h / 2.}, + {dff_f_x, lut_y[1] + lut_h / 2., dff_x + dff_w, lut_y[1] + lut_h / 2.}}}, + {id_F1, + {{cru_x + cru_w, grp_lut_y[1] - 9. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[1] - 9. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 9. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[1] + lut_h / 2.}, + {lut_x + lut_w, lut_y[1] + lut_h / 2., dff_x, lut_y[1] + lut_h / 2.}}}, + {id_I1MUX0, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 9. * right_wire_dist, mux5i_x, + grp_lut_y[1] - 9. * right_wire_dist}, + {mux5i_x, grp_lut_y[1] - 9. * right_wire_dist, mux5i_x, lut_y[1] + lut_h / 2.}, + {mux5i_x, lut_y[1] + lut_h / 2., mux2lut5_x, lut_y[1] + lut_h / 2.}}}, + {id_SEL0, + {{cru_x + cru_w, grp_lut_y[1] - 8. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[1] - 8. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[1] - 8. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[0] + mux_h - mux_f / 2.}}}, + {id_OF7, + {{cru_x + cru_w, grp_lut_y[1] - 7. * right_wire_dist, mux2lut8_x + 4. / 3. * mux_w, + grp_lut_y[1] - 7. * right_wire_dist}, + {mux2lut8_x + 4. / 3. * mux_w, grp_lut_y[1] - 7. * right_wire_dist, mux2lut8_x + 4. / 3. * mux_w, + mux2lut8_y + mux_h / 2.}, + {mux2lut8_x + 4. / 3. * mux_w, mux2lut8_y + mux_h / 2., mux2lut8_x + mux_w, mux2lut8_y + mux_h / 2.}}}, + {id_SEL1, + {{cru_x + cru_w, grp_lut_y[1] - 6. * right_wire_dist, mux2lut6_x + mux_w / 2., + grp_lut_y[1] - 6. * right_wire_dist}, + {mux2lut6_x + mux_w / 2., grp_lut_y[1] - 6. * right_wire_dist, mux2lut6_x + mux_w / 2., + mux2lut6_y[0] + mux_h - mux_f / 2.}}}, + {id_OF0, + {{cru_x + cru_w, grp_lut_y[1] - 5. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + grp_lut_y[1] - 5. * right_wire_dist}, + {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[1] - 5. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + mux2lut5_y[0] + mux_h / 2.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[0] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[0] + mux_h / 2.}}}, + {id_I1MUX1, + {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[0] + mux_h / 2., mux2lut6_x, mux2lut5_y[0] + mux_h / 2.}}}, + {id_OF1, + {{cru_x + cru_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + grp_lut_y[1] - 4. * right_wire_dist}, + {mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut6_y[0] + mux_h / 2.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut6_y[0] + mux_h / 2., mux2lut6_x + mux_w, mux2lut6_y[0] + mux_h / 2.}}}, + {id_I1MUX3, + {{mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut7_y + mux_h * 1. / 4.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut7_y + mux_h * 1. / 4., mux2lut7_x, mux2lut7_y + mux_h * 1. / 4.}}}, + {id_OF2, + {{cru_x + cru_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, + grp_lut_y[1] - 3. * right_wire_dist}, + {mux2lut5_x + 5. / 3. * mux_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, + mux2lut5_y[1] + mux_h / 2.}, + {mux2lut5_x + 5. / 3. * mux_w, mux2lut5_y[1] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[1] + mux_h / 2.}}}, + {id_I0MUX1, + {{mux2lut5_x + 5. / 3. * mux_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, + mux2lut6_y[0] + mux_h * 3. / 4.}, + {mux2lut5_x + 5. / 3. * mux_w, mux2lut6_y[0] + mux_h * 3. / 4., mux2lut6_x, + mux2lut6_y[0] + mux_h * 3. / 4.}}}, + {id_F2, + {{cru_x + cru_w, grp_lut_y[1] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[1] - 2. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[2] + lut_h / 2.}, + {lut_x + lut_w, lut_y[2] + lut_h / 2., dff_x, lut_y[2] + lut_h / 2.}}}, + {id_I0MUX2, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 2. * right_wire_dist, mux5i_x, + grp_lut_y[1] - 2. * right_wire_dist}, + {mux5i_x, grp_lut_y[1] - 2. * right_wire_dist, mux5i_x, lut_y[2] + lut_h / 2.}, + {mux5i_x, lut_y[2] + lut_h / 2., mux2lut5_x, lut_y[2] + lut_h / 2.}}}, + {id_Q2, + {{cru_x + cru_w, grp_lut_y[1] - right_wire_dist, dff_f_x, grp_lut_y[1] - right_wire_dist}, + {dff_f_x, grp_lut_y[1] - right_wire_dist, dff_f_x, lut_y[2] + lut_h / 2.}, + {dff_f_x, lut_y[2] + lut_h / 2., dff_x + dff_w, lut_y[2] + lut_h / 2.}}}, + // wires between LUT3 and LUT4 + {id_Q3, + {{cru_x + cru_w, grp_lut_y[2] - 9. * right_wire_dist, dff_f_x, grp_lut_y[2] - 9. * right_wire_dist}, + {dff_f_x, grp_lut_y[2] - 9. * right_wire_dist, dff_f_x, lut_y[3] + lut_h / 2.}, + {dff_f_x, lut_y[3] + lut_h / 2., dff_x + dff_w, lut_y[3] + lut_h / 2.}}}, + {id_F3, + {{cru_x + cru_w, grp_lut_y[2] - 8. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[2] - 8. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 8. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[3] + lut_h / 2.}, + {lut_x + lut_w, lut_y[3] + lut_h / 2., dff_x, lut_y[3] + lut_h / 2.}}}, + {id_I1MUX2, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 8. * right_wire_dist, mux5i_x, + grp_lut_y[2] - 8. * right_wire_dist}, + {mux5i_x, grp_lut_y[2] - 8. * right_wire_dist, mux5i_x, lut_y[3] + lut_h / 2.}, + {mux5i_x, lut_y[3] + lut_h / 2., mux2lut5_x, lut_y[3] + lut_h / 2.}}}, + {id_SEL2, + {{cru_x + cru_w, grp_lut_y[2] - 7. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[2] - 7. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[2] - 7. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[1] + mux_h - mux_f / 2.}}}, + {id_SEL3, + {{cru_x + cru_w, grp_lut_y[2] - 6. * right_wire_dist, mux2lut7_x + mux_w / 2., + grp_lut_y[2] - 6. * right_wire_dist}, + {mux2lut7_x + mux_w / 2., grp_lut_y[2] - 6. * right_wire_dist, mux2lut7_x + mux_w / 2., + mux2lut7_y + mux_h - mux_f / 2.}}}, + {id_SEL7, + {{cru_x + cru_w, grp_lut_y[2] - 5. * right_wire_dist, mux2lut8_x + mux_w / 2., + grp_lut_y[2] - 5. * right_wire_dist}, + {mux2lut8_x + mux_w / 2., grp_lut_y[2] - 5. * right_wire_dist, mux2lut8_x + mux_w / 2., + mux2lut8_y + mux_h - mux_f / 2.}}}, + {id_OF5, + {{cru_x + cru_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + grp_lut_y[2] - 4. * right_wire_dist}, + {mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut6_y[1] + mux_h / 2.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut6_y[1] + mux_h / 2., mux2lut6_x + mux_w, mux2lut6_y[1] + mux_h / 2.}}}, + {id_I0MUX3, + {{mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, + mux2lut7_y + mux_h * 3. / 4.}, + {mux2lut6_x + 4. / 3. * mux_w, mux2lut7_y + mux_h * 3. / 4., mux2lut7_x, mux2lut7_y + mux_h * 3. / 4.}}}, + {id_OF4, + {{cru_x + cru_w, grp_lut_y[2] - 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + grp_lut_y[2] - 3. * right_wire_dist}, + {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[2] - 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + mux2lut5_y[2] + mux_h / 2.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[2] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[2] + mux_h / 2.}}}, + {id_I1MUX5, + {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[2] + mux_h / 2., mux2lut6_x, mux2lut5_y[2] + mux_h / 2.}}}, + {id_F4, + {{cru_x + cru_w, grp_lut_y[2] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[2] - 2. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[4] + lut_h / 2.}, + {lut_x + lut_w, lut_y[4] + lut_h / 2., dff_x, lut_y[4] + lut_h / 2.}}}, + {id_I0MUX4, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 2. * right_wire_dist, mux5i_x, + grp_lut_y[2] - 2. * right_wire_dist}, + {mux5i_x, grp_lut_y[2] - 2. * right_wire_dist, mux5i_x, lut_y[4] + lut_h / 2.}, + {mux5i_x, lut_y[4] + lut_h / 2., mux2lut5_x, lut_y[4] + lut_h / 2.}}}, + {id_Q4, + {{cru_x + cru_w, grp_lut_y[2] - right_wire_dist, dff_f_x, grp_lut_y[2] - right_wire_dist}, + {dff_f_x, grp_lut_y[2] - right_wire_dist, dff_f_x, lut_y[4] + lut_h / 2.}, + {dff_f_x, lut_y[4] + lut_h / 2., dff_x + dff_w, lut_y[4] + lut_h / 2.}}}, + // wires between LUT5 and LUT6 + {id_Q5, + {{cru_x + cru_w, grp_lut_y[3] - 6. * right_wire_dist, dff_f_x, grp_lut_y[3] - 6. * right_wire_dist}, + {dff_f_x, grp_lut_y[3] - 6. * right_wire_dist, dff_f_x, lut_y[5] + lut_h / 2.}, + {dff_f_x, lut_y[5] + lut_h / 2., dff_x + dff_w, lut_y[5] + lut_h / 2.}}}, + {id_F5, + {{cru_x + cru_w, grp_lut_y[3] - 5. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[3] - 5. * right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - 5. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[5] + lut_h / 2.}, + {lut_x + lut_w, lut_y[5] + lut_h / 2., dff_x, lut_y[5] + lut_h / 2.}}}, + {id_I1MUX4, + {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - 5. * right_wire_dist, mux5i_x, + grp_lut_y[3] - 5. * right_wire_dist}, + {mux5i_x, grp_lut_y[3] - 5. * right_wire_dist, mux5i_x, lut_y[5] + lut_h / 2.}, + {mux5i_x, lut_y[5] + lut_h / 2., mux2lut5_x, lut_y[5] + lut_h / 2.}}}, + {id_SEL4, + {{cru_x + cru_w, grp_lut_y[3] - 4. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[3] - 4. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[3] - 4. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[2] + mux_h - mux_f / 2.}}}, + {id_SEL5, + {{cru_x + cru_w, grp_lut_y[3] - 2. * right_wire_dist, mux2lut6_x + mux_w / 2., + grp_lut_y[3] - 2. * right_wire_dist}, + {mux2lut6_x + mux_w / 2., grp_lut_y[3] - 2. * right_wire_dist, mux2lut6_x + mux_w / 2., + mux2lut6_y[1] + mux_h - mux_f / 2.}}}, + {id_F6, + {{cru_x + cru_w, grp_lut_y[3] - right_wire_dist, (lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[6] + lut_h / 2.}, + {lut_x + lut_w, lut_y[6] + lut_h / 2., (lut_x + lut_w + dff_x) / 2., lut_y[6] + lut_h / 2.}}}, + {id_I0MUX6, {{(lut_x + lut_w + dff_x) / 2., lut_y[6] + lut_h / 2., mux2lut5_x, lut_y[6] + lut_h / 2.}}}, + // wires above LUT7 + {id_F7, + {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + right_wire_dist, (lut_x + lut_w + dff_x) / 2., + grp_lut_y[3] + grp_lut_h + right_wire_dist}, + {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] + grp_lut_h + right_wire_dist, (lut_x + lut_w + dff_x) / 2., + lut_y[7] + lut_h / 2.}, + {lut_x + lut_w, lut_y[7] + lut_h / 2., (lut_x + lut_w + dff_x) / 2., lut_y[7] + lut_h / 2.}}}, + {id_I1MUX6, {{(lut_x + lut_w + dff_x) / 2., lut_y[7] + lut_h / 2., mux2lut5_x, lut_y[7] + lut_h / 2.}}}, + {id_SEL6, + {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist, mux2lut5_x + mux_w / 2., + grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist}, + {mux2lut5_x + mux_w / 2., grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist, mux2lut5_x + mux_w / 2., + mux2lut5_y[3] + mux_h - mux_f / 2.}}}, + {id_OF6, + {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist}, + {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, + mux2lut5_y[3] + mux_h / 2.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[3] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[3] + mux_h / 2.}}}, + {id_I0MUX5, + {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[3] + mux_h / 2., mux2lut5_x + 4. / 3. * mux_w, + mux2lut6_y[1] + mux_h * 3. / 4.}, + {mux2lut5_x + 4. / 3. * mux_w, mux2lut6_y[1] + mux_h * 3. / 4., mux2lut6_x, + mux2lut6_y[1] + mux_h * 3. / 4.}}}, +}; + +const dict>> globalSimpleWires = { + {id_I0MUX7, + {{mux2lut8_x, mux2lut8_y + mux_h / 4., mux2lut8_x - 1. / 3. * mux_w, mux2lut8_y + mux_h / 4.}, + {mux2lut8_x - 1. / 3. * mux_w, mux2lut8_y + mux_h / 4., mux2lut8_x - 1. / 3. * mux_w, + cru_y - 2. * right_wire_dist}, + {mux2lut8_x - 1. / 3. * mux_w, cru_y - 2. * right_wire_dist, 1. + mux2lut7_x + 4. / 3. * mux_w, + cru_y - 2. * right_wire_dist}, + {1. + mux2lut7_x + 4. / 3. * mux_w, cru_y - 2. * right_wire_dist, 1. + mux2lut7_x + 4. / 3. * mux_w, + grp_lut_y[0] - 3. * right_wire_dist}}}, +}; + +dict>> const globalWires = { +#define PIP_Y(pip_id) (pipPoint.at(pip_id).second) +#define WIRE_X(offset) (cru_x - ((float)offset) * sn_dist) + // 1 hop + {id_S10, + {{WIRE_X(0), PIP_Y(id_S100), WIRE_X(1), PIP_Y(id_S100)}, + {WIRE_X(1), PIP_Y(id_S100), WIRE_X(1), -1. + PIP_Y(id_S101)}, + {WIRE_X(1), -1. + PIP_Y(id_S101), WIRE_X(0), -1. + PIP_Y(id_S101)}}}, + {id_N10, + {{WIRE_X(0), PIP_Y(id_N100), WIRE_X(2), PIP_Y(id_N100)}, + {WIRE_X(2), PIP_Y(id_N100), WIRE_X(2), 1. + PIP_Y(id_N101)}, + {WIRE_X(2), 1. + PIP_Y(id_N101), WIRE_X(0), 1. + PIP_Y(id_N101)}}}, + {id_S10_loop0, + {{WIRE_X(0), PIP_Y(id_S100), WIRE_X(1), PIP_Y(id_S100)}, + {WIRE_X(1), PIP_Y(id_S100), WIRE_X(1), -1. * wrap_len}, + {WIRE_X(1), -1. * wrap_len, WIRE_X(2), -1. * wrap_len}, + {WIRE_X(2), -1. * wrap_len, WIRE_X(2), PIP_Y(id_N101)}, + {WIRE_X(2), PIP_Y(id_N101), WIRE_X(0), PIP_Y(id_N101)}}}, + {id_N10_loop0, + {{WIRE_X(0), PIP_Y(id_N100), WIRE_X(2), PIP_Y(id_N100)}, + {WIRE_X(2), PIP_Y(id_N100), WIRE_X(2), 1. + 1. * wrap_len}, + {WIRE_X(2), 1. + 1. * wrap_len, WIRE_X(1), 1. + 1. * wrap_len}, + {WIRE_X(1), 1. + 1. * wrap_len, WIRE_X(1), PIP_Y(id_S101)}, + {WIRE_X(1), PIP_Y(id_S101), WIRE_X(0), PIP_Y(id_S101)}}}, + {id_S13, + {{WIRE_X(0), PIP_Y(id_S130), WIRE_X(3), PIP_Y(id_S130)}, + {WIRE_X(3), PIP_Y(id_S130), WIRE_X(3), -1. + PIP_Y(id_S131)}, + {WIRE_X(3), -1. + PIP_Y(id_S131), WIRE_X(0), -1. + PIP_Y(id_S131)}}}, + {id_N13, + {{WIRE_X(0), PIP_Y(id_N130), WIRE_X(4), PIP_Y(id_N130)}, + {WIRE_X(4), PIP_Y(id_N130), WIRE_X(4), 1. + PIP_Y(id_N131)}, + {WIRE_X(4), 1. + PIP_Y(id_N131), WIRE_X(0), 1. + PIP_Y(id_N131)}}}, + {id_S13_loop0, + {{WIRE_X(0), PIP_Y(id_S130), WIRE_X(3), PIP_Y(id_S130)}, + {WIRE_X(3), PIP_Y(id_S130), WIRE_X(3), -1. * wrap_len}, + {WIRE_X(3), -1. * wrap_len, WIRE_X(4), -1. * wrap_len}, + {WIRE_X(4), -1. * wrap_len, WIRE_X(4), PIP_Y(id_N131)}, + {WIRE_X(4), PIP_Y(id_N131), WIRE_X(0), PIP_Y(id_N131)}}}, + {id_N13_loop0, + {{WIRE_X(0), PIP_Y(id_N130), WIRE_X(4), PIP_Y(id_N130)}, + {WIRE_X(4), PIP_Y(id_N130), WIRE_X(4), 1. + 1. * wrap_len}, + {WIRE_X(4), 1. + 1. * wrap_len, WIRE_X(3), 1. + 1. * wrap_len}, + {WIRE_X(3), 1. + 1. * wrap_len, WIRE_X(3), PIP_Y(id_S131)}, + {WIRE_X(3), PIP_Y(id_S131), WIRE_X(0), PIP_Y(id_S131)}}}, + // 1 hop SN + {id_SN10, + {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, + {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + PIP_Y(id_N111)}, + {WIRE_X(6), 1. + PIP_Y(id_N111), WIRE_X(0), 1. + PIP_Y(id_N111)}, + {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. + PIP_Y(id_S111)}, + {WIRE_X(5), -1. + PIP_Y(id_S111), WIRE_X(0), -1. + PIP_Y(id_S111)}}}, + {id_SN10_loop_n, + {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, + {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + 1. * wrap_len}, + {WIRE_X(6), 1. + 1. * wrap_len, WIRE_X(5), 1. + 1. * wrap_len}, + {WIRE_X(5), 1. + 1. * wrap_len, WIRE_X(5), PIP_Y(id_SN10)}, + {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. + PIP_Y(id_S111)}, + {WIRE_X(5), -1. + PIP_Y(id_S111), WIRE_X(0), -1. + PIP_Y(id_S111)}}}, + {id_SN10_loop_s, + {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, + {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + PIP_Y(id_N111)}, + {WIRE_X(6), 1. + PIP_Y(id_N111), WIRE_X(0), 1. + PIP_Y(id_N111)}, + {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. * wrap_len}, + {WIRE_X(5), -1. * wrap_len, WIRE_X(6), -1. * wrap_len}, + {WIRE_X(6), -1. * wrap_len, WIRE_X(6), PIP_Y(id_N111)}, + {WIRE_X(6), PIP_Y(id_N111), WIRE_X(0), PIP_Y(id_N111)}}}, + {id_SN20, + {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, + {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + PIP_Y(id_N121)}, + {WIRE_X(8), 1. + PIP_Y(id_N121), WIRE_X(0), 1. + PIP_Y(id_N121)}, + {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. + PIP_Y(id_S121)}, + {WIRE_X(7), -1. + PIP_Y(id_S121), WIRE_X(0), -1. + PIP_Y(id_S121)}}}, + {id_SN20_loop_n, + {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, + {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + 1. * wrap_len}, + {WIRE_X(8), 1. + 1. * wrap_len, WIRE_X(7), 1. + 1. * wrap_len}, + {WIRE_X(7), 1. + 1. * wrap_len, WIRE_X(7), PIP_Y(id_SN10)}, + {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. + PIP_Y(id_S121)}, + {WIRE_X(7), -1. + PIP_Y(id_S121), WIRE_X(0), -1. + PIP_Y(id_S121)}}}, + {id_SN20_loop_s, + {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, + {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + PIP_Y(id_N121)}, + {WIRE_X(8), 1. + PIP_Y(id_N121), WIRE_X(0), 1. + PIP_Y(id_N121)}, + {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. * wrap_len}, + {WIRE_X(7), -1. * wrap_len, WIRE_X(8), -1. * wrap_len}, + {WIRE_X(8), -1. * wrap_len, WIRE_X(8), PIP_Y(id_N121)}, + {WIRE_X(8), PIP_Y(id_N121), WIRE_X(0), PIP_Y(id_N121)}}}, + // 2 hop + {id_S20, + {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, + {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. + PIP_Y(id_S201)}, + {WIRE_X(11), -1. + PIP_Y(id_S201), WIRE_X(0), -1. + PIP_Y(id_S201)}, + {WIRE_X(9), -1. + PIP_Y(id_S201), WIRE_X(9), -2. + PIP_Y(id_S202)}, + {WIRE_X(9), -2. + PIP_Y(id_S202), WIRE_X(0), -2. + PIP_Y(id_S202)}}}, + {id_N20, + {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, + {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + PIP_Y(id_N201)}, + {WIRE_X(12), 1. + PIP_Y(id_N201), WIRE_X(0), 1. + PIP_Y(id_N201)}, + {WIRE_X(10), 1. + PIP_Y(id_N201), WIRE_X(10), 2. + PIP_Y(id_N202)}, + {WIRE_X(10), 2. + PIP_Y(id_N202), WIRE_X(0), 2. + PIP_Y(id_N202)}}}, + {id_S20_loop0, + {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, + {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. * wrap_len}, + {WIRE_X(11), -1. * wrap_len, WIRE_X(12), -1. * wrap_len}, + {WIRE_X(12), -1. * wrap_len, WIRE_X(12), PIP_Y(id_N201)}, + {WIRE_X(12), PIP_Y(id_N201), WIRE_X(0), PIP_Y(id_N201)}, + {WIRE_X(10), PIP_Y(id_N201), WIRE_X(10), 1. + PIP_Y(id_N202)}, + {WIRE_X(10), 1. + PIP_Y(id_N202), WIRE_X(0), 1. + PIP_Y(id_N202)}}}, + {id_N20_loop0, + {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, + {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + 1. * wrap_len}, + {WIRE_X(12), 1. + 1. * wrap_len, WIRE_X(11), 1. + 1. * wrap_len}, + {WIRE_X(11), 1. + 1. * wrap_len, WIRE_X(11), PIP_Y(id_S201)}, + {WIRE_X(11), PIP_Y(id_S201), WIRE_X(0), PIP_Y(id_S201)}, + {WIRE_X(9), PIP_Y(id_S201), WIRE_X(9), -1. + PIP_Y(id_S202)}, + {WIRE_X(9), -1. + PIP_Y(id_S202), WIRE_X(0), -1. + PIP_Y(id_S202)}}}, + {id_S20_loop1, + {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, + {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. + PIP_Y(id_S201)}, + {WIRE_X(11), -1. + PIP_Y(id_S201), WIRE_X(0), -1. + PIP_Y(id_S201)}, + {WIRE_X(9), -1. + PIP_Y(id_S201), WIRE_X(9), -1. + -1. * wrap_len}, + {WIRE_X(9), -1. + -1. * wrap_len, WIRE_X(10), -1. + -1. * wrap_len}, + {WIRE_X(10), -1. + -1. * wrap_len, WIRE_X(10), -1. + PIP_Y(id_N202)}, + {WIRE_X(10), -1. + PIP_Y(id_N202), WIRE_X(0), -1. + PIP_Y(id_N202)}}}, + {id_N20_loop1, + {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, + {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + PIP_Y(id_N201)}, + {WIRE_X(12), 1. + PIP_Y(id_N201), WIRE_X(0), 1. + PIP_Y(id_N201)}, + {WIRE_X(10), 1. + PIP_Y(id_N201), WIRE_X(10), 2. + 1. * wrap_len}, + {WIRE_X(10), 2. + 1. * wrap_len, WIRE_X(9), 2. + 1. * wrap_len}, + {WIRE_X(9), 2. + 1. * wrap_len, WIRE_X(9), 1. + PIP_Y(id_S202)}, + {WIRE_X(9), 1. + PIP_Y(id_S202), WIRE_X(0), 1. + PIP_Y(id_S202)}}}, + {id_S21, + {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, + {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. + PIP_Y(id_S211)}, + {WIRE_X(15), -1. + PIP_Y(id_S211), WIRE_X(0), -1. + PIP_Y(id_S211)}, + {WIRE_X(13), -1. + PIP_Y(id_S211), WIRE_X(13), -2. + PIP_Y(id_S212)}, + {WIRE_X(13), -2. + PIP_Y(id_S212), WIRE_X(0), -2. + PIP_Y(id_S212)}}}, + {id_N21, + {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, + {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + PIP_Y(id_N211)}, + {WIRE_X(16), 1. + PIP_Y(id_N211), WIRE_X(0), 1. + PIP_Y(id_N211)}, + {WIRE_X(14), 1. + PIP_Y(id_N211), WIRE_X(14), 2. + PIP_Y(id_N212)}, + {WIRE_X(14), 2. + PIP_Y(id_N212), WIRE_X(0), 2. + PIP_Y(id_N212)}}}, + {id_S21_loop0, + {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, + {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. * wrap_len}, + {WIRE_X(15), -1. * wrap_len, WIRE_X(16), -1. * wrap_len}, + {WIRE_X(16), -1. * wrap_len, WIRE_X(16), PIP_Y(id_N211)}, + {WIRE_X(16), PIP_Y(id_N211), WIRE_X(0), PIP_Y(id_N211)}, + {WIRE_X(14), PIP_Y(id_N211), WIRE_X(14), 1. + PIP_Y(id_N212)}, + {WIRE_X(14), 1. + PIP_Y(id_N212), WIRE_X(0), 1. + PIP_Y(id_N212)}}}, + {id_N21_loop0, + {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, + {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + 1. * wrap_len}, + {WIRE_X(16), 1. + 1. * wrap_len, WIRE_X(15), 1. + 1. * wrap_len}, + {WIRE_X(15), 1. + 1. * wrap_len, WIRE_X(15), PIP_Y(id_S211)}, + {WIRE_X(15), PIP_Y(id_S211), WIRE_X(0), PIP_Y(id_S211)}, + {WIRE_X(13), PIP_Y(id_S211), WIRE_X(13), -1. + PIP_Y(id_S212)}, + {WIRE_X(13), -1. + PIP_Y(id_S212), WIRE_X(0), -1. + PIP_Y(id_S212)}}}, + {id_S21_loop1, + {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, + {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. + PIP_Y(id_S211)}, + {WIRE_X(15), -1. + PIP_Y(id_S211), WIRE_X(0), -1. + PIP_Y(id_S211)}, + {WIRE_X(13), -1. + PIP_Y(id_S211), WIRE_X(13), -1. + -1. * wrap_len}, + {WIRE_X(13), -1. + -1. * wrap_len, WIRE_X(14), -1. + -1. * wrap_len}, + {WIRE_X(14), -1. + -1. * wrap_len, WIRE_X(14), -1. + PIP_Y(id_N212)}, + {WIRE_X(14), -1. + PIP_Y(id_N212), WIRE_X(0), -1. + PIP_Y(id_N212)}}}, + {id_N21_loop1, + {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, + {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + PIP_Y(id_N211)}, + {WIRE_X(16), 1. + PIP_Y(id_N211), WIRE_X(0), 1. + PIP_Y(id_N211)}, + {WIRE_X(14), 1. + PIP_Y(id_N211), WIRE_X(14), 2. + 1. * wrap_len}, + {WIRE_X(14), 2. + 1. * wrap_len, WIRE_X(13), 2. + 1. * wrap_len}, + {WIRE_X(13), 2. + 1. * wrap_len, WIRE_X(13), 1. + PIP_Y(id_S212)}, + {WIRE_X(13), 1. + PIP_Y(id_S212), WIRE_X(0), 1. + PIP_Y(id_S212)}}}, + {id_S22, + {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, + {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. + PIP_Y(id_S221)}, + {WIRE_X(19), -1. + PIP_Y(id_S221), WIRE_X(0), -1. + PIP_Y(id_S221)}, + {WIRE_X(17), -1. + PIP_Y(id_S221), WIRE_X(17), -2. + PIP_Y(id_S222)}, + {WIRE_X(17), -2. + PIP_Y(id_S222), WIRE_X(0), -2. + PIP_Y(id_S222)}}}, + {id_N22, + {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, + {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + PIP_Y(id_N221)}, + {WIRE_X(20), 1. + PIP_Y(id_N221), WIRE_X(0), 1. + PIP_Y(id_N221)}, + {WIRE_X(18), 1. + PIP_Y(id_N221), WIRE_X(18), 2. + PIP_Y(id_N222)}, + {WIRE_X(18), 2. + PIP_Y(id_N222), WIRE_X(0), 2. + PIP_Y(id_N222)}}}, + {id_S22_loop0, + {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, + {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. * wrap_len}, + {WIRE_X(19), -1. * wrap_len, WIRE_X(20), -1. * wrap_len}, + {WIRE_X(20), -1. * wrap_len, WIRE_X(20), PIP_Y(id_N221)}, + {WIRE_X(20), PIP_Y(id_N221), WIRE_X(0), PIP_Y(id_N221)}, + {WIRE_X(18), PIP_Y(id_N221), WIRE_X(18), 1. + PIP_Y(id_N222)}, + {WIRE_X(18), 1. + PIP_Y(id_N222), WIRE_X(0), 1. + PIP_Y(id_N222)}}}, + {id_N22_loop0, + {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, + {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + 1. * wrap_len}, + {WIRE_X(20), 1. + 1. * wrap_len, WIRE_X(19), 1. + 1. * wrap_len}, + {WIRE_X(19), 1. + 1. * wrap_len, WIRE_X(19), PIP_Y(id_S221)}, + {WIRE_X(19), PIP_Y(id_S221), WIRE_X(0), PIP_Y(id_S221)}, + {WIRE_X(17), PIP_Y(id_S221), WIRE_X(17), -1. + PIP_Y(id_S222)}, + {WIRE_X(17), -1. + PIP_Y(id_S222), WIRE_X(0), -1. + PIP_Y(id_S222)}}}, + {id_S22_loop1, + {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, + {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. + PIP_Y(id_S221)}, + {WIRE_X(19), -1. + PIP_Y(id_S221), WIRE_X(0), -1. + PIP_Y(id_S221)}, + {WIRE_X(17), -1. + PIP_Y(id_S221), WIRE_X(17), -1. + -1. * wrap_len}, + {WIRE_X(17), -1. + -1. * wrap_len, WIRE_X(18), -1. + -1. * wrap_len}, + {WIRE_X(18), -1. + -1. * wrap_len, WIRE_X(18), -1. + PIP_Y(id_N222)}, + {WIRE_X(18), -1. + PIP_Y(id_N222), WIRE_X(0), -1. + PIP_Y(id_N222)}}}, + {id_N22_loop1, + {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, + {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + PIP_Y(id_N221)}, + {WIRE_X(20), 1. + PIP_Y(id_N221), WIRE_X(0), 1. + PIP_Y(id_N221)}, + {WIRE_X(18), 1. + PIP_Y(id_N221), WIRE_X(18), 2. + 1. * wrap_len}, + {WIRE_X(18), 2. + 1. * wrap_len, WIRE_X(17), 2. + 1. * wrap_len}, + {WIRE_X(17), 2. + 1. * wrap_len, WIRE_X(17), 1. + PIP_Y(id_S222)}, + {WIRE_X(17), 1. + PIP_Y(id_S222), WIRE_X(0), 1. + PIP_Y(id_S222)}}}, + {id_S23, + {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, + {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. + PIP_Y(id_S231)}, + {WIRE_X(23), -1. + PIP_Y(id_S231), WIRE_X(0), -1. + PIP_Y(id_S231)}, + {WIRE_X(21), -1. + PIP_Y(id_S231), WIRE_X(21), -2. + PIP_Y(id_S232)}, + {WIRE_X(21), -2. + PIP_Y(id_S232), WIRE_X(0), -2. + PIP_Y(id_S232)}}}, + {id_N23, + {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, + {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + PIP_Y(id_N231)}, + {WIRE_X(24), 1. + PIP_Y(id_N231), WIRE_X(0), 1. + PIP_Y(id_N231)}, + {WIRE_X(22), 1. + PIP_Y(id_N231), WIRE_X(22), 2. + PIP_Y(id_N232)}, + {WIRE_X(22), 2. + PIP_Y(id_N232), WIRE_X(0), 2. + PIP_Y(id_N232)}}}, + {id_S23_loop0, + {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, + {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. * wrap_len}, + {WIRE_X(23), -1. * wrap_len, WIRE_X(24), -1. * wrap_len}, + {WIRE_X(24), -1. * wrap_len, WIRE_X(24), PIP_Y(id_N231)}, + {WIRE_X(24), PIP_Y(id_N231), WIRE_X(0), PIP_Y(id_N231)}, + {WIRE_X(22), PIP_Y(id_N231), WIRE_X(22), 1. + PIP_Y(id_N232)}, + {WIRE_X(22), 1. + PIP_Y(id_N232), WIRE_X(0), 1. + PIP_Y(id_N232)}}}, + {id_N23_loop0, + {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, + {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + 1. * wrap_len}, + {WIRE_X(24), 1. + 1. * wrap_len, WIRE_X(23), 1. + 1. * wrap_len}, + {WIRE_X(23), 1. + 1. * wrap_len, WIRE_X(23), PIP_Y(id_S231)}, + {WIRE_X(23), PIP_Y(id_S231), WIRE_X(0), PIP_Y(id_S231)}, + {WIRE_X(21), PIP_Y(id_S231), WIRE_X(21), -1. + PIP_Y(id_S232)}, + {WIRE_X(21), -1. + PIP_Y(id_S232), WIRE_X(0), -1. + PIP_Y(id_S232)}}}, + {id_S23_loop1, + {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, + {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. + PIP_Y(id_S231)}, + {WIRE_X(23), -1. + PIP_Y(id_S231), WIRE_X(0), -1. + PIP_Y(id_S231)}, + {WIRE_X(21), -1. + PIP_Y(id_S231), WIRE_X(21), -1. + -1. * wrap_len}, + {WIRE_X(21), -1. + -1. * wrap_len, WIRE_X(22), -1. + -1. * wrap_len}, + {WIRE_X(22), -1. + -1. * wrap_len, WIRE_X(22), -1. + PIP_Y(id_N232)}, + {WIRE_X(22), -1. + PIP_Y(id_N232), WIRE_X(0), -1. + PIP_Y(id_N232)}}}, + {id_N23_loop1, + {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, + {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + PIP_Y(id_N231)}, + {WIRE_X(24), 1. + PIP_Y(id_N231), WIRE_X(0), 1. + PIP_Y(id_N231)}, + {WIRE_X(22), 1. + PIP_Y(id_N231), WIRE_X(22), 2. + 1. * wrap_len}, + {WIRE_X(22), 2. + 1. * wrap_len, WIRE_X(21), 2. + 1. * wrap_len}, + {WIRE_X(21), 2. + 1. * wrap_len, WIRE_X(21), 1. + PIP_Y(id_S232)}, + {WIRE_X(21), 1. + PIP_Y(id_S232), WIRE_X(0), 1. + PIP_Y(id_S232)}}}, + {id_S24, + {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, + {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. + PIP_Y(id_S241)}, + {WIRE_X(27), -1. + PIP_Y(id_S241), WIRE_X(0), -1. + PIP_Y(id_S241)}, + {WIRE_X(25), -1. + PIP_Y(id_S241), WIRE_X(25), -2. + PIP_Y(id_S242)}, + {WIRE_X(25), -2. + PIP_Y(id_S242), WIRE_X(0), -2. + PIP_Y(id_S242)}}}, + {id_N24, + {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, + {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + PIP_Y(id_N241)}, + {WIRE_X(28), 1. + PIP_Y(id_N241), WIRE_X(0), 1. + PIP_Y(id_N241)}, + {WIRE_X(26), 1. + PIP_Y(id_N241), WIRE_X(26), 2. + PIP_Y(id_N242)}, + {WIRE_X(26), 2. + PIP_Y(id_N242), WIRE_X(0), 2. + PIP_Y(id_N242)}}}, + {id_S24_loop0, + {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, + {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. * wrap_len}, + {WIRE_X(27), -1. * wrap_len, WIRE_X(28), -1. * wrap_len}, + {WIRE_X(28), -1. * wrap_len, WIRE_X(28), PIP_Y(id_N241)}, + {WIRE_X(28), PIP_Y(id_N241), WIRE_X(0), PIP_Y(id_N241)}, + {WIRE_X(26), PIP_Y(id_N241), WIRE_X(26), 1. + PIP_Y(id_N242)}, + {WIRE_X(26), 1. + PIP_Y(id_N242), WIRE_X(0), 1. + PIP_Y(id_N242)}}}, + {id_N24_loop0, + {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, + {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + 1. * wrap_len}, + {WIRE_X(28), 1. + 1. * wrap_len, WIRE_X(27), 1. + 1. * wrap_len}, + {WIRE_X(27), 1. + 1. * wrap_len, WIRE_X(27), PIP_Y(id_S241)}, + {WIRE_X(27), PIP_Y(id_S241), WIRE_X(0), PIP_Y(id_S241)}, + {WIRE_X(25), PIP_Y(id_S241), WIRE_X(25), -1. + PIP_Y(id_S242)}, + {WIRE_X(25), -1. + PIP_Y(id_S242), WIRE_X(0), -1. + PIP_Y(id_S242)}}}, + {id_S24_loop1, + {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, + {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. + PIP_Y(id_S241)}, + {WIRE_X(27), -1. + PIP_Y(id_S241), WIRE_X(0), -1. + PIP_Y(id_S241)}, + {WIRE_X(25), -1. + PIP_Y(id_S241), WIRE_X(25), -1. + -1. * wrap_len}, + {WIRE_X(25), -1. + -1. * wrap_len, WIRE_X(26), -1. + -1. * wrap_len}, + {WIRE_X(26), -1. + -1. * wrap_len, WIRE_X(26), -1. + PIP_Y(id_N242)}, + {WIRE_X(26), -1. + PIP_Y(id_N242), WIRE_X(0), -1. + PIP_Y(id_N242)}}}, + {id_N24_loop1, + {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, + {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + PIP_Y(id_N241)}, + {WIRE_X(28), 1. + PIP_Y(id_N241), WIRE_X(0), 1. + PIP_Y(id_N241)}, + {WIRE_X(26), 1. + PIP_Y(id_N241), WIRE_X(26), 2. + 1. * wrap_len}, + {WIRE_X(26), 2. + 1. * wrap_len, WIRE_X(25), 2. + 1. * wrap_len}, + {WIRE_X(25), 2. + 1. * wrap_len, WIRE_X(25), 1. + PIP_Y(id_S242)}, + {WIRE_X(25), 1. + PIP_Y(id_S242), WIRE_X(0), 1. + PIP_Y(id_S242)}}}, + {id_S25, + {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, + {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. + PIP_Y(id_S251)}, + {WIRE_X(31), -1. + PIP_Y(id_S251), WIRE_X(0), -1. + PIP_Y(id_S251)}, + {WIRE_X(29), -1. + PIP_Y(id_S251), WIRE_X(29), -2. + PIP_Y(id_S252)}, + {WIRE_X(29), -2. + PIP_Y(id_S252), WIRE_X(0), -2. + PIP_Y(id_S252)}}}, + {id_N25, + {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, + {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + PIP_Y(id_N251)}, + {WIRE_X(32), 1. + PIP_Y(id_N251), WIRE_X(0), 1. + PIP_Y(id_N251)}, + {WIRE_X(30), 1. + PIP_Y(id_N251), WIRE_X(30), 2. + PIP_Y(id_N252)}, + {WIRE_X(30), 2. + PIP_Y(id_N252), WIRE_X(0), 2. + PIP_Y(id_N252)}}}, + {id_S25_loop0, + {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, + {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. * wrap_len}, + {WIRE_X(31), -1. * wrap_len, WIRE_X(32), -1. * wrap_len}, + {WIRE_X(32), -1. * wrap_len, WIRE_X(32), PIP_Y(id_N251)}, + {WIRE_X(32), PIP_Y(id_N251), WIRE_X(0), PIP_Y(id_N251)}, + {WIRE_X(30), PIP_Y(id_N251), WIRE_X(30), 1. + PIP_Y(id_N252)}, + {WIRE_X(30), 1. + PIP_Y(id_N252), WIRE_X(0), 1. + PIP_Y(id_N252)}}}, + {id_N25_loop0, + {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, + {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + 1. * wrap_len}, + {WIRE_X(32), 1. + 1. * wrap_len, WIRE_X(31), 1. + 1. * wrap_len}, + {WIRE_X(31), 1. + 1. * wrap_len, WIRE_X(31), PIP_Y(id_S251)}, + {WIRE_X(31), PIP_Y(id_S251), WIRE_X(0), PIP_Y(id_S251)}, + {WIRE_X(29), PIP_Y(id_S251), WIRE_X(29), -1. + PIP_Y(id_S252)}, + {WIRE_X(29), -1. + PIP_Y(id_S252), WIRE_X(0), -1. + PIP_Y(id_S252)}}}, + {id_S25_loop1, + {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, + {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. + PIP_Y(id_S251)}, + {WIRE_X(31), -1. + PIP_Y(id_S251), WIRE_X(0), -1. + PIP_Y(id_S251)}, + {WIRE_X(29), -1. + PIP_Y(id_S251), WIRE_X(29), -1. + -1. * wrap_len}, + {WIRE_X(29), -1. + -1. * wrap_len, WIRE_X(30), -1. + -1. * wrap_len}, + {WIRE_X(30), -1. + -1. * wrap_len, WIRE_X(30), -1. + PIP_Y(id_N252)}, + {WIRE_X(30), -1. + PIP_Y(id_N252), WIRE_X(0), -1. + PIP_Y(id_N252)}}}, + {id_N25_loop1, + {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, + {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + PIP_Y(id_N251)}, + {WIRE_X(32), 1. + PIP_Y(id_N251), WIRE_X(0), 1. + PIP_Y(id_N251)}, + {WIRE_X(30), 1. + PIP_Y(id_N251), WIRE_X(30), 2. + 1. * wrap_len}, + {WIRE_X(30), 2. + 1. * wrap_len, WIRE_X(29), 2. + 1. * wrap_len}, + {WIRE_X(29), 2. + 1. * wrap_len, WIRE_X(29), 1. + PIP_Y(id_S252)}, + {WIRE_X(29), 1. + PIP_Y(id_S252), WIRE_X(0), 1. + PIP_Y(id_S252)}}}, + {id_S26, + {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, + {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. + PIP_Y(id_S261)}, + {WIRE_X(35), -1. + PIP_Y(id_S261), WIRE_X(0), -1. + PIP_Y(id_S261)}, + {WIRE_X(33), -1. + PIP_Y(id_S261), WIRE_X(33), -2. + PIP_Y(id_S262)}, + {WIRE_X(33), -2. + PIP_Y(id_S262), WIRE_X(0), -2. + PIP_Y(id_S262)}}}, + {id_N26, + {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, + {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + PIP_Y(id_N261)}, + {WIRE_X(36), 1. + PIP_Y(id_N261), WIRE_X(0), 1. + PIP_Y(id_N261)}, + {WIRE_X(34), 1. + PIP_Y(id_N261), WIRE_X(34), 2. + PIP_Y(id_N262)}, + {WIRE_X(34), 2. + PIP_Y(id_N262), WIRE_X(0), 2. + PIP_Y(id_N262)}}}, + {id_S26_loop0, + {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, + {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. * wrap_len}, + {WIRE_X(35), -1. * wrap_len, WIRE_X(36), -1. * wrap_len}, + {WIRE_X(36), -1. * wrap_len, WIRE_X(36), PIP_Y(id_N261)}, + {WIRE_X(36), PIP_Y(id_N261), WIRE_X(0), PIP_Y(id_N261)}, + {WIRE_X(34), PIP_Y(id_N261), WIRE_X(34), 1. + PIP_Y(id_N262)}, + {WIRE_X(34), 1. + PIP_Y(id_N262), WIRE_X(0), 1. + PIP_Y(id_N262)}}}, + {id_N26_loop0, + {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, + {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + 1. * wrap_len}, + {WIRE_X(36), 1. + 1. * wrap_len, WIRE_X(35), 1. + 1. * wrap_len}, + {WIRE_X(35), 1. + 1. * wrap_len, WIRE_X(35), PIP_Y(id_S261)}, + {WIRE_X(35), PIP_Y(id_S261), WIRE_X(0), PIP_Y(id_S261)}, + {WIRE_X(33), PIP_Y(id_S261), WIRE_X(33), -1. + PIP_Y(id_S262)}, + {WIRE_X(33), -1. + PIP_Y(id_S262), WIRE_X(0), -1. + PIP_Y(id_S262)}}}, + {id_S26_loop1, + {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, + {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. + PIP_Y(id_S261)}, + {WIRE_X(35), -1. + PIP_Y(id_S261), WIRE_X(0), -1. + PIP_Y(id_S261)}, + {WIRE_X(33), -1. + PIP_Y(id_S261), WIRE_X(33), -1. + -1. * wrap_len}, + {WIRE_X(33), -1. + -1. * wrap_len, WIRE_X(34), -1. + -1. * wrap_len}, + {WIRE_X(34), -1. + -1. * wrap_len, WIRE_X(34), -1. + PIP_Y(id_N262)}, + {WIRE_X(34), -1. + PIP_Y(id_N262), WIRE_X(0), -1. + PIP_Y(id_N262)}}}, + {id_N26_loop1, + {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, + {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + PIP_Y(id_N261)}, + {WIRE_X(36), 1. + PIP_Y(id_N261), WIRE_X(0), 1. + PIP_Y(id_N261)}, + {WIRE_X(34), 1. + PIP_Y(id_N261), WIRE_X(34), 2. + 1. * wrap_len}, + {WIRE_X(34), 2. + 1. * wrap_len, WIRE_X(33), 2. + 1. * wrap_len}, + {WIRE_X(33), 2. + 1. * wrap_len, WIRE_X(33), 1. + PIP_Y(id_S262)}, + {WIRE_X(33), 1. + PIP_Y(id_S262), WIRE_X(0), 1. + PIP_Y(id_S262)}}}, + {id_S27, + {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, + {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. + PIP_Y(id_S271)}, + {WIRE_X(39), -1. + PIP_Y(id_S271), WIRE_X(0), -1. + PIP_Y(id_S271)}, + {WIRE_X(37), -1. + PIP_Y(id_S271), WIRE_X(37), -2. + PIP_Y(id_S272)}, + {WIRE_X(37), -2. + PIP_Y(id_S272), WIRE_X(0), -2. + PIP_Y(id_S272)}}}, + {id_N27, + {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, + {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + PIP_Y(id_N271)}, + {WIRE_X(40), 1. + PIP_Y(id_N271), WIRE_X(0), 1. + PIP_Y(id_N271)}, + {WIRE_X(38), 1. + PIP_Y(id_N271), WIRE_X(38), 2. + PIP_Y(id_N272)}, + {WIRE_X(38), 2. + PIP_Y(id_N272), WIRE_X(0), 2. + PIP_Y(id_N272)}}}, + {id_S27_loop0, + {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, + {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. * wrap_len}, + {WIRE_X(39), -1. * wrap_len, WIRE_X(40), -1. * wrap_len}, + {WIRE_X(40), -1. * wrap_len, WIRE_X(40), PIP_Y(id_N271)}, + {WIRE_X(40), PIP_Y(id_N271), WIRE_X(0), PIP_Y(id_N271)}, + {WIRE_X(38), PIP_Y(id_N271), WIRE_X(38), 1. + PIP_Y(id_N272)}, + {WIRE_X(38), 1. + PIP_Y(id_N272), WIRE_X(0), 1. + PIP_Y(id_N272)}}}, + {id_N27_loop0, + {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, + {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + 1. * wrap_len}, + {WIRE_X(40), 1. + 1. * wrap_len, WIRE_X(39), 1. + 1. * wrap_len}, + {WIRE_X(39), 1. + 1. * wrap_len, WIRE_X(39), PIP_Y(id_S271)}, + {WIRE_X(39), PIP_Y(id_S271), WIRE_X(0), PIP_Y(id_S271)}, + {WIRE_X(37), PIP_Y(id_S271), WIRE_X(37), -1. + PIP_Y(id_S272)}, + {WIRE_X(37), -1. + PIP_Y(id_S272), WIRE_X(0), -1. + PIP_Y(id_S272)}}}, + {id_S27_loop1, + {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, + {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. + PIP_Y(id_S271)}, + {WIRE_X(39), -1. + PIP_Y(id_S271), WIRE_X(0), -1. + PIP_Y(id_S271)}, + {WIRE_X(37), -1. + PIP_Y(id_S271), WIRE_X(37), -1. + -1. * wrap_len}, + {WIRE_X(37), -1. + -1. * wrap_len, WIRE_X(38), -1. + -1. * wrap_len}, + {WIRE_X(38), -1. + -1. * wrap_len, WIRE_X(38), -1. + PIP_Y(id_N272)}, + {WIRE_X(38), -1. + PIP_Y(id_N272), WIRE_X(0), -1. + PIP_Y(id_N272)}}}, + {id_N27_loop1, + {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, + {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + PIP_Y(id_N271)}, + {WIRE_X(40), 1. + PIP_Y(id_N271), WIRE_X(0), 1. + PIP_Y(id_N271)}, + {WIRE_X(38), 1. + PIP_Y(id_N271), WIRE_X(38), 2. + 1. * wrap_len}, + {WIRE_X(38), 2. + 1. * wrap_len, WIRE_X(37), 2. + 1. * wrap_len}, + {WIRE_X(37), 2. + 1. * wrap_len, WIRE_X(37), 1. + PIP_Y(id_S272)}, + {WIRE_X(37), 1. + PIP_Y(id_S272), WIRE_X(0), 1. + PIP_Y(id_S272)}}}, +// clock taps +#define CLK_GT00_X 41.f +#define CLK_GT10_X 46.f +// 4 hop +#define HOP4X_START (CLK_GT00_X + 10.f) +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START) + {id_S80, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, + {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N808) - 7.}, + {HOP4X(2), PIP_Y(id_N808) - 7., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S808) - 8.}, + {HOP4X(0), PIP_Y(id_S808) - 8., WIRE_X(0), PIP_Y(id_S808) - 8.}}}, + {id_N80, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, + {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N800) + 7.}, + {HOP4X(3), PIP_Y(id_N800) + 7., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N808) + 8.}, + {HOP4X(1), PIP_Y(id_N808) + 8., WIRE_X(0), PIP_Y(id_N808) + 8.}}}, + {id_S80_loop0, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N800) - 0.}, + {HOP4X(15), PIP_Y(id_N800) - 0., HOP4X(13), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N800) + 1.}, + {HOP4X(13), PIP_Y(id_N800) + 1., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N800) + 2.}, + {HOP4X(11), PIP_Y(id_N800) + 2., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N800) + 3.}, + {HOP4X(9), PIP_Y(id_N800) + 3., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N804) + 3., WIRE_X(0), PIP_Y(id_N804) + 3.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N800) + 4.}, + {HOP4X(7), PIP_Y(id_N800) + 4., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N800) + 5.}, + {HOP4X(5), PIP_Y(id_N800) + 5., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N800) + 6.}, + {HOP4X(3), PIP_Y(id_N800) + 6., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N808) + 7.}, + {HOP4X(1), PIP_Y(id_N808) + 7., WIRE_X(0), PIP_Y(id_N808) + 7.}}}, + {id_S80_loop1, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N800) - 1.}, + {HOP4X(13), PIP_Y(id_N800) - 1., HOP4X(11), PIP_Y(id_N800) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N800) - 0.}, + {HOP4X(11), PIP_Y(id_N800) - 0., HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N800) + 1.}, + {HOP4X(9), PIP_Y(id_N800) + 1., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N804) + 1., WIRE_X(0), PIP_Y(id_N804) + 1.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N800) + 2.}, + {HOP4X(7), PIP_Y(id_N800) + 2., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N800) + 3.}, + {HOP4X(5), PIP_Y(id_N800) + 3., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N800) + 4.}, + {HOP4X(3), PIP_Y(id_N800) + 4., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N808) + 5.}, + {HOP4X(1), PIP_Y(id_N808) + 5., WIRE_X(0), PIP_Y(id_N808) + 5.}}}, + {id_S80_loop2, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N800) - 2.}, + {HOP4X(11), PIP_Y(id_N800) - 2., HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N800) - 1.}, + {HOP4X(9), PIP_Y(id_N800) - 1., HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N804) - 1., WIRE_X(0), PIP_Y(id_N804) - 1.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N800) - 0.}, + {HOP4X(7), PIP_Y(id_N800) - 0., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N800) + 1.}, + {HOP4X(5), PIP_Y(id_N800) + 1., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N800) + 2.}, + {HOP4X(3), PIP_Y(id_N800) + 2., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N808) + 3.}, + {HOP4X(1), PIP_Y(id_N808) + 3., WIRE_X(0), PIP_Y(id_N808) + 3.}}}, + {id_S80_loop3, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N800) - 3.}, + {HOP4X(9), PIP_Y(id_N804) - 3., WIRE_X(0), PIP_Y(id_N804) - 3.}, + {HOP4X(9), PIP_Y(id_N800) - 3., HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N800) - 2.}, + {HOP4X(7), PIP_Y(id_N800) - 2., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N800) - 1.}, + {HOP4X(5), PIP_Y(id_N800) - 1., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N800) - 0.}, + {HOP4X(3), PIP_Y(id_N800) - 0., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N808) + 1.}, + {HOP4X(1), PIP_Y(id_N808) + 1., WIRE_X(0), PIP_Y(id_N808) + 1.}}}, + {id_S80_loop4, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N800) - 4.}, + {HOP4X(7), PIP_Y(id_N800) - 4., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N800) - 3.}, + {HOP4X(5), PIP_Y(id_N800) - 3., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N800) - 2.}, + {HOP4X(3), PIP_Y(id_N800) - 2., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N808) - 1.}, + {HOP4X(1), PIP_Y(id_N808) - 1., WIRE_X(0), PIP_Y(id_N808) - 1.}}}, + {id_S80_loop5, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N800) - 5.}, + {HOP4X(5), PIP_Y(id_N800) - 5., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N800) - 4.}, + {HOP4X(3), PIP_Y(id_N800) - 4., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N808) - 3.}, + {HOP4X(1), PIP_Y(id_N808) - 3., WIRE_X(0), PIP_Y(id_N808) - 3.}}}, + {id_S80_loop6, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, + {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N800) - 6.}, + {HOP4X(3), PIP_Y(id_N800) - 6., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N808) - 5.}, + {HOP4X(1), PIP_Y(id_N808) - 5., WIRE_X(0), PIP_Y(id_N808) - 5.}}}, + {id_S80_loop7, + {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, + {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, + {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, + {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, + {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, + {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, + {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, + {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, + {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, + {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N808) - 7.}, + {HOP4X(2), PIP_Y(id_N808) - 7., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N808) - 7.}, + {HOP4X(1), PIP_Y(id_N808) - 7., WIRE_X(0), PIP_Y(id_N808) - 7.}}}, + {id_N80_loop0, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N808) + 0.}, + {HOP4X(14), PIP_Y(id_N808) + 0., HOP4X(12), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N808) - 1.}, + {HOP4X(12), PIP_Y(id_N808) - 1., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N808) - 2.}, + {HOP4X(10), PIP_Y(id_N808) - 2., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N808) - 3.}, + {HOP4X(8), PIP_Y(id_S804) - 3., WIRE_X(0), PIP_Y(id_S804) - 3.}, + {HOP4X(8), PIP_Y(id_N808) - 3., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N808) - 4.}, + {HOP4X(6), PIP_Y(id_N808) - 4., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N808) - 5.}, + {HOP4X(4), PIP_Y(id_N808) - 5., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N808) - 6.}, + {HOP4X(2), PIP_Y(id_N808) - 6., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S808) - 7.}, + {HOP4X(0), PIP_Y(id_S808) - 7., WIRE_X(0), PIP_Y(id_S808) - 7.}}}, + {id_N80_loop1, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N808) + 1.}, + {HOP4X(12), PIP_Y(id_N808) + 1., HOP4X(10), PIP_Y(id_N808) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N808) + 0.}, + {HOP4X(10), PIP_Y(id_N808) + 0., HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N808) - 1.}, + {HOP4X(8), PIP_Y(id_S804) - 1., WIRE_X(0), PIP_Y(id_S804) - 1.}, + {HOP4X(8), PIP_Y(id_N808) - 1., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N808) - 2.}, + {HOP4X(6), PIP_Y(id_N808) - 2., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N808) - 3.}, + {HOP4X(4), PIP_Y(id_N808) - 3., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N808) - 4.}, + {HOP4X(2), PIP_Y(id_N808) - 4., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S808) - 5.}, + {HOP4X(0), PIP_Y(id_S808) - 5., WIRE_X(0), PIP_Y(id_S808) - 5.}}}, + {id_N80_loop2, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N808) + 2.}, + {HOP4X(10), PIP_Y(id_N808) + 2., HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N808) + 1.}, + {HOP4X(8), PIP_Y(id_S804) + 1., WIRE_X(0), PIP_Y(id_S804) + 1.}, + {HOP4X(8), PIP_Y(id_N808) + 1., HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N808) + 0.}, + {HOP4X(6), PIP_Y(id_N808) + 0., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N808) - 1.}, + {HOP4X(4), PIP_Y(id_N808) - 1., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N808) - 2.}, + {HOP4X(2), PIP_Y(id_N808) - 2., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S808) - 3.}, + {HOP4X(0), PIP_Y(id_S808) - 3., WIRE_X(0), PIP_Y(id_S808) - 3.}}}, + {id_N80_loop3, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N808) + 3.}, + {HOP4X(8), PIP_Y(id_S804) + 3., WIRE_X(0), PIP_Y(id_S804) + 3.}, + {HOP4X(8), PIP_Y(id_N808) + 3., HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N808) + 2.}, + {HOP4X(6), PIP_Y(id_N808) + 2., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N808) + 1.}, + {HOP4X(4), PIP_Y(id_N808) + 1., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N808) + 0.}, + {HOP4X(2), PIP_Y(id_N808) + 0., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S808) - 1.}, + {HOP4X(0), PIP_Y(id_S808) - 1., WIRE_X(0), PIP_Y(id_S808) - 1.}}}, + {id_N80_loop4, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N808) + 4.}, + {HOP4X(6), PIP_Y(id_N808) + 4., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N808) + 3.}, + {HOP4X(4), PIP_Y(id_N808) + 3., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N808) + 2.}, + {HOP4X(2), PIP_Y(id_N808) + 2., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S808) + 1.}, + {HOP4X(0), PIP_Y(id_S808) + 1., WIRE_X(0), PIP_Y(id_S808) + 1.}}}, + {id_N80_loop5, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N808) + 5.}, + {HOP4X(4), PIP_Y(id_N808) + 5., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N808) + 4.}, + {HOP4X(2), PIP_Y(id_N808) + 4., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S808) + 3.}, + {HOP4X(0), PIP_Y(id_S808) + 3., WIRE_X(0), PIP_Y(id_S808) + 3.}}}, + {id_N80_loop6, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, + {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N808) + 6.}, + {HOP4X(2), PIP_Y(id_N808) + 6., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S808) + 5.}, + {HOP4X(0), PIP_Y(id_S808) + 5., WIRE_X(0), PIP_Y(id_S808) + 5.}}}, + {id_N80_loop7, + {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, + {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, + {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, + {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, + {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, + {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, + {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, + {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, + {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N800) + 7.}, + {HOP4X(3), PIP_Y(id_N800) + 7., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S808) + 7.}, + {HOP4X(0), PIP_Y(id_S808) + 7., WIRE_X(0), PIP_Y(id_S808) + 7.}}}, + +#undef HOP4X +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f) + {id_S81, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, + {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N818) - 7.}, + {HOP4X(2), PIP_Y(id_N818) - 7., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S818) - 8.}, + {HOP4X(0), PIP_Y(id_S818) - 8., WIRE_X(0), PIP_Y(id_S818) - 8.}}}, + {id_N81, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, + {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N810) + 7.}, + {HOP4X(3), PIP_Y(id_N810) + 7., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N818) + 8.}, + {HOP4X(1), PIP_Y(id_N818) + 8., WIRE_X(0), PIP_Y(id_N818) + 8.}}}, + {id_S81_loop0, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N810) - 0.}, + {HOP4X(15), PIP_Y(id_N810) - 0., HOP4X(13), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N810) + 1.}, + {HOP4X(13), PIP_Y(id_N810) + 1., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N810) + 2.}, + {HOP4X(11), PIP_Y(id_N810) + 2., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N810) + 3.}, + {HOP4X(9), PIP_Y(id_N810) + 3., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N814) + 3., WIRE_X(0), PIP_Y(id_N814) + 3.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N810) + 4.}, + {HOP4X(7), PIP_Y(id_N810) + 4., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N810) + 5.}, + {HOP4X(5), PIP_Y(id_N810) + 5., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N810) + 6.}, + {HOP4X(3), PIP_Y(id_N810) + 6., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N818) + 7.}, + {HOP4X(1), PIP_Y(id_N818) + 7., WIRE_X(0), PIP_Y(id_N818) + 7.}}}, + {id_S81_loop1, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N810) - 1.}, + {HOP4X(13), PIP_Y(id_N810) - 1., HOP4X(11), PIP_Y(id_N810) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N810) - 0.}, + {HOP4X(11), PIP_Y(id_N810) - 0., HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N810) + 1.}, + {HOP4X(9), PIP_Y(id_N810) + 1., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N814) + 1., WIRE_X(0), PIP_Y(id_N814) + 1.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N810) + 2.}, + {HOP4X(7), PIP_Y(id_N810) + 2., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N810) + 3.}, + {HOP4X(5), PIP_Y(id_N810) + 3., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N810) + 4.}, + {HOP4X(3), PIP_Y(id_N810) + 4., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N818) + 5.}, + {HOP4X(1), PIP_Y(id_N818) + 5., WIRE_X(0), PIP_Y(id_N818) + 5.}}}, + {id_S81_loop2, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N810) - 2.}, + {HOP4X(11), PIP_Y(id_N810) - 2., HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N810) - 1.}, + {HOP4X(9), PIP_Y(id_N810) - 1., HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N814) - 1., WIRE_X(0), PIP_Y(id_N814) - 1.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N810) - 0.}, + {HOP4X(7), PIP_Y(id_N810) - 0., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N810) + 1.}, + {HOP4X(5), PIP_Y(id_N810) + 1., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N810) + 2.}, + {HOP4X(3), PIP_Y(id_N810) + 2., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N818) + 3.}, + {HOP4X(1), PIP_Y(id_N818) + 3., WIRE_X(0), PIP_Y(id_N818) + 3.}}}, + {id_S81_loop3, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N810) - 3.}, + {HOP4X(9), PIP_Y(id_N814) - 3., WIRE_X(0), PIP_Y(id_N814) - 3.}, + {HOP4X(9), PIP_Y(id_N810) - 3., HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N810) - 2.}, + {HOP4X(7), PIP_Y(id_N810) - 2., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N810) - 1.}, + {HOP4X(5), PIP_Y(id_N810) - 1., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N810) - 0.}, + {HOP4X(3), PIP_Y(id_N810) - 0., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N818) + 1.}, + {HOP4X(1), PIP_Y(id_N818) + 1., WIRE_X(0), PIP_Y(id_N818) + 1.}}}, + {id_S81_loop4, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N810) - 4.}, + {HOP4X(7), PIP_Y(id_N810) - 4., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N810) - 3.}, + {HOP4X(5), PIP_Y(id_N810) - 3., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N810) - 2.}, + {HOP4X(3), PIP_Y(id_N810) - 2., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N818) - 1.}, + {HOP4X(1), PIP_Y(id_N818) - 1., WIRE_X(0), PIP_Y(id_N818) - 1.}}}, + {id_S81_loop5, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N810) - 5.}, + {HOP4X(5), PIP_Y(id_N810) - 5., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N810) - 4.}, + {HOP4X(3), PIP_Y(id_N810) - 4., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N818) - 3.}, + {HOP4X(1), PIP_Y(id_N818) - 3., WIRE_X(0), PIP_Y(id_N818) - 3.}}}, + {id_S81_loop6, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, + {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N810) - 6.}, + {HOP4X(3), PIP_Y(id_N810) - 6., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N818) - 5.}, + {HOP4X(1), PIP_Y(id_N818) - 5., WIRE_X(0), PIP_Y(id_N818) - 5.}}}, + {id_S81_loop7, + {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, + {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, + {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, + {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, + {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, + {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, + {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, + {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, + {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, + {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N818) - 7.}, + {HOP4X(2), PIP_Y(id_N818) - 7., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N818) - 7.}, + {HOP4X(1), PIP_Y(id_N818) - 7., WIRE_X(0), PIP_Y(id_N818) - 7.}}}, + {id_N81_loop0, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N818) + 0.}, + {HOP4X(14), PIP_Y(id_N818) + 0., HOP4X(12), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N818) - 1.}, + {HOP4X(12), PIP_Y(id_N818) - 1., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N818) - 2.}, + {HOP4X(10), PIP_Y(id_N818) - 2., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N818) - 3.}, + {HOP4X(8), PIP_Y(id_S814) - 3., WIRE_X(0), PIP_Y(id_S814) - 3.}, + {HOP4X(8), PIP_Y(id_N818) - 3., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N818) - 4.}, + {HOP4X(6), PIP_Y(id_N818) - 4., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N818) - 5.}, + {HOP4X(4), PIP_Y(id_N818) - 5., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N818) - 6.}, + {HOP4X(2), PIP_Y(id_N818) - 6., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S818) - 7.}, + {HOP4X(0), PIP_Y(id_S818) - 7., WIRE_X(0), PIP_Y(id_S818) - 7.}}}, + {id_N81_loop1, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N818) + 1.}, + {HOP4X(12), PIP_Y(id_N818) + 1., HOP4X(10), PIP_Y(id_N818) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N818) + 0.}, + {HOP4X(10), PIP_Y(id_N818) + 0., HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N818) - 1.}, + {HOP4X(8), PIP_Y(id_S814) - 1., WIRE_X(0), PIP_Y(id_S814) - 1.}, + {HOP4X(8), PIP_Y(id_N818) - 1., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N818) - 2.}, + {HOP4X(6), PIP_Y(id_N818) - 2., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N818) - 3.}, + {HOP4X(4), PIP_Y(id_N818) - 3., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N818) - 4.}, + {HOP4X(2), PIP_Y(id_N818) - 4., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S818) - 5.}, + {HOP4X(0), PIP_Y(id_S818) - 5., WIRE_X(0), PIP_Y(id_S818) - 5.}}}, + {id_N81_loop2, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N818) + 2.}, + {HOP4X(10), PIP_Y(id_N818) + 2., HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N818) + 1.}, + {HOP4X(8), PIP_Y(id_S814) + 1., WIRE_X(0), PIP_Y(id_S814) + 1.}, + {HOP4X(8), PIP_Y(id_N818) + 1., HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N818) + 0.}, + {HOP4X(6), PIP_Y(id_N818) + 0., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N818) - 1.}, + {HOP4X(4), PIP_Y(id_N818) - 1., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N818) - 2.}, + {HOP4X(2), PIP_Y(id_N818) - 2., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S818) - 3.}, + {HOP4X(0), PIP_Y(id_S818) - 3., WIRE_X(0), PIP_Y(id_S818) - 3.}}}, + {id_N81_loop3, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N818) + 3.}, + {HOP4X(8), PIP_Y(id_S814) + 3., WIRE_X(0), PIP_Y(id_S814) + 3.}, + {HOP4X(8), PIP_Y(id_N818) + 3., HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N818) + 2.}, + {HOP4X(6), PIP_Y(id_N818) + 2., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N818) + 1.}, + {HOP4X(4), PIP_Y(id_N818) + 1., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N818) + 0.}, + {HOP4X(2), PIP_Y(id_N818) + 0., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S818) - 1.}, + {HOP4X(0), PIP_Y(id_S818) - 1., WIRE_X(0), PIP_Y(id_S818) - 1.}}}, + {id_N81_loop4, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N818) + 4.}, + {HOP4X(6), PIP_Y(id_N818) + 4., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N818) + 3.}, + {HOP4X(4), PIP_Y(id_N818) + 3., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N818) + 2.}, + {HOP4X(2), PIP_Y(id_N818) + 2., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S818) + 1.}, + {HOP4X(0), PIP_Y(id_S818) + 1., WIRE_X(0), PIP_Y(id_S818) + 1.}}}, + {id_N81_loop5, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N818) + 5.}, + {HOP4X(4), PIP_Y(id_N818) + 5., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N818) + 4.}, + {HOP4X(2), PIP_Y(id_N818) + 4., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S818) + 3.}, + {HOP4X(0), PIP_Y(id_S818) + 3., WIRE_X(0), PIP_Y(id_S818) + 3.}}}, + {id_N81_loop6, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, + {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N818) + 6.}, + {HOP4X(2), PIP_Y(id_N818) + 6., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S818) + 5.}, + {HOP4X(0), PIP_Y(id_S818) + 5., WIRE_X(0), PIP_Y(id_S818) + 5.}}}, + {id_N81_loop7, + {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, + {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, + {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, + {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, + {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, + {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, + {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, + {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, + {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N810) + 7.}, + {HOP4X(3), PIP_Y(id_N810) + 7., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S818) + 7.}, + {HOP4X(0), PIP_Y(id_S818) + 7., WIRE_X(0), PIP_Y(id_S818) + 7.}}}, + +#undef HOP4X +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f + 18.f) + {id_S82, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, + {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N828) - 7.}, + {HOP4X(2), PIP_Y(id_N828) - 7., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S828) - 8.}, + {HOP4X(0), PIP_Y(id_S828) - 8., WIRE_X(0), PIP_Y(id_S828) - 8.}}}, + {id_N82, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, + {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N820) + 7.}, + {HOP4X(3), PIP_Y(id_N820) + 7., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N828) + 8.}, + {HOP4X(1), PIP_Y(id_N828) + 8., WIRE_X(0), PIP_Y(id_N828) + 8.}}}, + {id_S82_loop0, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N820) - 0.}, + {HOP4X(15), PIP_Y(id_N820) - 0., HOP4X(13), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N820) + 1.}, + {HOP4X(13), PIP_Y(id_N820) + 1., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N820) + 2.}, + {HOP4X(11), PIP_Y(id_N820) + 2., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N820) + 3.}, + {HOP4X(9), PIP_Y(id_N820) + 3., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N824) + 3., WIRE_X(0), PIP_Y(id_N824) + 3.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N820) + 4.}, + {HOP4X(7), PIP_Y(id_N820) + 4., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N820) + 5.}, + {HOP4X(5), PIP_Y(id_N820) + 5., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N820) + 6.}, + {HOP4X(3), PIP_Y(id_N820) + 6., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N828) + 7.}, + {HOP4X(1), PIP_Y(id_N828) + 7., WIRE_X(0), PIP_Y(id_N828) + 7.}}}, + {id_S82_loop1, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N820) - 1.}, + {HOP4X(13), PIP_Y(id_N820) - 1., HOP4X(11), PIP_Y(id_N820) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N820) - 0.}, + {HOP4X(11), PIP_Y(id_N820) - 0., HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N820) + 1.}, + {HOP4X(9), PIP_Y(id_N820) + 1., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N824) + 1., WIRE_X(0), PIP_Y(id_N824) + 1.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N820) + 2.}, + {HOP4X(7), PIP_Y(id_N820) + 2., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N820) + 3.}, + {HOP4X(5), PIP_Y(id_N820) + 3., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N820) + 4.}, + {HOP4X(3), PIP_Y(id_N820) + 4., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N828) + 5.}, + {HOP4X(1), PIP_Y(id_N828) + 5., WIRE_X(0), PIP_Y(id_N828) + 5.}}}, + {id_S82_loop2, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N820) - 2.}, + {HOP4X(11), PIP_Y(id_N820) - 2., HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N820) - 1.}, + {HOP4X(9), PIP_Y(id_N820) - 1., HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N824) - 1., WIRE_X(0), PIP_Y(id_N824) - 1.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N820) - 0.}, + {HOP4X(7), PIP_Y(id_N820) - 0., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N820) + 1.}, + {HOP4X(5), PIP_Y(id_N820) + 1., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N820) + 2.}, + {HOP4X(3), PIP_Y(id_N820) + 2., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N828) + 3.}, + {HOP4X(1), PIP_Y(id_N828) + 3., WIRE_X(0), PIP_Y(id_N828) + 3.}}}, + {id_S82_loop3, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N820) - 3.}, + {HOP4X(9), PIP_Y(id_N824) - 3., WIRE_X(0), PIP_Y(id_N824) - 3.}, + {HOP4X(9), PIP_Y(id_N820) - 3., HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N820) - 2.}, + {HOP4X(7), PIP_Y(id_N820) - 2., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N820) - 1.}, + {HOP4X(5), PIP_Y(id_N820) - 1., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N820) - 0.}, + {HOP4X(3), PIP_Y(id_N820) - 0., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N828) + 1.}, + {HOP4X(1), PIP_Y(id_N828) + 1., WIRE_X(0), PIP_Y(id_N828) + 1.}}}, + {id_S82_loop4, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N820) - 4.}, + {HOP4X(7), PIP_Y(id_N820) - 4., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N820) - 3.}, + {HOP4X(5), PIP_Y(id_N820) - 3., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N820) - 2.}, + {HOP4X(3), PIP_Y(id_N820) - 2., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N828) - 1.}, + {HOP4X(1), PIP_Y(id_N828) - 1., WIRE_X(0), PIP_Y(id_N828) - 1.}}}, + {id_S82_loop5, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N820) - 5.}, + {HOP4X(5), PIP_Y(id_N820) - 5., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N820) - 4.}, + {HOP4X(3), PIP_Y(id_N820) - 4., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N828) - 3.}, + {HOP4X(1), PIP_Y(id_N828) - 3., WIRE_X(0), PIP_Y(id_N828) - 3.}}}, + {id_S82_loop6, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, + {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N820) - 6.}, + {HOP4X(3), PIP_Y(id_N820) - 6., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N828) - 5.}, + {HOP4X(1), PIP_Y(id_N828) - 5., WIRE_X(0), PIP_Y(id_N828) - 5.}}}, + {id_S82_loop7, + {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, + {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, + {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, + {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, + {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, + {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, + {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, + {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, + {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, + {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N828) - 7.}, + {HOP4X(2), PIP_Y(id_N828) - 7., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N828) - 7.}, + {HOP4X(1), PIP_Y(id_N828) - 7., WIRE_X(0), PIP_Y(id_N828) - 7.}}}, + {id_N82_loop0, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N828) + 0.}, + {HOP4X(14), PIP_Y(id_N828) + 0., HOP4X(12), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N828) - 1.}, + {HOP4X(12), PIP_Y(id_N828) - 1., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N828) - 2.}, + {HOP4X(10), PIP_Y(id_N828) - 2., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N828) - 3.}, + {HOP4X(8), PIP_Y(id_S824) - 3., WIRE_X(0), PIP_Y(id_S824) - 3.}, + {HOP4X(8), PIP_Y(id_N828) - 3., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N828) - 4.}, + {HOP4X(6), PIP_Y(id_N828) - 4., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N828) - 5.}, + {HOP4X(4), PIP_Y(id_N828) - 5., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N828) - 6.}, + {HOP4X(2), PIP_Y(id_N828) - 6., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S828) - 7.}, + {HOP4X(0), PIP_Y(id_S828) - 7., WIRE_X(0), PIP_Y(id_S828) - 7.}}}, + {id_N82_loop1, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N828) + 1.}, + {HOP4X(12), PIP_Y(id_N828) + 1., HOP4X(10), PIP_Y(id_N828) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N828) + 0.}, + {HOP4X(10), PIP_Y(id_N828) + 0., HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N828) - 1.}, + {HOP4X(8), PIP_Y(id_S824) - 1., WIRE_X(0), PIP_Y(id_S824) - 1.}, + {HOP4X(8), PIP_Y(id_N828) - 1., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N828) - 2.}, + {HOP4X(6), PIP_Y(id_N828) - 2., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N828) - 3.}, + {HOP4X(4), PIP_Y(id_N828) - 3., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N828) - 4.}, + {HOP4X(2), PIP_Y(id_N828) - 4., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S828) - 5.}, + {HOP4X(0), PIP_Y(id_S828) - 5., WIRE_X(0), PIP_Y(id_S828) - 5.}}}, + {id_N82_loop2, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N828) + 2.}, + {HOP4X(10), PIP_Y(id_N828) + 2., HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N828) + 1.}, + {HOP4X(8), PIP_Y(id_S824) + 1., WIRE_X(0), PIP_Y(id_S824) + 1.}, + {HOP4X(8), PIP_Y(id_N828) + 1., HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N828) + 0.}, + {HOP4X(6), PIP_Y(id_N828) + 0., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N828) - 1.}, + {HOP4X(4), PIP_Y(id_N828) - 1., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N828) - 2.}, + {HOP4X(2), PIP_Y(id_N828) - 2., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S828) - 3.}, + {HOP4X(0), PIP_Y(id_S828) - 3., WIRE_X(0), PIP_Y(id_S828) - 3.}}}, + {id_N82_loop3, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N828) + 3.}, + {HOP4X(8), PIP_Y(id_S824) + 3., WIRE_X(0), PIP_Y(id_S824) + 3.}, + {HOP4X(8), PIP_Y(id_N828) + 3., HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N828) + 2.}, + {HOP4X(6), PIP_Y(id_N828) + 2., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N828) + 1.}, + {HOP4X(4), PIP_Y(id_N828) + 1., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N828) + 0.}, + {HOP4X(2), PIP_Y(id_N828) + 0., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S828) - 1.}, + {HOP4X(0), PIP_Y(id_S828) - 1., WIRE_X(0), PIP_Y(id_S828) - 1.}}}, + {id_N82_loop4, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N828) + 4.}, + {HOP4X(6), PIP_Y(id_N828) + 4., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N828) + 3.}, + {HOP4X(4), PIP_Y(id_N828) + 3., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N828) + 2.}, + {HOP4X(2), PIP_Y(id_N828) + 2., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S828) + 1.}, + {HOP4X(0), PIP_Y(id_S828) + 1., WIRE_X(0), PIP_Y(id_S828) + 1.}}}, + {id_N82_loop5, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N828) + 5.}, + {HOP4X(4), PIP_Y(id_N828) + 5., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N828) + 4.}, + {HOP4X(2), PIP_Y(id_N828) + 4., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S828) + 3.}, + {HOP4X(0), PIP_Y(id_S828) + 3., WIRE_X(0), PIP_Y(id_S828) + 3.}}}, + {id_N82_loop6, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, + {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N828) + 6.}, + {HOP4X(2), PIP_Y(id_N828) + 6., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S828) + 5.}, + {HOP4X(0), PIP_Y(id_S828) + 5., WIRE_X(0), PIP_Y(id_S828) + 5.}}}, + {id_N82_loop7, + {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, + {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, + {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, + {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, + {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, + {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, + {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, + {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, + {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N820) + 7.}, + {HOP4X(3), PIP_Y(id_N820) + 7., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S828) + 7.}, + {HOP4X(0), PIP_Y(id_S828) + 7., WIRE_X(0), PIP_Y(id_S828) + 7.}}}, + +#undef HOP4X +#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f + 18.f + 18.f) + {id_S83, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, + {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N838) - 7.}, + {HOP4X(2), PIP_Y(id_N838) - 7., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S838) - 8.}, + {HOP4X(0), PIP_Y(id_S838) - 8., WIRE_X(0), PIP_Y(id_S838) - 8.}}}, + {id_N83, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, + {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N830) + 7.}, + {HOP4X(3), PIP_Y(id_N830) + 7., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N838) + 8.}, + {HOP4X(1), PIP_Y(id_N838) + 8., WIRE_X(0), PIP_Y(id_N838) + 8.}}}, + {id_S83_loop0, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, + {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, + {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N830) - 0.}, + {HOP4X(15), PIP_Y(id_N830) - 0., HOP4X(13), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N830) + 1.}, + {HOP4X(13), PIP_Y(id_N830) + 1., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N830) + 2.}, + {HOP4X(11), PIP_Y(id_N830) + 2., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N830) + 3.}, + {HOP4X(9), PIP_Y(id_N830) + 3., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N834) + 3., WIRE_X(0), PIP_Y(id_N834) + 3.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N830) + 4.}, + {HOP4X(7), PIP_Y(id_N830) + 4., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N830) + 5.}, + {HOP4X(5), PIP_Y(id_N830) + 5., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N830) + 6.}, + {HOP4X(3), PIP_Y(id_N830) + 6., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N838) + 7.}, + {HOP4X(1), PIP_Y(id_N838) + 7., WIRE_X(0), PIP_Y(id_N838) + 7.}}}, + {id_S83_loop1, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, + {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, + {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N830) - 1.}, + {HOP4X(13), PIP_Y(id_N830) - 1., HOP4X(11), PIP_Y(id_N830) + left_wire_dist - 1.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N830) - 0.}, + {HOP4X(11), PIP_Y(id_N830) - 0., HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N830) + 1.}, + {HOP4X(9), PIP_Y(id_N830) + 1., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(9), PIP_Y(id_N834) + 1., WIRE_X(0), PIP_Y(id_N834) + 1.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N830) + 2.}, + {HOP4X(7), PIP_Y(id_N830) + 2., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N830) + 3.}, + {HOP4X(5), PIP_Y(id_N830) + 3., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N830) + 4.}, + {HOP4X(3), PIP_Y(id_N830) + 4., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N838) + 5.}, + {HOP4X(1), PIP_Y(id_N838) + 5., WIRE_X(0), PIP_Y(id_N838) + 5.}}}, + {id_S83_loop2, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, + {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, + {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N830) - 2.}, + {HOP4X(11), PIP_Y(id_N830) - 2., HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 2.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N830) - 1.}, + {HOP4X(9), PIP_Y(id_N830) - 1., HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 1.}, + {HOP4X(9), PIP_Y(id_N834) - 1., WIRE_X(0), PIP_Y(id_N834) - 1.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N830) - 0.}, + {HOP4X(7), PIP_Y(id_N830) - 0., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N830) + 1.}, + {HOP4X(5), PIP_Y(id_N830) + 1., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N830) + 2.}, + {HOP4X(3), PIP_Y(id_N830) + 2., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N838) + 3.}, + {HOP4X(1), PIP_Y(id_N838) + 3., WIRE_X(0), PIP_Y(id_N838) + 3.}}}, + {id_S83_loop3, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, + {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, + {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N830) - 3.}, + {HOP4X(9), PIP_Y(id_N834) - 3., WIRE_X(0), PIP_Y(id_N834) - 3.}, + {HOP4X(9), PIP_Y(id_N830) - 3., HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 3.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N830) - 2.}, + {HOP4X(7), PIP_Y(id_N830) - 2., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 2.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N830) - 1.}, + {HOP4X(5), PIP_Y(id_N830) - 1., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 1.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N830) - 0.}, + {HOP4X(3), PIP_Y(id_N830) - 0., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 0.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N838) + 1.}, + {HOP4X(1), PIP_Y(id_N838) + 1., WIRE_X(0), PIP_Y(id_N838) + 1.}}}, + {id_S83_loop4, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, + {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, + {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N830) - 4.}, + {HOP4X(7), PIP_Y(id_N830) - 4., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 4.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N830) - 3.}, + {HOP4X(5), PIP_Y(id_N830) - 3., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 3.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N830) - 2.}, + {HOP4X(3), PIP_Y(id_N830) - 2., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 2.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N838) - 1.}, + {HOP4X(1), PIP_Y(id_N838) - 1., WIRE_X(0), PIP_Y(id_N838) - 1.}}}, + {id_S83_loop5, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, + {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, + {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N830) - 5.}, + {HOP4X(5), PIP_Y(id_N830) - 5., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 5.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N830) - 4.}, + {HOP4X(3), PIP_Y(id_N830) - 4., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 4.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N838) - 3.}, + {HOP4X(1), PIP_Y(id_N838) - 3., WIRE_X(0), PIP_Y(id_N838) - 3.}}}, + {id_S83_loop6, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, + {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, + {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, + {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N830) - 6.}, + {HOP4X(3), PIP_Y(id_N830) - 6., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 6.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N838) - 5.}, + {HOP4X(1), PIP_Y(id_N838) - 5., WIRE_X(0), PIP_Y(id_N838) - 5.}}}, + {id_S83_loop7, + {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, + {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, + {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, + {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, + {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, + {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, + {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, + {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, + {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, + {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, + {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N838) - 7.}, + {HOP4X(2), PIP_Y(id_N838) - 7., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, + {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, + {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N838) - 7.}, + {HOP4X(1), PIP_Y(id_N838) - 7., WIRE_X(0), PIP_Y(id_N838) - 7.}}}, + {id_N83_loop0, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, + {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, + {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N838) + 0.}, + {HOP4X(14), PIP_Y(id_N838) + 0., HOP4X(12), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(12), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N838) - 1.}, + {HOP4X(12), PIP_Y(id_N838) - 1., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N838) - 2.}, + {HOP4X(10), PIP_Y(id_N838) - 2., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N838) - 3.}, + {HOP4X(8), PIP_Y(id_S834) - 3., WIRE_X(0), PIP_Y(id_S834) - 3.}, + {HOP4X(8), PIP_Y(id_N838) - 3., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N838) - 4.}, + {HOP4X(6), PIP_Y(id_N838) - 4., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N838) - 5.}, + {HOP4X(4), PIP_Y(id_N838) - 5., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 5.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N838) - 6.}, + {HOP4X(2), PIP_Y(id_N838) - 6., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 6.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S838) - 7.}, + {HOP4X(0), PIP_Y(id_S838) - 7., WIRE_X(0), PIP_Y(id_S838) - 7.}}}, + {id_N83_loop1, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, + {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, + {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N838) + 1.}, + {HOP4X(12), PIP_Y(id_N838) + 1., HOP4X(10), PIP_Y(id_N838) - left_wire_dist + 1.}, + {HOP4X(10), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N838) + 0.}, + {HOP4X(10), PIP_Y(id_N838) + 0., HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N838) - 1.}, + {HOP4X(8), PIP_Y(id_S834) - 1., WIRE_X(0), PIP_Y(id_S834) - 1.}, + {HOP4X(8), PIP_Y(id_N838) - 1., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N838) - 2.}, + {HOP4X(6), PIP_Y(id_N838) - 2., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N838) - 3.}, + {HOP4X(4), PIP_Y(id_N838) - 3., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 3.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N838) - 4.}, + {HOP4X(2), PIP_Y(id_N838) - 4., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 4.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S838) - 5.}, + {HOP4X(0), PIP_Y(id_S838) - 5., WIRE_X(0), PIP_Y(id_S838) - 5.}}}, + {id_N83_loop2, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, + {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, + {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N838) + 2.}, + {HOP4X(10), PIP_Y(id_N838) + 2., HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 2.}, + {HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N838) + 1.}, + {HOP4X(8), PIP_Y(id_S834) + 1., WIRE_X(0), PIP_Y(id_S834) + 1.}, + {HOP4X(8), PIP_Y(id_N838) + 1., HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 1.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N838) + 0.}, + {HOP4X(6), PIP_Y(id_N838) + 0., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N838) - 1.}, + {HOP4X(4), PIP_Y(id_N838) - 1., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 1.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N838) - 2.}, + {HOP4X(2), PIP_Y(id_N838) - 2., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 2.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S838) - 3.}, + {HOP4X(0), PIP_Y(id_S838) - 3., WIRE_X(0), PIP_Y(id_S838) - 3.}}}, + {id_N83_loop3, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, + {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, + {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N838) + 3.}, + {HOP4X(8), PIP_Y(id_S834) + 3., WIRE_X(0), PIP_Y(id_S834) + 3.}, + {HOP4X(8), PIP_Y(id_N838) + 3., HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 3.}, + {HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N838) + 2.}, + {HOP4X(6), PIP_Y(id_N838) + 2., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 2.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N838) + 1.}, + {HOP4X(4), PIP_Y(id_N838) + 1., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 1.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N838) + 0.}, + {HOP4X(2), PIP_Y(id_N838) + 0., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 0.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S838) - 1.}, + {HOP4X(0), PIP_Y(id_S838) - 1., WIRE_X(0), PIP_Y(id_S838) - 1.}}}, + {id_N83_loop4, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, + {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, + {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N838) + 4.}, + {HOP4X(6), PIP_Y(id_N838) + 4., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 4.}, + {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N838) + 3.}, + {HOP4X(4), PIP_Y(id_N838) + 3., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 3.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N838) + 2.}, + {HOP4X(2), PIP_Y(id_N838) + 2., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 2.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S838) + 1.}, + {HOP4X(0), PIP_Y(id_S838) + 1., WIRE_X(0), PIP_Y(id_S838) + 1.}}}, + {id_N83_loop5, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, + {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, + {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N838) + 5.}, + {HOP4X(4), PIP_Y(id_N838) + 5., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 5.}, + {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N838) + 4.}, + {HOP4X(2), PIP_Y(id_N838) + 4., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 4.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S838) + 3.}, + {HOP4X(0), PIP_Y(id_S838) + 3., WIRE_X(0), PIP_Y(id_S838) + 3.}}}, + {id_N83_loop6, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, + {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, + {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, + {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N838) + 6.}, + {HOP4X(2), PIP_Y(id_N838) + 6., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 6.}, + {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S838) + 5.}, + {HOP4X(0), PIP_Y(id_S838) + 5., WIRE_X(0), PIP_Y(id_S838) + 5.}}}, + {id_N83_loop7, + {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, + {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, + {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, + {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, + {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, + {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, + {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, + {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, + {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, + {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, + {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, + {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, + {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, + {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, + {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, + {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N830) + 7.}, + {HOP4X(3), PIP_Y(id_N830) + 7., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7.}, + {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, + {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, + {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S838) + 7.}, + {HOP4X(0), PIP_Y(id_S838) + 7., WIRE_X(0), PIP_Y(id_S838) + 7.}}}, + +#define PIP_X(pip_id) (pipPoint.at(pip_id).second) +#define WIRE_Y(offset) (cru_y + cru_h + ((float)offset) * ew_dist) + // 1 hop + {id_E10, + {{PIP_X(id_E100), WIRE_Y(0), PIP_X(id_E100), WIRE_Y(1)}, + {PIP_X(id_E100), WIRE_Y(1), PIP_X(id_E101) + 1., WIRE_Y(1)}, + {PIP_X(id_E101) + 1., WIRE_Y(1), PIP_X(id_E101) + 1., WIRE_Y(0)}}}, + {id_W10, + {{PIP_X(id_W100), WIRE_Y(0), PIP_X(id_W100), WIRE_Y(2)}, + {PIP_X(id_W100), WIRE_Y(2), PIP_X(id_W101) - 1., WIRE_Y(2)}, + {PIP_X(id_W101) - 1., WIRE_Y(2), PIP_X(id_W101) - 1., WIRE_Y(0)}}}, + {id_E10_loop0, + {{PIP_X(id_E100), WIRE_Y(0), PIP_X(id_E100), WIRE_Y(1)}, + {PIP_X(id_E100), WIRE_Y(1), 1. + wrap_len, WIRE_Y(1)}, + {1. + wrap_len, WIRE_Y(1), 1. + wrap_len, WIRE_Y(2)}, + {1. + wrap_len, WIRE_Y(2), PIP_X(id_W101), WIRE_Y(2)}, + {PIP_X(id_W101), WIRE_Y(2), PIP_X(id_W101), WIRE_Y(0)}}}, + {id_W10_loop0, + {{PIP_X(id_W100), WIRE_Y(0), PIP_X(id_W100), WIRE_Y(2)}, + {PIP_X(id_W100), WIRE_Y(2), -1. * wrap_len, WIRE_Y(2)}, + {-1. * wrap_len, WIRE_Y(2), -1. * wrap_len, WIRE_Y(1)}, + {-1. * wrap_len, WIRE_Y(1), PIP_X(id_E101), WIRE_Y(1)}, + {PIP_X(id_E101), WIRE_Y(1), PIP_X(id_E101), WIRE_Y(0)}}}, + {id_E13, + {{PIP_X(id_E130), WIRE_Y(0), PIP_X(id_E130), WIRE_Y(3)}, + {PIP_X(id_E130), WIRE_Y(3), PIP_X(id_E131) + 1., WIRE_Y(3)}, + {PIP_X(id_E131) + 1., WIRE_Y(3), PIP_X(id_E131) + 1., WIRE_Y(0)}}}, + {id_W13, + {{PIP_X(id_W130), WIRE_Y(0), PIP_X(id_W130), WIRE_Y(4)}, + {PIP_X(id_W130), WIRE_Y(4), PIP_X(id_W131) - 1., WIRE_Y(4)}, + {PIP_X(id_W131) - 1., WIRE_Y(4), PIP_X(id_W131) - 1., WIRE_Y(0)}}}, + {id_E13_loop0, + {{PIP_X(id_E130), WIRE_Y(0), PIP_X(id_E130), WIRE_Y(3)}, + {PIP_X(id_E130), WIRE_Y(3), 1. + wrap_len, WIRE_Y(3)}, + {1. + wrap_len, WIRE_Y(3), 1. + wrap_len, WIRE_Y(4)}, + {1. + wrap_len, WIRE_Y(4), PIP_X(id_W131), WIRE_Y(4)}, + {PIP_X(id_W131), WIRE_Y(4), PIP_X(id_W131), WIRE_Y(0)}}}, + {id_W13_loop0, + {{PIP_X(id_W130), WIRE_Y(0), PIP_X(id_W130), WIRE_Y(4)}, + {PIP_X(id_W130), WIRE_Y(4), -1. * wrap_len, WIRE_Y(4)}, + {-1. * wrap_len, WIRE_Y(4), -1. * wrap_len, WIRE_Y(3)}, + {-1. * wrap_len, WIRE_Y(3), PIP_X(id_E131), WIRE_Y(3)}, + {PIP_X(id_E131), WIRE_Y(3), PIP_X(id_E131), WIRE_Y(0)}}}, + // 1 hop EW + {id_EW10, + {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, + {PIP_X(id_EW10), WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(6)}, + {PIP_X(id_E111) + 1., WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(0)}, + {PIP_X(id_EW10), WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(5)}, + {PIP_X(id_W111) - 1., WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(0)}}}, + {id_EW10_loop_e, + {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, + {PIP_X(id_EW10), WIRE_Y(6), wrap_len + 1., WIRE_Y(6)}, + {wrap_len + 1., WIRE_Y(6), wrap_len + 1., WIRE_Y(5)}, + {wrap_len + 1., WIRE_Y(5), PIP_X(id_W111), WIRE_Y(5)}, + {PIP_X(id_W111), WIRE_Y(5), PIP_X(id_W111), WIRE_Y(0)}, + {PIP_X(id_EW10), WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(5)}, + {PIP_X(id_W111) - 1., WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(0)}}}, + {id_EW10_loop_w, + {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, + {PIP_X(id_EW10), WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(6)}, + {PIP_X(id_E111) + 1., WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(0)}, + {PIP_X(id_EW10), WIRE_Y(5), -wrap_len, WIRE_Y(5)}, + {-wrap_len, WIRE_Y(5), -wrap_len, WIRE_Y(6)}, + {-wrap_len, WIRE_Y(6), PIP_X(id_E111), WIRE_Y(6)}, + {PIP_X(id_E111), WIRE_Y(6), PIP_X(id_E111), WIRE_Y(0)}}}, + {id_EW20, + {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, + {PIP_X(id_EW20), WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(8)}, + {PIP_X(id_E121) + 1., WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(0)}, + {PIP_X(id_EW20), WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(7)}, + {PIP_X(id_W121) - 1., WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(0)}}}, + {id_EW20_loop_e, + {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, + {PIP_X(id_EW20), WIRE_Y(8), wrap_len + 1., WIRE_Y(8)}, + {wrap_len + 1., WIRE_Y(8), wrap_len + 1., WIRE_Y(7)}, + {wrap_len + 1., WIRE_Y(7), PIP_X(id_W121), WIRE_Y(7)}, + {PIP_X(id_W121), WIRE_Y(7), PIP_X(id_W121), WIRE_Y(0)}, + {PIP_X(id_EW20), WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(7)}, + {PIP_X(id_W121) - 1., WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(0)}}}, + {id_EW20_loop_w, + {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, + {PIP_X(id_EW20), WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(8)}, + {PIP_X(id_E121) + 1., WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(0)}, + {PIP_X(id_EW20), WIRE_Y(7), -wrap_len, WIRE_Y(7)}, + {-wrap_len, WIRE_Y(7), -wrap_len, WIRE_Y(8)}, + {-wrap_len, WIRE_Y(8), PIP_X(id_E121), WIRE_Y(8)}, + {PIP_X(id_E121), WIRE_Y(8), PIP_X(id_E121), WIRE_Y(0)}}}, +// 2 hop +#define HOP2Y(offset) WIRE_Y(offset + 9) + {id_E20, + {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, + {PIP_X(id_E200), HOP2Y(2), PIP_X(id_E201) + 1., HOP2Y(2)}, + {PIP_X(id_E201) + 1., HOP2Y(2), PIP_X(id_E201) + 1., WIRE_Y(0)}, + {PIP_X(id_E201) + 1., HOP2Y(0), PIP_X(id_E202) + 2., HOP2Y(0)}, + {PIP_X(id_E202) + 2., HOP2Y(0), PIP_X(id_E202) + 2., WIRE_Y(0)}}}, + {id_W20, + {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, + {PIP_X(id_W200), HOP2Y(3), PIP_X(id_W201) - 1., HOP2Y(3)}, + {PIP_X(id_W201) - 1., HOP2Y(3), PIP_X(id_W201) - 1., WIRE_Y(0)}, + {PIP_X(id_W201) - 1., HOP2Y(1), PIP_X(id_W202) - 2., HOP2Y(1)}, + {PIP_X(id_W202) - 2., HOP2Y(1), PIP_X(id_W202) - 2., WIRE_Y(0)}}}, + {id_E20_loop0, + {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, + {PIP_X(id_E200), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W201), HOP2Y(3)}, + {PIP_X(id_W201), HOP2Y(3), PIP_X(id_W201), WIRE_Y(0)}, + {PIP_X(id_W201), HOP2Y(1), PIP_X(id_W202) - 1., HOP2Y(1)}, + {PIP_X(id_W202) - 1., HOP2Y(1), PIP_X(id_W202) - 1., WIRE_Y(0)}}}, + {id_W20_loop0, + {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, + {PIP_X(id_W200), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E201), HOP2Y(2)}, + {PIP_X(id_E201), HOP2Y(2), PIP_X(id_E201), WIRE_Y(0)}, + {PIP_X(id_E201), HOP2Y(0), PIP_X(id_E202) + 1., HOP2Y(0)}, + {PIP_X(id_E202) + 1., HOP2Y(0), PIP_X(id_E202) + 1., WIRE_Y(0)}}}, + {id_E20_loop1, + {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, + {PIP_X(id_E200), HOP2Y(2), PIP_X(id_E201) + 1., HOP2Y(2)}, + {PIP_X(id_E201) + 1., HOP2Y(2), PIP_X(id_E201) + 1., WIRE_Y(0)}, + {PIP_X(id_E201) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W202) + 1., HOP2Y(1)}, + {PIP_X(id_W202) + 1., HOP2Y(1), PIP_X(id_W202) + 1., WIRE_Y(0)}}}, + {id_W20_loop1, + {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, + {PIP_X(id_W200), HOP2Y(3), PIP_X(id_W201) - 1., HOP2Y(3)}, + {PIP_X(id_W201) - 1., HOP2Y(3), PIP_X(id_W201) - 1., WIRE_Y(0)}, + {PIP_X(id_W201) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E202) - 1., HOP2Y(0)}, + {PIP_X(id_E202) - 1., HOP2Y(0), PIP_X(id_E202) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 1) + {id_E21, + {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, + {PIP_X(id_E210), HOP2Y(2), PIP_X(id_E211) + 1., HOP2Y(2)}, + {PIP_X(id_E211) + 1., HOP2Y(2), PIP_X(id_E211) + 1., WIRE_Y(0)}, + {PIP_X(id_E211) + 1., HOP2Y(0), PIP_X(id_E212) + 2., HOP2Y(0)}, + {PIP_X(id_E212) + 2., HOP2Y(0), PIP_X(id_E212) + 2., WIRE_Y(0)}}}, + {id_W21, + {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, + {PIP_X(id_W210), HOP2Y(3), PIP_X(id_W211) - 1., HOP2Y(3)}, + {PIP_X(id_W211) - 1., HOP2Y(3), PIP_X(id_W211) - 1., WIRE_Y(0)}, + {PIP_X(id_W211) - 1., HOP2Y(1), PIP_X(id_W212) - 2., HOP2Y(1)}, + {PIP_X(id_W212) - 2., HOP2Y(1), PIP_X(id_W212) - 2., WIRE_Y(0)}}}, + {id_E21_loop0, + {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, + {PIP_X(id_E210), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W211), HOP2Y(3)}, + {PIP_X(id_W211), HOP2Y(3), PIP_X(id_W211), WIRE_Y(0)}, + {PIP_X(id_W211), HOP2Y(1), PIP_X(id_W212) - 1., HOP2Y(1)}, + {PIP_X(id_W212) - 1., HOP2Y(1), PIP_X(id_W212) - 1., WIRE_Y(0)}}}, + {id_W21_loop0, + {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, + {PIP_X(id_W210), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E211), HOP2Y(2)}, + {PIP_X(id_E211), HOP2Y(2), PIP_X(id_E211), WIRE_Y(0)}, + {PIP_X(id_E211), HOP2Y(0), PIP_X(id_E212) + 1., HOP2Y(0)}, + {PIP_X(id_E212) + 1., HOP2Y(0), PIP_X(id_E212) + 1., WIRE_Y(0)}}}, + {id_E21_loop1, + {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, + {PIP_X(id_E210), HOP2Y(2), PIP_X(id_E211) + 1., HOP2Y(2)}, + {PIP_X(id_E211) + 1., HOP2Y(2), PIP_X(id_E211) + 1., WIRE_Y(0)}, + {PIP_X(id_E211) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W212) + 1., HOP2Y(1)}, + {PIP_X(id_W212) + 1., HOP2Y(1), PIP_X(id_W212) + 1., WIRE_Y(0)}}}, + {id_W21_loop1, + {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, + {PIP_X(id_W210), HOP2Y(3), PIP_X(id_W211) - 1., HOP2Y(3)}, + {PIP_X(id_W211) - 1., HOP2Y(3), PIP_X(id_W211) - 1., WIRE_Y(0)}, + {PIP_X(id_W211) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E212) - 1., HOP2Y(0)}, + {PIP_X(id_E212) - 1., HOP2Y(0), PIP_X(id_E212) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 2) + {id_E22, + {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, + {PIP_X(id_E220), HOP2Y(2), PIP_X(id_E221) + 1., HOP2Y(2)}, + {PIP_X(id_E221) + 1., HOP2Y(2), PIP_X(id_E221) + 1., WIRE_Y(0)}, + {PIP_X(id_E221) + 1., HOP2Y(0), PIP_X(id_E222) + 2., HOP2Y(0)}, + {PIP_X(id_E222) + 2., HOP2Y(0), PIP_X(id_E222) + 2., WIRE_Y(0)}}}, + {id_W22, + {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, + {PIP_X(id_W220), HOP2Y(3), PIP_X(id_W221) - 1., HOP2Y(3)}, + {PIP_X(id_W221) - 1., HOP2Y(3), PIP_X(id_W221) - 1., WIRE_Y(0)}, + {PIP_X(id_W221) - 1., HOP2Y(1), PIP_X(id_W222) - 2., HOP2Y(1)}, + {PIP_X(id_W222) - 2., HOP2Y(1), PIP_X(id_W222) - 2., WIRE_Y(0)}}}, + {id_E22_loop0, + {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, + {PIP_X(id_E220), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W221), HOP2Y(3)}, + {PIP_X(id_W221), HOP2Y(3), PIP_X(id_W221), WIRE_Y(0)}, + {PIP_X(id_W221), HOP2Y(1), PIP_X(id_W222) - 1., HOP2Y(1)}, + {PIP_X(id_W222) - 1., HOP2Y(1), PIP_X(id_W222) - 1., WIRE_Y(0)}}}, + {id_W22_loop0, + {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, + {PIP_X(id_W220), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E221), HOP2Y(2)}, + {PIP_X(id_E221), HOP2Y(2), PIP_X(id_E221), WIRE_Y(0)}, + {PIP_X(id_E221), HOP2Y(0), PIP_X(id_E222) + 1., HOP2Y(0)}, + {PIP_X(id_E222) + 1., HOP2Y(0), PIP_X(id_E222) + 1., WIRE_Y(0)}}}, + {id_E22_loop1, + {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, + {PIP_X(id_E220), HOP2Y(2), PIP_X(id_E221) + 1., HOP2Y(2)}, + {PIP_X(id_E221) + 1., HOP2Y(2), PIP_X(id_E221) + 1., WIRE_Y(0)}, + {PIP_X(id_E221) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W222) + 1., HOP2Y(1)}, + {PIP_X(id_W222) + 1., HOP2Y(1), PIP_X(id_W222) + 1., WIRE_Y(0)}}}, + {id_W22_loop1, + {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, + {PIP_X(id_W220), HOP2Y(3), PIP_X(id_W221) - 1., HOP2Y(3)}, + {PIP_X(id_W221) - 1., HOP2Y(3), PIP_X(id_W221) - 1., WIRE_Y(0)}, + {PIP_X(id_W221) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E222) - 1., HOP2Y(0)}, + {PIP_X(id_E222) - 1., HOP2Y(0), PIP_X(id_E222) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 3) + {id_E23, + {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, + {PIP_X(id_E230), HOP2Y(2), PIP_X(id_E231) + 1., HOP2Y(2)}, + {PIP_X(id_E231) + 1., HOP2Y(2), PIP_X(id_E231) + 1., WIRE_Y(0)}, + {PIP_X(id_E231) + 1., HOP2Y(0), PIP_X(id_E232) + 2., HOP2Y(0)}, + {PIP_X(id_E232) + 2., HOP2Y(0), PIP_X(id_E232) + 2., WIRE_Y(0)}}}, + {id_W23, + {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, + {PIP_X(id_W230), HOP2Y(3), PIP_X(id_W231) - 1., HOP2Y(3)}, + {PIP_X(id_W231) - 1., HOP2Y(3), PIP_X(id_W231) - 1., WIRE_Y(0)}, + {PIP_X(id_W231) - 1., HOP2Y(1), PIP_X(id_W232) - 2., HOP2Y(1)}, + {PIP_X(id_W232) - 2., HOP2Y(1), PIP_X(id_W232) - 2., WIRE_Y(0)}}}, + {id_E23_loop0, + {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, + {PIP_X(id_E230), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W231), HOP2Y(3)}, + {PIP_X(id_W231), HOP2Y(3), PIP_X(id_W231), WIRE_Y(0)}, + {PIP_X(id_W231), HOP2Y(1), PIP_X(id_W232) - 1., HOP2Y(1)}, + {PIP_X(id_W232) - 1., HOP2Y(1), PIP_X(id_W232) - 1., WIRE_Y(0)}}}, + {id_W23_loop0, + {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, + {PIP_X(id_W230), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E231), HOP2Y(2)}, + {PIP_X(id_E231), HOP2Y(2), PIP_X(id_E231), WIRE_Y(0)}, + {PIP_X(id_E231), HOP2Y(0), PIP_X(id_E232) + 1., HOP2Y(0)}, + {PIP_X(id_E232) + 1., HOP2Y(0), PIP_X(id_E232) + 1., WIRE_Y(0)}}}, + {id_E23_loop1, + {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, + {PIP_X(id_E230), HOP2Y(2), PIP_X(id_E231) + 1., HOP2Y(2)}, + {PIP_X(id_E231) + 1., HOP2Y(2), PIP_X(id_E231) + 1., WIRE_Y(0)}, + {PIP_X(id_E231) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W232) + 1., HOP2Y(1)}, + {PIP_X(id_W232) + 1., HOP2Y(1), PIP_X(id_W232) + 1., WIRE_Y(0)}}}, + {id_W23_loop1, + {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, + {PIP_X(id_W230), HOP2Y(3), PIP_X(id_W231) - 1., HOP2Y(3)}, + {PIP_X(id_W231) - 1., HOP2Y(3), PIP_X(id_W231) - 1., WIRE_Y(0)}, + {PIP_X(id_W231) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E232) - 1., HOP2Y(0)}, + {PIP_X(id_E232) - 1., HOP2Y(0), PIP_X(id_E232) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 4) + {id_E24, + {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, + {PIP_X(id_E240), HOP2Y(2), PIP_X(id_E241) + 1., HOP2Y(2)}, + {PIP_X(id_E241) + 1., HOP2Y(2), PIP_X(id_E241) + 1., WIRE_Y(0)}, + {PIP_X(id_E241) + 1., HOP2Y(0), PIP_X(id_E242) + 2., HOP2Y(0)}, + {PIP_X(id_E242) + 2., HOP2Y(0), PIP_X(id_E242) + 2., WIRE_Y(0)}}}, + {id_W24, + {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, + {PIP_X(id_W240), HOP2Y(3), PIP_X(id_W241) - 1., HOP2Y(3)}, + {PIP_X(id_W241) - 1., HOP2Y(3), PIP_X(id_W241) - 1., WIRE_Y(0)}, + {PIP_X(id_W241) - 1., HOP2Y(1), PIP_X(id_W242) - 2., HOP2Y(1)}, + {PIP_X(id_W242) - 2., HOP2Y(1), PIP_X(id_W242) - 2., WIRE_Y(0)}}}, + {id_E24_loop0, + {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, + {PIP_X(id_E240), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W241), HOP2Y(3)}, + {PIP_X(id_W241), HOP2Y(3), PIP_X(id_W241), WIRE_Y(0)}, + {PIP_X(id_W241), HOP2Y(1), PIP_X(id_W242) - 1., HOP2Y(1)}, + {PIP_X(id_W242) - 1., HOP2Y(1), PIP_X(id_W242) - 1., WIRE_Y(0)}}}, + {id_W24_loop0, + {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, + {PIP_X(id_W240), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E241), HOP2Y(2)}, + {PIP_X(id_E241), HOP2Y(2), PIP_X(id_E241), WIRE_Y(0)}, + {PIP_X(id_E241), HOP2Y(0), PIP_X(id_E242) + 1., HOP2Y(0)}, + {PIP_X(id_E242) + 1., HOP2Y(0), PIP_X(id_E242) + 1., WIRE_Y(0)}}}, + {id_E24_loop1, + {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, + {PIP_X(id_E240), HOP2Y(2), PIP_X(id_E241) + 1., HOP2Y(2)}, + {PIP_X(id_E241) + 1., HOP2Y(2), PIP_X(id_E241) + 1., WIRE_Y(0)}, + {PIP_X(id_E241) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W242) + 1., HOP2Y(1)}, + {PIP_X(id_W242) + 1., HOP2Y(1), PIP_X(id_W242) + 1., WIRE_Y(0)}}}, + {id_W24_loop1, + {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, + {PIP_X(id_W240), HOP2Y(3), PIP_X(id_W241) - 1., HOP2Y(3)}, + {PIP_X(id_W241) - 1., HOP2Y(3), PIP_X(id_W241) - 1., WIRE_Y(0)}, + {PIP_X(id_W241) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E242) - 1., HOP2Y(0)}, + {PIP_X(id_E242) - 1., HOP2Y(0), PIP_X(id_E242) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 5) + {id_E25, + {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, + {PIP_X(id_E250), HOP2Y(2), PIP_X(id_E251) + 1., HOP2Y(2)}, + {PIP_X(id_E251) + 1., HOP2Y(2), PIP_X(id_E251) + 1., WIRE_Y(0)}, + {PIP_X(id_E251) + 1., HOP2Y(0), PIP_X(id_E252) + 2., HOP2Y(0)}, + {PIP_X(id_E252) + 2., HOP2Y(0), PIP_X(id_E252) + 2., WIRE_Y(0)}}}, + {id_W25, + {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, + {PIP_X(id_W250), HOP2Y(3), PIP_X(id_W251) - 1., HOP2Y(3)}, + {PIP_X(id_W251) - 1., HOP2Y(3), PIP_X(id_W251) - 1., WIRE_Y(0)}, + {PIP_X(id_W251) - 1., HOP2Y(1), PIP_X(id_W252) - 2., HOP2Y(1)}, + {PIP_X(id_W252) - 2., HOP2Y(1), PIP_X(id_W252) - 2., WIRE_Y(0)}}}, + {id_E25_loop0, + {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, + {PIP_X(id_E250), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W251), HOP2Y(3)}, + {PIP_X(id_W251), HOP2Y(3), PIP_X(id_W251), WIRE_Y(0)}, + {PIP_X(id_W251), HOP2Y(1), PIP_X(id_W252) - 1., HOP2Y(1)}, + {PIP_X(id_W252) - 1., HOP2Y(1), PIP_X(id_W252) - 1., WIRE_Y(0)}}}, + {id_W25_loop0, + {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, + {PIP_X(id_W250), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E251), HOP2Y(2)}, + {PIP_X(id_E251), HOP2Y(2), PIP_X(id_E251), WIRE_Y(0)}, + {PIP_X(id_E251), HOP2Y(0), PIP_X(id_E252) + 1., HOP2Y(0)}, + {PIP_X(id_E252) + 1., HOP2Y(0), PIP_X(id_E252) + 1., WIRE_Y(0)}}}, + {id_E25_loop1, + {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, + {PIP_X(id_E250), HOP2Y(2), PIP_X(id_E251) + 1., HOP2Y(2)}, + {PIP_X(id_E251) + 1., HOP2Y(2), PIP_X(id_E251) + 1., WIRE_Y(0)}, + {PIP_X(id_E251) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W252) + 1., HOP2Y(1)}, + {PIP_X(id_W252) + 1., HOP2Y(1), PIP_X(id_W252) + 1., WIRE_Y(0)}}}, + {id_W25_loop1, + {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, + {PIP_X(id_W250), HOP2Y(3), PIP_X(id_W251) - 1., HOP2Y(3)}, + {PIP_X(id_W251) - 1., HOP2Y(3), PIP_X(id_W251) - 1., WIRE_Y(0)}, + {PIP_X(id_W251) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E252) - 1., HOP2Y(0)}, + {PIP_X(id_E252) - 1., HOP2Y(0), PIP_X(id_E252) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 6) + {id_E26, + {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, + {PIP_X(id_E260), HOP2Y(2), PIP_X(id_E261) + 1., HOP2Y(2)}, + {PIP_X(id_E261) + 1., HOP2Y(2), PIP_X(id_E261) + 1., WIRE_Y(0)}, + {PIP_X(id_E261) + 1., HOP2Y(0), PIP_X(id_E262) + 2., HOP2Y(0)}, + {PIP_X(id_E262) + 2., HOP2Y(0), PIP_X(id_E262) + 2., WIRE_Y(0)}}}, + {id_W26, + {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, + {PIP_X(id_W260), HOP2Y(3), PIP_X(id_W261) - 1., HOP2Y(3)}, + {PIP_X(id_W261) - 1., HOP2Y(3), PIP_X(id_W261) - 1., WIRE_Y(0)}, + {PIP_X(id_W261) - 1., HOP2Y(1), PIP_X(id_W262) - 2., HOP2Y(1)}, + {PIP_X(id_W262) - 2., HOP2Y(1), PIP_X(id_W262) - 2., WIRE_Y(0)}}}, + {id_E26_loop0, + {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, + {PIP_X(id_E260), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W261), HOP2Y(3)}, + {PIP_X(id_W261), HOP2Y(3), PIP_X(id_W261), WIRE_Y(0)}, + {PIP_X(id_W261), HOP2Y(1), PIP_X(id_W262) - 1., HOP2Y(1)}, + {PIP_X(id_W262) - 1., HOP2Y(1), PIP_X(id_W262) - 1., WIRE_Y(0)}}}, + {id_W26_loop0, + {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, + {PIP_X(id_W260), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E261), HOP2Y(2)}, + {PIP_X(id_E261), HOP2Y(2), PIP_X(id_E261), WIRE_Y(0)}, + {PIP_X(id_E261), HOP2Y(0), PIP_X(id_E262) + 1., HOP2Y(0)}, + {PIP_X(id_E262) + 1., HOP2Y(0), PIP_X(id_E262) + 1., WIRE_Y(0)}}}, + {id_E26_loop1, + {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, + {PIP_X(id_E260), HOP2Y(2), PIP_X(id_E261) + 1., HOP2Y(2)}, + {PIP_X(id_E261) + 1., HOP2Y(2), PIP_X(id_E261) + 1., WIRE_Y(0)}, + {PIP_X(id_E261) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W262) + 1., HOP2Y(1)}, + {PIP_X(id_W262) + 1., HOP2Y(1), PIP_X(id_W262) + 1., WIRE_Y(0)}}}, + {id_W26_loop1, + {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, + {PIP_X(id_W260), HOP2Y(3), PIP_X(id_W261) - 1., HOP2Y(3)}, + {PIP_X(id_W261) - 1., HOP2Y(3), PIP_X(id_W261) - 1., WIRE_Y(0)}, + {PIP_X(id_W261) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E262) - 1., HOP2Y(0)}, + {PIP_X(id_E262) - 1., HOP2Y(0), PIP_X(id_E262) - 1., WIRE_Y(0)}}}, + +#undef HOP2Y +#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 7) + {id_E27, + {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, + {PIP_X(id_E270), HOP2Y(2), PIP_X(id_E271) + 1., HOP2Y(2)}, + {PIP_X(id_E271) + 1., HOP2Y(2), PIP_X(id_E271) + 1., WIRE_Y(0)}, + {PIP_X(id_E271) + 1., HOP2Y(0), PIP_X(id_E272) + 2., HOP2Y(0)}, + {PIP_X(id_E272) + 2., HOP2Y(0), PIP_X(id_E272) + 2., WIRE_Y(0)}}}, + {id_W27, + {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, + {PIP_X(id_W270), HOP2Y(3), PIP_X(id_W271) - 1., HOP2Y(3)}, + {PIP_X(id_W271) - 1., HOP2Y(3), PIP_X(id_W271) - 1., WIRE_Y(0)}, + {PIP_X(id_W271) - 1., HOP2Y(1), PIP_X(id_W272) - 2., HOP2Y(1)}, + {PIP_X(id_W272) - 2., HOP2Y(1), PIP_X(id_W272) - 2., WIRE_Y(0)}}}, + {id_E27_loop0, + {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, + {PIP_X(id_E270), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, + {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, + {wrap_len + 1., HOP2Y(3), PIP_X(id_W271), HOP2Y(3)}, + {PIP_X(id_W271), HOP2Y(3), PIP_X(id_W271), WIRE_Y(0)}, + {PIP_X(id_W271), HOP2Y(1), PIP_X(id_W272) - 1., HOP2Y(1)}, + {PIP_X(id_W272) - 1., HOP2Y(1), PIP_X(id_W272) - 1., WIRE_Y(0)}}}, + {id_W27_loop0, + {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, + {PIP_X(id_W270), HOP2Y(3), -wrap_len, HOP2Y(3)}, + {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, + {-wrap_len, HOP2Y(2), PIP_X(id_E271), HOP2Y(2)}, + {PIP_X(id_E271), HOP2Y(2), PIP_X(id_E271), WIRE_Y(0)}, + {PIP_X(id_E271), HOP2Y(0), PIP_X(id_E272) + 1., HOP2Y(0)}, + {PIP_X(id_E272) + 1., HOP2Y(0), PIP_X(id_E272) + 1., WIRE_Y(0)}}}, + {id_E27_loop1, + {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, + {PIP_X(id_E270), HOP2Y(2), PIP_X(id_E271) + 1., HOP2Y(2)}, + {PIP_X(id_E271) + 1., HOP2Y(2), PIP_X(id_E271) + 1., WIRE_Y(0)}, + {PIP_X(id_E271) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, + {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, + {wrap_len + 2., HOP2Y(1), PIP_X(id_W272) + 1., HOP2Y(1)}, + {PIP_X(id_W272) + 1., HOP2Y(1), PIP_X(id_W272) + 1., WIRE_Y(0)}}}, + {id_W27_loop1, + {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, + {PIP_X(id_W270), HOP2Y(3), PIP_X(id_W271) - 1., HOP2Y(3)}, + {PIP_X(id_W271) - 1., HOP2Y(3), PIP_X(id_W271) - 1., WIRE_Y(0)}, + {PIP_X(id_W271) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, + {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, + {-wrap_len - 1., HOP2Y(0), PIP_X(id_E272) - 1., HOP2Y(0)}, + {PIP_X(id_E272) - 1., HOP2Y(0), PIP_X(id_E272) - 1., WIRE_Y(0)}}}, + +// clock branches +#define CLK_GBO0_Y 41.f +#define CLK_GBO1_Y 46.f +// 4 hop +#define HOP4Y_START (CLK_GBO0_Y + 10.f) +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START) + {id_E80, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, + {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W808) + 7., HOP4Y(2)}, + {PIP_X(id_W808) + 7., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E808) + 8., HOP4Y(0)}, + {PIP_X(id_E808) + 8, HOP4Y(0), PIP_X(id_E808) + 8., WIRE_Y(0)}}}, + {id_W80, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, + {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W800) - 7., HOP4Y(3)}, + {PIP_X(id_W800) - 7., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W808) - 8., HOP4Y(1)}, + {PIP_X(id_W808) - 8, HOP4Y(1), PIP_X(id_W808) - 8., WIRE_Y(0)}}}, + {id_E80_loop0, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W800) - 0., HOP4Y(15)}, + {PIP_X(id_W800) - 0., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W800) - 1., HOP4Y(13)}, + {PIP_X(id_W800) - 1., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W800) - 2., HOP4Y(11)}, + {PIP_X(id_W800) - 2., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W800) - 3., HOP4Y(9)}, + {PIP_X(id_W800) - 3., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W804) - 3., HOP4Y(9), PIP_X(id_W804) - 3., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W800) - 4., HOP4Y(7)}, + {PIP_X(id_W800) - 4., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W800) - 5., HOP4Y(5)}, + {PIP_X(id_W800) - 5., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W800) - 6., HOP4Y(3)}, + {PIP_X(id_W800) - 6., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W808) - 7., HOP4Y(1)}, + {PIP_X(id_W808) - 7, HOP4Y(1), PIP_X(id_W808) - 7., WIRE_Y(0)}}}, + {id_E80_loop1, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W800) + 1., HOP4Y(13)}, + {PIP_X(id_W800) + 1., HOP4Y(13), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W800) - 0., HOP4Y(11)}, + {PIP_X(id_W800) - 0., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W800) - 1., HOP4Y(9)}, + {PIP_X(id_W804) - 1., HOP4Y(9), PIP_X(id_W804) - 1., WIRE_Y(0)}, + {PIP_X(id_W800) - 1., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W800) - 2., HOP4Y(7)}, + {PIP_X(id_W800) - 2., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W800) - 3., HOP4Y(5)}, + {PIP_X(id_W800) - 3., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W800) - 4., HOP4Y(3)}, + {PIP_X(id_W800) - 4., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W808) - 5., HOP4Y(1)}, + {PIP_X(id_W808) - 5., HOP4Y(1), PIP_X(id_W808) - 5., WIRE_Y(0)}}}, + {id_E80_loop2, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W800) + 2., HOP4Y(11)}, + {PIP_X(id_W800) + 2., HOP4Y(11), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W800) + 1., HOP4Y(9)}, + {PIP_X(id_W804) + 1., HOP4Y(9), PIP_X(id_W804) + 1., WIRE_Y(0)}, + {PIP_X(id_W800) + 1., HOP4Y(9), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W800) + 0., HOP4Y(7)}, + {PIP_X(id_W800) + 0., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W800) - 1., HOP4Y(5)}, + {PIP_X(id_W800) - 1., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W800) - 2., HOP4Y(3)}, + {PIP_X(id_W800) - 2., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W808) - 3., HOP4Y(1)}, + {PIP_X(id_W808) - 3., HOP4Y(1), PIP_X(id_W808) - 3., WIRE_Y(0)}}}, + {id_E80_loop3, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W800) + 3., HOP4Y(9)}, + {PIP_X(id_W804) + 3., HOP4Y(9), PIP_X(id_W804) + 3., WIRE_Y(0)}, + {PIP_X(id_W800) + 3., HOP4Y(9), PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W800) + 2., HOP4Y(7)}, + {PIP_X(id_W800) + 2., HOP4Y(7), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W800) + 1., HOP4Y(5)}, + {PIP_X(id_W800) + 1., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W800) - 0., HOP4Y(3)}, + {PIP_X(id_W800) - 0., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W808) - 1., HOP4Y(1)}, + {PIP_X(id_W808) - 1., HOP4Y(1), PIP_X(id_W808) - 1., WIRE_Y(0)}}}, + {id_E80_loop4, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W800) + 4., HOP4Y(7)}, + {PIP_X(id_W800) + 4., HOP4Y(7), PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W800) + 3., HOP4Y(5)}, + {PIP_X(id_W800) + 3., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W800) + 2., HOP4Y(3)}, + {PIP_X(id_W800) + 2., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W808) + 1., HOP4Y(1)}, + {PIP_X(id_W808) + 1., HOP4Y(1), PIP_X(id_W808) + 1., WIRE_Y(0)}}}, + {id_E80_loop5, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W800) + 5., HOP4Y(5)}, + {PIP_X(id_W800) + 5., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W800) + 4., HOP4Y(3)}, + {PIP_X(id_W800) + 4., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W808) + 3., HOP4Y(1)}, + {PIP_X(id_W808) + 3., HOP4Y(1), PIP_X(id_W808) + 3., WIRE_Y(0)}}}, + {id_E80_loop6, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, + {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W800) + 6., HOP4Y(3)}, + {PIP_X(id_W800) + 6., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W808) + 5., HOP4Y(1)}, + {PIP_X(id_W808) + 5., HOP4Y(1), PIP_X(id_W808) + 5., WIRE_Y(0)}}}, + {id_E80_loop7, + {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, + {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, + {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, + {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, + {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, + {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, + {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, + {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, + {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W808) + 7., HOP4Y(2)}, + {PIP_X(id_W808) + 7., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W808) + 7., HOP4Y(1)}, + {PIP_X(id_W808) + 7., HOP4Y(1), PIP_X(id_W808) + 7., WIRE_Y(0)}}}, + {id_W80_loop0, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W808) + 0., HOP4Y(14)}, + {PIP_X(id_W808) + 0., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W808) + 1., HOP4Y(12)}, + {PIP_X(id_W808) + 1., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W808) + 2., HOP4Y(10)}, + {PIP_X(id_W808) + 2., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W808) + 3., HOP4Y(8)}, + {PIP_X(id_W808) + 3., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E804) + 3., HOP4Y(8), PIP_X(id_E804) + 3., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W808) + 4., HOP4Y(6)}, + {PIP_X(id_W808) + 4., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W808) + 5., HOP4Y(4)}, + {PIP_X(id_W808) + 5., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W808) + 6., HOP4Y(2)}, + {PIP_X(id_W808) + 6., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E808) + 7., HOP4Y(0)}, + {PIP_X(id_E808) + 7., HOP4Y(0), PIP_X(id_E808) + 7., WIRE_Y(0)}}}, + {id_W80_loop1, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W808) - 1., HOP4Y(12)}, + {PIP_X(id_W808) - 1., HOP4Y(12), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W808) + 0., HOP4Y(10)}, + {PIP_X(id_W808) + 0., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W808) + 1., HOP4Y(8)}, + {PIP_X(id_W808) + 1., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E804) + 1., HOP4Y(8), PIP_X(id_E804) + 1., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W808) + 2., HOP4Y(6)}, + {PIP_X(id_W808) + 2., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W808) + 3., HOP4Y(4)}, + {PIP_X(id_W808) + 3., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W808) + 4., HOP4Y(2)}, + {PIP_X(id_W808) + 4., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E808) + 5., HOP4Y(0)}, + {PIP_X(id_E808) + 5., HOP4Y(0), PIP_X(id_E808) + 5., WIRE_Y(0)}}}, + {id_W80_loop2, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W808) - 2., HOP4Y(10)}, + {PIP_X(id_W808) - 2., HOP4Y(10), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W808) - 1., HOP4Y(8)}, + {PIP_X(id_W808) - 1., HOP4Y(8), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E804) - 1., HOP4Y(8), PIP_X(id_E804) - 1., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W808) + 0., HOP4Y(6)}, + {PIP_X(id_W808) + 0., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W808) + 1., HOP4Y(4)}, + {PIP_X(id_W808) + 1., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W808) + 2., HOP4Y(2)}, + {PIP_X(id_W808) + 2., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E808) + 3., HOP4Y(0)}, + {PIP_X(id_E808) + 3., HOP4Y(0), PIP_X(id_E808) + 3., WIRE_Y(0)}}}, + {id_W80_loop3, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W808) - 3., HOP4Y(8)}, + {PIP_X(id_W808) - 3., HOP4Y(8), PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E804) - 3., HOP4Y(8), PIP_X(id_E804) - 3., WIRE_Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W808) - 2., HOP4Y(6)}, + {PIP_X(id_W808) - 2., HOP4Y(6), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W808) - 1., HOP4Y(4)}, + {PIP_X(id_W808) - 1., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W808) + 0., HOP4Y(2)}, + {PIP_X(id_W808) + 0., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E808) + 1., HOP4Y(0)}, + {PIP_X(id_E808) + 1., HOP4Y(0), PIP_X(id_E808) + 1., WIRE_Y(0)}}}, + {id_W80_loop4, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W804) - 4., HOP4Y(6), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W808) - 4., HOP4Y(6)}, + {PIP_X(id_W808) - 4., HOP4Y(6), PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W808) - 3., HOP4Y(4)}, + {PIP_X(id_W808) - 3., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W808) - 2., HOP4Y(2)}, + {PIP_X(id_W808) - 2., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E808) - 1., HOP4Y(0)}, + {PIP_X(id_E808) - 1., HOP4Y(0), PIP_X(id_E808) - 1., WIRE_Y(0)}}}, + {id_W80_loop5, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W808) - 5., HOP4Y(4)}, + {PIP_X(id_W808) - 5., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W808) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W808) - 4., HOP4Y(2)}, + {PIP_X(id_W808) - 4., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E808) - 3., HOP4Y(0)}, + {PIP_X(id_E808) - 3., HOP4Y(0), PIP_X(id_E808) - 3., WIRE_Y(0)}}}, + {id_W80_loop6, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, + {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W808) - 6., HOP4Y(2)}, + {PIP_X(id_W808) - 6., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W808) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E808) - 5., HOP4Y(0)}, + {PIP_X(id_E808) - 5., HOP4Y(0), PIP_X(id_E808) - 5., WIRE_Y(0)}}}, + {id_W80_loop7, + {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, + {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, + {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, + {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, + {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, + {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, + {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, + {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, + {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W800) - 7., HOP4Y(3)}, + {PIP_X(id_W800) - 7., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E808) - 7., HOP4Y(0)}, + {PIP_X(id_E808) - 7., HOP4Y(0), PIP_X(id_E808) - 7., WIRE_Y(0)}}}, + +#undef HOP4Y +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f) + {id_E81, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, + {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W818) + 7., HOP4Y(2)}, + {PIP_X(id_W818) + 7., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E818) + 8., HOP4Y(0)}, + {PIP_X(id_E818) + 8, HOP4Y(0), PIP_X(id_E818) + 8., WIRE_Y(0)}}}, + {id_W81, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, + {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W810) - 7., HOP4Y(3)}, + {PIP_X(id_W810) - 7., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W818) - 8., HOP4Y(1)}, + {PIP_X(id_W818) - 8, HOP4Y(1), PIP_X(id_W818) - 8., WIRE_Y(0)}}}, + {id_E81_loop0, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W810) - 0., HOP4Y(15)}, + {PIP_X(id_W810) - 0., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W810) - 1., HOP4Y(13)}, + {PIP_X(id_W810) - 1., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W810) - 2., HOP4Y(11)}, + {PIP_X(id_W810) - 2., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W810) - 3., HOP4Y(9)}, + {PIP_X(id_W810) - 3., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W814) - 3., HOP4Y(9), PIP_X(id_W814) - 3., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W810) - 4., HOP4Y(7)}, + {PIP_X(id_W810) - 4., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W810) - 5., HOP4Y(5)}, + {PIP_X(id_W810) - 5., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W810) - 6., HOP4Y(3)}, + {PIP_X(id_W810) - 6., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W818) - 7., HOP4Y(1)}, + {PIP_X(id_W818) - 7, HOP4Y(1), PIP_X(id_W818) - 7., WIRE_Y(0)}}}, + {id_E81_loop1, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W810) + 1., HOP4Y(13)}, + {PIP_X(id_W810) + 1., HOP4Y(13), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W810) - 0., HOP4Y(11)}, + {PIP_X(id_W810) - 0., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W810) - 1., HOP4Y(9)}, + {PIP_X(id_W814) - 1., HOP4Y(9), PIP_X(id_W814) - 1., WIRE_Y(0)}, + {PIP_X(id_W810) - 1., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W810) - 2., HOP4Y(7)}, + {PIP_X(id_W810) - 2., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W810) - 3., HOP4Y(5)}, + {PIP_X(id_W810) - 3., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W810) - 4., HOP4Y(3)}, + {PIP_X(id_W810) - 4., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W818) - 5., HOP4Y(1)}, + {PIP_X(id_W818) - 5., HOP4Y(1), PIP_X(id_W818) - 5., WIRE_Y(0)}}}, + {id_E81_loop2, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W810) + 2., HOP4Y(11)}, + {PIP_X(id_W810) + 2., HOP4Y(11), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W810) + 1., HOP4Y(9)}, + {PIP_X(id_W814) + 1., HOP4Y(9), PIP_X(id_W814) + 1., WIRE_Y(0)}, + {PIP_X(id_W810) + 1., HOP4Y(9), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W810) + 0., HOP4Y(7)}, + {PIP_X(id_W810) + 0., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W810) - 1., HOP4Y(5)}, + {PIP_X(id_W810) - 1., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W810) - 2., HOP4Y(3)}, + {PIP_X(id_W810) - 2., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W818) - 3., HOP4Y(1)}, + {PIP_X(id_W818) - 3., HOP4Y(1), PIP_X(id_W818) - 3., WIRE_Y(0)}}}, + {id_E81_loop3, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W810) + 3., HOP4Y(9)}, + {PIP_X(id_W814) + 3., HOP4Y(9), PIP_X(id_W814) + 3., WIRE_Y(0)}, + {PIP_X(id_W810) + 3., HOP4Y(9), PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W810) + 2., HOP4Y(7)}, + {PIP_X(id_W810) + 2., HOP4Y(7), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W810) + 1., HOP4Y(5)}, + {PIP_X(id_W810) + 1., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W810) - 0., HOP4Y(3)}, + {PIP_X(id_W810) - 0., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W818) - 1., HOP4Y(1)}, + {PIP_X(id_W818) - 1., HOP4Y(1), PIP_X(id_W818) - 1., WIRE_Y(0)}}}, + {id_E81_loop4, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W810) + 4., HOP4Y(7)}, + {PIP_X(id_W810) + 4., HOP4Y(7), PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W810) + 3., HOP4Y(5)}, + {PIP_X(id_W810) + 3., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W810) + 2., HOP4Y(3)}, + {PIP_X(id_W810) + 2., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W818) + 1., HOP4Y(1)}, + {PIP_X(id_W818) + 1., HOP4Y(1), PIP_X(id_W818) + 1., WIRE_Y(0)}}}, + {id_E81_loop5, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W810) + 5., HOP4Y(5)}, + {PIP_X(id_W810) + 5., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W810) + 4., HOP4Y(3)}, + {PIP_X(id_W810) + 4., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W818) + 3., HOP4Y(1)}, + {PIP_X(id_W818) + 3., HOP4Y(1), PIP_X(id_W818) + 3., WIRE_Y(0)}}}, + {id_E81_loop6, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, + {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W810) + 6., HOP4Y(3)}, + {PIP_X(id_W810) + 6., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W818) + 5., HOP4Y(1)}, + {PIP_X(id_W818) + 5., HOP4Y(1), PIP_X(id_W818) + 5., WIRE_Y(0)}}}, + {id_E81_loop7, + {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, + {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, + {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, + {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, + {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, + {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, + {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, + {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, + {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W818) + 7., HOP4Y(2)}, + {PIP_X(id_W818) + 7., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W818) + 7., HOP4Y(1)}, + {PIP_X(id_W818) + 7., HOP4Y(1), PIP_X(id_W818) + 7., WIRE_Y(0)}}}, + {id_W81_loop0, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W818) + 0., HOP4Y(14)}, + {PIP_X(id_W818) + 0., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W818) + 1., HOP4Y(12)}, + {PIP_X(id_W818) + 1., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W818) + 2., HOP4Y(10)}, + {PIP_X(id_W818) + 2., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W818) + 3., HOP4Y(8)}, + {PIP_X(id_W818) + 3., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E814) + 3., HOP4Y(8), PIP_X(id_E814) + 3., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W818) + 4., HOP4Y(6)}, + {PIP_X(id_W818) + 4., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W818) + 5., HOP4Y(4)}, + {PIP_X(id_W818) + 5., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W818) + 6., HOP4Y(2)}, + {PIP_X(id_W818) + 6., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E818) + 7., HOP4Y(0)}, + {PIP_X(id_E818) + 7., HOP4Y(0), PIP_X(id_E818) + 7., WIRE_Y(0)}}}, + {id_W81_loop1, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W818) - 1., HOP4Y(12)}, + {PIP_X(id_W818) - 1., HOP4Y(12), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W818) + 0., HOP4Y(10)}, + {PIP_X(id_W818) + 0., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W818) + 1., HOP4Y(8)}, + {PIP_X(id_W818) + 1., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E814) + 1., HOP4Y(8), PIP_X(id_E814) + 1., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W818) + 2., HOP4Y(6)}, + {PIP_X(id_W818) + 2., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W818) + 3., HOP4Y(4)}, + {PIP_X(id_W818) + 3., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W818) + 4., HOP4Y(2)}, + {PIP_X(id_W818) + 4., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E818) + 5., HOP4Y(0)}, + {PIP_X(id_E818) + 5., HOP4Y(0), PIP_X(id_E818) + 5., WIRE_Y(0)}}}, + {id_W81_loop2, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W818) - 2., HOP4Y(10)}, + {PIP_X(id_W818) - 2., HOP4Y(10), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W818) - 1., HOP4Y(8)}, + {PIP_X(id_W818) - 1., HOP4Y(8), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E814) - 1., HOP4Y(8), PIP_X(id_E814) - 1., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W818) + 0., HOP4Y(6)}, + {PIP_X(id_W818) + 0., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W818) + 1., HOP4Y(4)}, + {PIP_X(id_W818) + 1., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W818) + 2., HOP4Y(2)}, + {PIP_X(id_W818) + 2., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E818) + 3., HOP4Y(0)}, + {PIP_X(id_E818) + 3., HOP4Y(0), PIP_X(id_E818) + 3., WIRE_Y(0)}}}, + {id_W81_loop3, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W818) - 3., HOP4Y(8)}, + {PIP_X(id_W818) - 3., HOP4Y(8), PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E814) - 3., HOP4Y(8), PIP_X(id_E814) - 3., WIRE_Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W818) - 2., HOP4Y(6)}, + {PIP_X(id_W818) - 2., HOP4Y(6), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W818) - 1., HOP4Y(4)}, + {PIP_X(id_W818) - 1., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W818) + 0., HOP4Y(2)}, + {PIP_X(id_W818) + 0., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E818) + 1., HOP4Y(0)}, + {PIP_X(id_E818) + 1., HOP4Y(0), PIP_X(id_E818) + 1., WIRE_Y(0)}}}, + {id_W81_loop4, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W814) - 4., HOP4Y(6), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W818) - 4., HOP4Y(6)}, + {PIP_X(id_W818) - 4., HOP4Y(6), PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W818) - 3., HOP4Y(4)}, + {PIP_X(id_W818) - 3., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W818) - 2., HOP4Y(2)}, + {PIP_X(id_W818) - 2., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E818) - 1., HOP4Y(0)}, + {PIP_X(id_E818) - 1., HOP4Y(0), PIP_X(id_E818) - 1., WIRE_Y(0)}}}, + {id_W81_loop5, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W818) - 5., HOP4Y(4)}, + {PIP_X(id_W818) - 5., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W818) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W818) - 4., HOP4Y(2)}, + {PIP_X(id_W818) - 4., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E818) - 3., HOP4Y(0)}, + {PIP_X(id_E818) - 3., HOP4Y(0), PIP_X(id_E818) - 3., WIRE_Y(0)}}}, + {id_W81_loop6, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, + {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W818) - 6., HOP4Y(2)}, + {PIP_X(id_W818) - 6., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W818) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E818) - 5., HOP4Y(0)}, + {PIP_X(id_E818) - 5., HOP4Y(0), PIP_X(id_E818) - 5., WIRE_Y(0)}}}, + {id_W81_loop7, + {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, + {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, + {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, + {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, + {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, + {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, + {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, + {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, + {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W810) - 7., HOP4Y(3)}, + {PIP_X(id_W810) - 7., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E818) - 7., HOP4Y(0)}, + {PIP_X(id_E818) - 7., HOP4Y(0), PIP_X(id_E818) - 7., WIRE_Y(0)}}}, + +#undef HOP4Y +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f + 18.f) + {id_E82, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, + {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W828) + 7., HOP4Y(2)}, + {PIP_X(id_W828) + 7., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E828) + 8., HOP4Y(0)}, + {PIP_X(id_E828) + 8, HOP4Y(0), PIP_X(id_E828) + 8., WIRE_Y(0)}}}, + {id_W82, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, + {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W820) - 7., HOP4Y(3)}, + {PIP_X(id_W820) - 7., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W828) - 8., HOP4Y(1)}, + {PIP_X(id_W828) - 8, HOP4Y(1), PIP_X(id_W828) - 8., WIRE_Y(0)}}}, + {id_E82_loop0, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W820) - 0., HOP4Y(15)}, + {PIP_X(id_W820) - 0., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W820) - 1., HOP4Y(13)}, + {PIP_X(id_W820) - 1., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W820) - 2., HOP4Y(11)}, + {PIP_X(id_W820) - 2., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W820) - 3., HOP4Y(9)}, + {PIP_X(id_W820) - 3., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W824) - 3., HOP4Y(9), PIP_X(id_W824) - 3., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W820) - 4., HOP4Y(7)}, + {PIP_X(id_W820) - 4., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W820) - 5., HOP4Y(5)}, + {PIP_X(id_W820) - 5., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W820) - 6., HOP4Y(3)}, + {PIP_X(id_W820) - 6., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W828) - 7., HOP4Y(1)}, + {PIP_X(id_W828) - 7, HOP4Y(1), PIP_X(id_W828) - 7., WIRE_Y(0)}}}, + {id_E82_loop1, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W820) + 1., HOP4Y(13)}, + {PIP_X(id_W820) + 1., HOP4Y(13), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W820) - 0., HOP4Y(11)}, + {PIP_X(id_W820) - 0., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W820) - 1., HOP4Y(9)}, + {PIP_X(id_W824) - 1., HOP4Y(9), PIP_X(id_W824) - 1., WIRE_Y(0)}, + {PIP_X(id_W820) - 1., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W820) - 2., HOP4Y(7)}, + {PIP_X(id_W820) - 2., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W820) - 3., HOP4Y(5)}, + {PIP_X(id_W820) - 3., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W820) - 4., HOP4Y(3)}, + {PIP_X(id_W820) - 4., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W828) - 5., HOP4Y(1)}, + {PIP_X(id_W828) - 5., HOP4Y(1), PIP_X(id_W828) - 5., WIRE_Y(0)}}}, + {id_E82_loop2, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W820) + 2., HOP4Y(11)}, + {PIP_X(id_W820) + 2., HOP4Y(11), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W820) + 1., HOP4Y(9)}, + {PIP_X(id_W824) + 1., HOP4Y(9), PIP_X(id_W824) + 1., WIRE_Y(0)}, + {PIP_X(id_W820) + 1., HOP4Y(9), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W820) + 0., HOP4Y(7)}, + {PIP_X(id_W820) + 0., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W820) - 1., HOP4Y(5)}, + {PIP_X(id_W820) - 1., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W820) - 2., HOP4Y(3)}, + {PIP_X(id_W820) - 2., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W828) - 3., HOP4Y(1)}, + {PIP_X(id_W828) - 3., HOP4Y(1), PIP_X(id_W828) - 3., WIRE_Y(0)}}}, + {id_E82_loop3, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W820) + 3., HOP4Y(9)}, + {PIP_X(id_W824) + 3., HOP4Y(9), PIP_X(id_W824) + 3., WIRE_Y(0)}, + {PIP_X(id_W820) + 3., HOP4Y(9), PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W820) + 2., HOP4Y(7)}, + {PIP_X(id_W820) + 2., HOP4Y(7), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W820) + 1., HOP4Y(5)}, + {PIP_X(id_W820) + 1., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W820) - 0., HOP4Y(3)}, + {PIP_X(id_W820) - 0., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W828) - 1., HOP4Y(1)}, + {PIP_X(id_W828) - 1., HOP4Y(1), PIP_X(id_W828) - 1., WIRE_Y(0)}}}, + {id_E82_loop4, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W820) + 4., HOP4Y(7)}, + {PIP_X(id_W820) + 4., HOP4Y(7), PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W820) + 3., HOP4Y(5)}, + {PIP_X(id_W820) + 3., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W820) + 2., HOP4Y(3)}, + {PIP_X(id_W820) + 2., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W828) + 1., HOP4Y(1)}, + {PIP_X(id_W828) + 1., HOP4Y(1), PIP_X(id_W828) + 1., WIRE_Y(0)}}}, + {id_E82_loop5, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W820) + 5., HOP4Y(5)}, + {PIP_X(id_W820) + 5., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W820) + 4., HOP4Y(3)}, + {PIP_X(id_W820) + 4., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W828) + 3., HOP4Y(1)}, + {PIP_X(id_W828) + 3., HOP4Y(1), PIP_X(id_W828) + 3., WIRE_Y(0)}}}, + {id_E82_loop6, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, + {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W820) + 6., HOP4Y(3)}, + {PIP_X(id_W820) + 6., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W828) + 5., HOP4Y(1)}, + {PIP_X(id_W828) + 5., HOP4Y(1), PIP_X(id_W828) + 5., WIRE_Y(0)}}}, + {id_E82_loop7, + {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, + {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, + {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, + {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, + {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, + {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, + {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, + {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, + {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W828) + 7., HOP4Y(2)}, + {PIP_X(id_W828) + 7., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W828) + 7., HOP4Y(1)}, + {PIP_X(id_W828) + 7., HOP4Y(1), PIP_X(id_W828) + 7., WIRE_Y(0)}}}, + {id_W82_loop0, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W828) + 0., HOP4Y(14)}, + {PIP_X(id_W828) + 0., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W828) + 1., HOP4Y(12)}, + {PIP_X(id_W828) + 1., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W828) + 2., HOP4Y(10)}, + {PIP_X(id_W828) + 2., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W828) + 3., HOP4Y(8)}, + {PIP_X(id_W828) + 3., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E824) + 3., HOP4Y(8), PIP_X(id_E824) + 3., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W828) + 4., HOP4Y(6)}, + {PIP_X(id_W828) + 4., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W828) + 5., HOP4Y(4)}, + {PIP_X(id_W828) + 5., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W828) + 6., HOP4Y(2)}, + {PIP_X(id_W828) + 6., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E828) + 7., HOP4Y(0)}, + {PIP_X(id_E828) + 7., HOP4Y(0), PIP_X(id_E828) + 7., WIRE_Y(0)}}}, + {id_W82_loop1, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W828) - 1., HOP4Y(12)}, + {PIP_X(id_W828) - 1., HOP4Y(12), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W828) + 0., HOP4Y(10)}, + {PIP_X(id_W828) + 0., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W828) + 1., HOP4Y(8)}, + {PIP_X(id_W828) + 1., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E824) + 1., HOP4Y(8), PIP_X(id_E824) + 1., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W828) + 2., HOP4Y(6)}, + {PIP_X(id_W828) + 2., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W828) + 3., HOP4Y(4)}, + {PIP_X(id_W828) + 3., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W828) + 4., HOP4Y(2)}, + {PIP_X(id_W828) + 4., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E828) + 5., HOP4Y(0)}, + {PIP_X(id_E828) + 5., HOP4Y(0), PIP_X(id_E828) + 5., WIRE_Y(0)}}}, + {id_W82_loop2, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W828) - 2., HOP4Y(10)}, + {PIP_X(id_W828) - 2., HOP4Y(10), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W828) - 1., HOP4Y(8)}, + {PIP_X(id_W828) - 1., HOP4Y(8), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E824) - 1., HOP4Y(8), PIP_X(id_E824) - 1., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W828) + 0., HOP4Y(6)}, + {PIP_X(id_W828) + 0., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W828) + 1., HOP4Y(4)}, + {PIP_X(id_W828) + 1., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W828) + 2., HOP4Y(2)}, + {PIP_X(id_W828) + 2., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E828) + 3., HOP4Y(0)}, + {PIP_X(id_E828) + 3., HOP4Y(0), PIP_X(id_E828) + 3., WIRE_Y(0)}}}, + {id_W82_loop3, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W828) - 3., HOP4Y(8)}, + {PIP_X(id_W828) - 3., HOP4Y(8), PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E824) - 3., HOP4Y(8), PIP_X(id_E824) - 3., WIRE_Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W828) - 2., HOP4Y(6)}, + {PIP_X(id_W828) - 2., HOP4Y(6), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W828) - 1., HOP4Y(4)}, + {PIP_X(id_W828) - 1., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W828) + 0., HOP4Y(2)}, + {PIP_X(id_W828) + 0., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E828) + 1., HOP4Y(0)}, + {PIP_X(id_E828) + 1., HOP4Y(0), PIP_X(id_E828) + 1., WIRE_Y(0)}}}, + {id_W82_loop4, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W824) - 4., HOP4Y(6), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W828) - 4., HOP4Y(6)}, + {PIP_X(id_W828) - 4., HOP4Y(6), PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W828) - 3., HOP4Y(4)}, + {PIP_X(id_W828) - 3., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W828) - 2., HOP4Y(2)}, + {PIP_X(id_W828) - 2., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E828) - 1., HOP4Y(0)}, + {PIP_X(id_E828) - 1., HOP4Y(0), PIP_X(id_E828) - 1., WIRE_Y(0)}}}, + {id_W82_loop5, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W828) - 5., HOP4Y(4)}, + {PIP_X(id_W828) - 5., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W828) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W828) - 4., HOP4Y(2)}, + {PIP_X(id_W828) - 4., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E828) - 3., HOP4Y(0)}, + {PIP_X(id_E828) - 3., HOP4Y(0), PIP_X(id_E828) - 3., WIRE_Y(0)}}}, + {id_W82_loop6, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, + {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W828) - 6., HOP4Y(2)}, + {PIP_X(id_W828) - 6., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W828) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E828) - 5., HOP4Y(0)}, + {PIP_X(id_E828) - 5., HOP4Y(0), PIP_X(id_E828) - 5., WIRE_Y(0)}}}, + {id_W82_loop7, + {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, + {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, + {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, + {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, + {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, + {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, + {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, + {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, + {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W820) - 7., HOP4Y(3)}, + {PIP_X(id_W820) - 7., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E828) - 7., HOP4Y(0)}, + {PIP_X(id_E828) - 7., HOP4Y(0), PIP_X(id_E828) - 7., WIRE_Y(0)}}}, + +#undef HOP4Y +#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f + 18.f + 18.f) + {id_E83, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, + {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W838) + 7., HOP4Y(2)}, + {PIP_X(id_W838) + 7., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E838) + 8., HOP4Y(0)}, + {PIP_X(id_E838) + 8, HOP4Y(0), PIP_X(id_E838) + 8., WIRE_Y(0)}}}, + {id_W83, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, + {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W830) - 7., HOP4Y(3)}, + {PIP_X(id_W830) - 7., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W838) - 8., HOP4Y(1)}, + {PIP_X(id_W838) - 8, HOP4Y(1), PIP_X(id_W838) - 8., WIRE_Y(0)}}}, + {id_E83_loop0, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, + {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, + {wrap_len + 1., HOP4Y(15), PIP_X(id_W830) - 0., HOP4Y(15)}, + {PIP_X(id_W830) - 0., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W830) - 1., HOP4Y(13)}, + {PIP_X(id_W830) - 1., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W830) - 2., HOP4Y(11)}, + {PIP_X(id_W830) - 2., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W830) - 3., HOP4Y(9)}, + {PIP_X(id_W830) - 3., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(7)}, + {PIP_X(id_W834) - 3., HOP4Y(9), PIP_X(id_W834) - 3., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W830) - 4., HOP4Y(7)}, + {PIP_X(id_W830) - 4., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W830) - 5., HOP4Y(5)}, + {PIP_X(id_W830) - 5., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W830) - 6., HOP4Y(3)}, + {PIP_X(id_W830) - 6., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W838) - 7., HOP4Y(1)}, + {PIP_X(id_W838) - 7, HOP4Y(1), PIP_X(id_W838) - 7., WIRE_Y(0)}}}, + {id_E83_loop1, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, + {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, + {wrap_len + 2., HOP4Y(13), PIP_X(id_W830) + 1., HOP4Y(13)}, + {PIP_X(id_W830) + 1., HOP4Y(13), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W830) - 0., HOP4Y(11)}, + {PIP_X(id_W830) - 0., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W830) - 1., HOP4Y(9)}, + {PIP_X(id_W834) - 1., HOP4Y(9), PIP_X(id_W834) - 1., WIRE_Y(0)}, + {PIP_X(id_W830) - 1., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W830) - 2., HOP4Y(7)}, + {PIP_X(id_W830) - 2., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W830) - 3., HOP4Y(5)}, + {PIP_X(id_W830) - 3., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W830) - 4., HOP4Y(3)}, + {PIP_X(id_W830) - 4., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W838) - 5., HOP4Y(1)}, + {PIP_X(id_W838) - 5., HOP4Y(1), PIP_X(id_W838) - 5., WIRE_Y(0)}}}, + {id_E83_loop2, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, + {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, + {wrap_len + 3., HOP4Y(11), PIP_X(id_W830) + 2., HOP4Y(11)}, + {PIP_X(id_W830) + 2., HOP4Y(11), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W830) + 1., HOP4Y(9)}, + {PIP_X(id_W834) + 1., HOP4Y(9), PIP_X(id_W834) + 1., WIRE_Y(0)}, + {PIP_X(id_W830) + 1., HOP4Y(9), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W830) + 0., HOP4Y(7)}, + {PIP_X(id_W830) + 0., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W830) - 1., HOP4Y(5)}, + {PIP_X(id_W830) - 1., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W830) - 2., HOP4Y(3)}, + {PIP_X(id_W830) - 2., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W838) - 3., HOP4Y(1)}, + {PIP_X(id_W838) - 3., HOP4Y(1), PIP_X(id_W838) - 3., WIRE_Y(0)}}}, + {id_E83_loop3, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, + {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, + {wrap_len + 4., HOP4Y(9), PIP_X(id_W830) + 3., HOP4Y(9)}, + {PIP_X(id_W834) + 3., HOP4Y(9), PIP_X(id_W834) + 3., WIRE_Y(0)}, + {PIP_X(id_W830) + 3., HOP4Y(9), PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W830) + 2., HOP4Y(7)}, + {PIP_X(id_W830) + 2., HOP4Y(7), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W830) + 1., HOP4Y(5)}, + {PIP_X(id_W830) + 1., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W830) - 0., HOP4Y(3)}, + {PIP_X(id_W830) - 0., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W838) - 1., HOP4Y(1)}, + {PIP_X(id_W838) - 1., HOP4Y(1), PIP_X(id_W838) - 1., WIRE_Y(0)}}}, + {id_E83_loop4, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, + {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, + {wrap_len + 5., HOP4Y(7), PIP_X(id_W830) + 4., HOP4Y(7)}, + {PIP_X(id_W830) + 4., HOP4Y(7), PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W830) + 3., HOP4Y(5)}, + {PIP_X(id_W830) + 3., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W830) + 2., HOP4Y(3)}, + {PIP_X(id_W830) + 2., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W838) + 1., HOP4Y(1)}, + {PIP_X(id_W838) + 1., HOP4Y(1), PIP_X(id_W838) + 1., WIRE_Y(0)}}}, + {id_E83_loop5, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, + {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, + {wrap_len + 6., HOP4Y(5), PIP_X(id_W830) + 5., HOP4Y(5)}, + {PIP_X(id_W830) + 5., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 5., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W830) + 4., HOP4Y(3)}, + {PIP_X(id_W830) + 4., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W838) + 3., HOP4Y(1)}, + {PIP_X(id_W838) + 3., HOP4Y(1), PIP_X(id_W838) + 3., WIRE_Y(0)}}}, + {id_E83_loop6, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, + {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, + {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, + {wrap_len + 7., HOP4Y(3), PIP_X(id_W830) + 6., HOP4Y(3)}, + {PIP_X(id_W830) + 6., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 6., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W838) + 5., HOP4Y(1)}, + {PIP_X(id_W838) + 5., HOP4Y(1), PIP_X(id_W838) + 5., WIRE_Y(0)}}}, + {id_E83_loop7, + {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, + {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, + {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, + {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, + {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, + {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, + {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, + {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, + {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, + {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W838) + 7., HOP4Y(2)}, + {PIP_X(id_W838) + 7., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, + {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, + {wrap_len + 8., HOP4Y(1), PIP_X(id_W838) + 7., HOP4Y(1)}, + {PIP_X(id_W838) + 7., HOP4Y(1), PIP_X(id_W838) + 7., WIRE_Y(0)}}}, + {id_W83_loop0, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, + {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, + {-wrap_len - 0., HOP4Y(14), PIP_X(id_W838) + 0., HOP4Y(14)}, + {PIP_X(id_W838) + 0., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(12)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W838) + 1., HOP4Y(12)}, + {PIP_X(id_W838) + 1., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W838) + 2., HOP4Y(10)}, + {PIP_X(id_W838) + 2., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W838) + 3., HOP4Y(8)}, + {PIP_X(id_W838) + 3., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(6)}, + {PIP_X(id_E834) + 3., HOP4Y(8), PIP_X(id_E834) + 3., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W838) + 4., HOP4Y(6)}, + {PIP_X(id_W838) + 4., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W838) + 5., HOP4Y(4)}, + {PIP_X(id_W838) + 5., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W838) + 6., HOP4Y(2)}, + {PIP_X(id_W838) + 6., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E838) + 7., HOP4Y(0)}, + {PIP_X(id_E838) + 7., HOP4Y(0), PIP_X(id_E838) + 7., WIRE_Y(0)}}}, + {id_W83_loop1, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, + {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, + {-wrap_len - 1., HOP4Y(12), PIP_X(id_W838) - 1., HOP4Y(12)}, + {PIP_X(id_W838) - 1., HOP4Y(12), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(10)}, + {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W838) + 0., HOP4Y(10)}, + {PIP_X(id_W838) + 0., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W838) + 1., HOP4Y(8)}, + {PIP_X(id_W838) + 1., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(6)}, + {PIP_X(id_E834) + 1., HOP4Y(8), PIP_X(id_E834) + 1., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W838) + 2., HOP4Y(6)}, + {PIP_X(id_W838) + 2., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W838) + 3., HOP4Y(4)}, + {PIP_X(id_W838) + 3., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W838) + 4., HOP4Y(2)}, + {PIP_X(id_W838) + 4., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E838) + 5., HOP4Y(0)}, + {PIP_X(id_E838) + 5., HOP4Y(0), PIP_X(id_E838) + 5., WIRE_Y(0)}}}, + {id_W83_loop2, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, + {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, + {-wrap_len - 2., HOP4Y(10), PIP_X(id_W838) - 2., HOP4Y(10)}, + {PIP_X(id_W838) - 2., HOP4Y(10), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(8)}, + {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W838) - 1., HOP4Y(8)}, + {PIP_X(id_W838) - 1., HOP4Y(8), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(6)}, + {PIP_X(id_E834) - 1., HOP4Y(8), PIP_X(id_E834) - 1., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W838) + 0., HOP4Y(6)}, + {PIP_X(id_W838) + 0., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W838) + 1., HOP4Y(4)}, + {PIP_X(id_W838) + 1., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W838) + 2., HOP4Y(2)}, + {PIP_X(id_W838) + 2., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E838) + 3., HOP4Y(0)}, + {PIP_X(id_E838) + 3., HOP4Y(0), PIP_X(id_E838) + 3., WIRE_Y(0)}}}, + {id_W83_loop3, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, + {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, + {-wrap_len - 3., HOP4Y(8), PIP_X(id_W838) - 3., HOP4Y(8)}, + {PIP_X(id_W838) - 3., HOP4Y(8), PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(6)}, + {PIP_X(id_E834) - 3., HOP4Y(8), PIP_X(id_E834) - 3., WIRE_Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W838) - 2., HOP4Y(6)}, + {PIP_X(id_W838) - 2., HOP4Y(6), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W838) - 1., HOP4Y(4)}, + {PIP_X(id_W838) - 1., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W838) + 0., HOP4Y(2)}, + {PIP_X(id_W838) + 0., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E838) + 1., HOP4Y(0)}, + {PIP_X(id_E838) + 1., HOP4Y(0), PIP_X(id_E838) + 1., WIRE_Y(0)}}}, + {id_W83_loop4, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, + {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, + {PIP_X(id_W834) - 4., HOP4Y(6), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {-wrap_len - 4., HOP4Y(6), PIP_X(id_W838) - 4., HOP4Y(6)}, + {PIP_X(id_W838) - 4., HOP4Y(6), PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(4)}, + {PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W838) - 3., HOP4Y(4)}, + {PIP_X(id_W838) - 3., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W838) - 2., HOP4Y(2)}, + {PIP_X(id_W838) - 2., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E838) - 1., HOP4Y(0)}, + {PIP_X(id_E838) - 1., HOP4Y(0), PIP_X(id_E838) - 1., WIRE_Y(0)}}}, + {id_W83_loop5, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, + {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, + {-wrap_len - 5., HOP4Y(4), PIP_X(id_W838) - 5., HOP4Y(4)}, + {PIP_X(id_W838) - 5., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 5., HOP4Y(2)}, + {PIP_X(id_W838) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W838) - 4., HOP4Y(2)}, + {PIP_X(id_W838) - 4., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E838) - 3., HOP4Y(0)}, + {PIP_X(id_E838) - 3., HOP4Y(0), PIP_X(id_E838) - 3., WIRE_Y(0)}}}, + {id_W83_loop6, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, + {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, + {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, + {-wrap_len - 6., HOP4Y(2), PIP_X(id_W838) - 6., HOP4Y(2)}, + {PIP_X(id_W838) - 6., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 6., HOP4Y(0)}, + {PIP_X(id_W838) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E838) - 5., HOP4Y(0)}, + {PIP_X(id_E838) - 5., HOP4Y(0), PIP_X(id_E838) - 5., WIRE_Y(0)}}}, + {id_W83_loop7, + {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, + {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, + {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, + {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, + {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, + {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, + {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, + {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, + {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, + {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, + {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, + {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, + {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, + {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, + {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, + {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W830) - 7., HOP4Y(3)}, + {PIP_X(id_W830) - 7., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1)}, + {PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, + {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, + {-wrap_len - 7., HOP4Y(0), PIP_X(id_E838) - 7., HOP4Y(0)}, + {PIP_X(id_E838) - 7., HOP4Y(0), PIP_X(id_E838) - 7., WIRE_Y(0)}}}, +}; const int PIP_SRC_DST_LEN = 20; static void get_pip_xy(CruSide side, float &off, float &x, float &y) diff --git a/gowin/gfx.h b/gowin/gfx.h index 574d73106a..b5ea4678ac 100644 --- a/gowin/gfx.h +++ b/gowin/gfx.h @@ -24,4902 +24,6 @@ NEXTPNR_NAMESPACE_BEGIN -// LUTs -const float lut_w = 0.6732 - 0.6386; -const float lut_h = 0.9392 - 0.9074; -const float lut_x = 0.6386; -const float lut_y[] = {1. - 0.9392, 1. - 0.8870, 1. - 0.7864, 1. - 0.7321, - 1. - 0.6399, 1. - 0.5847, 1. - 0.5068, 1. - 0.4503}; -const float dff_w = 0.0580; -const float dff_x = 0.6821; -const float grp_lut_w = 0.1399; -const float grp_lut_h = 0.0954; -const float grp_lut_x = 0.6284; -const float grp_lut_y[] = {1. - 0.9440, 1. - 0.7915, 1. - 0.6442, 1. - 0.5101}; - -// mux -const float mux_w = 0.8134 - 0.7899; -const float mux_f = 0.9450 - 0.9358; -const float mux_h = grp_lut_h; - -const float mux2lut5_x = 0.7900; -const float mux2lut5_y[] = {grp_lut_y[0], grp_lut_y[1], grp_lut_y[2], grp_lut_y[3]}; -const float mux2lut6_x = 0.8378; -const float mux2lut6_y[] = {1. - 0.9261, 1. - 0.6205}; -const float mux2lut7_x = 0.8859; -const float mux2lut7_y = 1. - 0.7870; -const float mux2lut8_x = 0.9337; -const float mux2lut8_y = 1. - 0.8098; - -// pip -enum CruSide -{ - Top, - Bottom, - Left, - Right, - Center -}; -const float cru_x = 0.2568; -const float cru_y = 1. - 0.9783; -const float cru_w = 0.6010 - cru_x; -const float cru_h = 1. - cru_y - 0.3742; - -const float lut_A_off = 1. - 0.9107 - lut_y[0]; -const float lut_D_off = lut_h - lut_A_off; -const float lut_B_off = lut_A_off - (lut_h - lut_D_off) / 3.; -const float lut_C_off = lut_D_off + (lut_h - lut_D_off) / 3.; - -const float right_wire_dist = (grp_lut_y[1] - grp_lut_y[0] - grp_lut_h) / 11.; -const float left_wire_dist = cru_h / 100.; -const float top_wire_dist = cru_w / 100.; -const float clk_ce_set_vdist = (lut_y[1] - lut_y[0] - lut_h) / 4.; - -const float sn_dist = cru_x / 125.; -const float ew_dist = (1. - cru_y - cru_h) / 130.; -const float wrap_len = 0.02f; -const float spine_pip_off = 0.11f; - -const float io_x = cru_x + cru_w + 0.1; -const float io_w = (1. - io_x) / 3.; -const float io_gap = 0.03; -const float io_h = (cru_h - 4. * io_gap) / 2.; -const float io_y = cru_y + io_gap; - -const float ios_scl = 0.5; -const float ios_h = ios_scl * io_h; -const float ios_w = ios_scl * io_w; -const float ios_gap_y = io_gap; -const float ios_gap_x = io_gap * 1.4; -const float ios_x = io_x; -const float ios_y = ios_scl * io_y; - -const dict portPoint = { - {id_O, 3. * io_h / 4.}, - {id_I, 2. * io_h / 4.}, - {id_OEN, 1. * io_h / 4.}, -}; - -const dict>> portSign = { - {id_O, - {{io_h / 14. * 1.33, portPoint.at(id_O) + io_h / 14., io_h / 14. * 1.66, portPoint.at(id_O) + io_h / 14.}, - {io_h / 14. * 1.66, portPoint.at(id_O) + io_h / 14., io_h / 14. * 2., portPoint.at(id_O) + io_h / 14. * 0.6}, - {io_h / 14. * 2., portPoint.at(id_O) + io_h / 14. * 0.6, io_h / 14. * 2., - portPoint.at(id_O) - io_h / 14. * 0.6}, - {io_h / 14. * 2., portPoint.at(id_O) - io_h / 14. * 0.6, io_h / 14. * 1.66, portPoint.at(id_O) - io_h / 14.}, - {io_h / 14. * 1.66, portPoint.at(id_O) - io_h / 14., io_h / 14. * 1.33, portPoint.at(id_O) - io_h / 14.}, - {io_h / 14. * 1.33, portPoint.at(id_O) - io_h / 14., io_h / 14. * 1., portPoint.at(id_O) - io_h / 14. * 0.6}, - {io_h / 14. * 1., portPoint.at(id_O) - io_h / 14. * 0.6, io_h / 14. * 1., - portPoint.at(id_O) + io_h / 14. * 0.6}, - {io_h / 14. * 1., portPoint.at(id_O) + io_h / 14. * 0.6, io_h / 14. * 1.33, - portPoint.at(id_O) + io_h / 14.}}}, - {id_I, - {{io_h / 14., portPoint.at(id_I) + io_h / 14., 2. * io_h / 14., portPoint.at(id_I) + io_h / 14.}, - {io_h / 14. * 1.5, portPoint.at(id_I) + io_h / 14., 1. * io_h / 14. * 1.5, portPoint.at(id_I) - io_h / 14.}, - {io_h / 14., portPoint.at(id_I) - io_h / 14., 2. * io_h / 14., portPoint.at(id_I) - io_h / 14.}}}, - {id_OEN, - {{io_h / 14. * 1.33, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 1.66, portPoint.at(id_OEN) + io_h / 14.}, - {io_h / 14. * 1.66, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 2., - portPoint.at(id_OEN) + io_h / 14. * 0.6}, - {io_h / 14. * 2., portPoint.at(id_OEN) + io_h / 14. * 0.6, io_h / 14. * 2., - portPoint.at(id_OEN) - io_h / 14. * 0.6}, - {io_h / 14. * 2., portPoint.at(id_OEN) - io_h / 14. * 0.6, io_h / 14. * 1.66, - portPoint.at(id_OEN) - io_h / 14.}, - {io_h / 14. * 1.66, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 1.33, portPoint.at(id_OEN) - io_h / 14.}, - {io_h / 14. * 1.33, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 1., - portPoint.at(id_OEN) - io_h / 14. * 0.6}, - {io_h / 14. * 1., portPoint.at(id_OEN) - io_h / 14. * 0.6, io_h / 14. * 1., - portPoint.at(id_OEN) + io_h / 14. * 0.6}, - {io_h / 14. * 1., portPoint.at(id_OEN) + io_h / 14. * 0.6, io_h / 14. * 1.33, - portPoint.at(id_OEN) + io_h / 14.}, - {io_h / 14. * 2.2, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 3.2, portPoint.at(id_OEN) + io_h / 14.}, - {io_h / 14. * 2.2, portPoint.at(id_OEN) + 0., io_h / 14. * 3.2, portPoint.at(id_OEN) + 0.}, - {io_h / 14. * 2.2, portPoint.at(id_OEN) - io_h / 14., io_h / 14. * 3.2, portPoint.at(id_OEN) - io_h / 14.}, - {io_h / 14. * 2.2, portPoint.at(id_OEN) + io_h / 14., io_h / 14. * 2.2, portPoint.at(id_OEN) - io_h / 14.}}}, -}; - -const dict spineY = { - {id_SPINE0, 1. - 1. * ew_dist}, {id_SPINE1, 1. - 2. * ew_dist}, {id_SPINE2, 1. - 3. * ew_dist}, - {id_SPINE3, 1. - 4. * ew_dist}, {id_SPINE4, 1. - 5. * ew_dist}, {id_SPINE5, 1. - 6. * ew_dist}, - {id_SPINE6, 1. - 7. * ew_dist}, {id_SPINE7, 1. - 8. * ew_dist}, {id_SPINE8, 1. - 1. * ew_dist}, - {id_SPINE9, 1. - 2. * ew_dist}, {id_SPINE10, 1. - 3. * ew_dist}, {id_SPINE11, 1. - 4. * ew_dist}, - {id_SPINE12, 1. - 5. * ew_dist}, {id_SPINE13, 1. - 6. * ew_dist}, {id_SPINE14, 1. - 7. * ew_dist}, - {id_SPINE15, 1. - 8. * ew_dist}, {id_SPINE16, 1. - 1. * ew_dist}, {id_SPINE17, 1. - 2. * ew_dist}, - {id_SPINE18, 1. - 3. * ew_dist}, {id_SPINE19, 1. - 4. * ew_dist}, {id_SPINE20, 1. - 5. * ew_dist}, - {id_SPINE21, 1. - 6. * ew_dist}, {id_SPINE22, 1. - 7. * ew_dist}, {id_SPINE23, 1. - 8. * ew_dist}, - {id_SPINE24, 1. - 1. * ew_dist}, {id_SPINE25, 1. - 2. * ew_dist}, {id_SPINE26, 1. - 3. * ew_dist}, - {id_SPINE27, 1. - 4. * ew_dist}, {id_SPINE28, 1. - 5. * ew_dist}, {id_SPINE29, 1. - 6. * ew_dist}, - {id_SPINE30, 1. - 7. * ew_dist}, {id_SPINE31, 1. - 8. * ew_dist}, -}; - -const dict> pipPoint = { - {id_X01, {Center, cru_y + 1. * cru_h / 9.}}, - {id_X02, {Center, cru_y + 2. * cru_h / 9.}}, - {id_X03, {Center, cru_y + 3. * cru_h / 9.}}, - {id_X04, {Center, cru_y + 4. * cru_h / 9.}}, - {id_X05, {Center, cru_y + 5. * cru_h / 9.}}, - {id_X06, {Center, cru_y + 6. * cru_h / 9.}}, - {id_X07, {Center, cru_y + 7. * cru_h / 9.}}, - {id_X08, {Center, cru_y + 8. * cru_h / 9.}}, - // LUT inputs - {id_A0, {Right, lut_y[0] + lut_A_off}}, - {id_B0, {Right, lut_y[0] + lut_B_off}}, - {id_C0, {Right, lut_y[0] + lut_C_off}}, - {id_D0, {Right, lut_y[0] + lut_D_off}}, - {id_A1, {Right, lut_y[1] + lut_A_off}}, - {id_B1, {Right, lut_y[1] + lut_B_off}}, - {id_C1, {Right, lut_y[1] + lut_C_off}}, - {id_D1, {Right, lut_y[1] + lut_D_off}}, - {id_A2, {Right, lut_y[2] + lut_A_off}}, - {id_B2, {Right, lut_y[2] + lut_B_off}}, - {id_C2, {Right, lut_y[2] + lut_C_off}}, - {id_D2, {Right, lut_y[2] + lut_D_off}}, - {id_A3, {Right, lut_y[3] + lut_A_off}}, - {id_B3, {Right, lut_y[3] + lut_B_off}}, - {id_C3, {Right, lut_y[3] + lut_C_off}}, - {id_D3, {Right, lut_y[3] + lut_D_off}}, - {id_A4, {Right, lut_y[4] + lut_A_off}}, - {id_B4, {Right, lut_y[4] + lut_B_off}}, - {id_C4, {Right, lut_y[4] + lut_C_off}}, - {id_D4, {Right, lut_y[4] + lut_D_off}}, - {id_A5, {Right, lut_y[5] + lut_A_off}}, - {id_B5, {Right, lut_y[5] + lut_B_off}}, - {id_C5, {Right, lut_y[5] + lut_C_off}}, - {id_D5, {Right, lut_y[5] + lut_D_off}}, - {id_A6, {Right, lut_y[6] + lut_A_off}}, - {id_B6, {Right, lut_y[6] + lut_B_off}}, - {id_C6, {Right, lut_y[6] + lut_C_off}}, - {id_D6, {Right, lut_y[6] + lut_D_off}}, - {id_A7, {Right, lut_y[7] + lut_A_off}}, - {id_B7, {Right, lut_y[7] + lut_B_off}}, - {id_C7, {Right, lut_y[7] + lut_C_off}}, - {id_D7, {Right, lut_y[7] + lut_D_off}}, - // wires below LUT0 - {id_Q0, {Right, grp_lut_y[0] - right_wire_dist}}, - {id_F0, {Right, grp_lut_y[0] - 2. * right_wire_dist}}, - {id_OF3, {Right, grp_lut_y[0] - 3. * right_wire_dist}}, - // wires between LUT1 and LUT2 - {id_Q2, {Right, grp_lut_y[1] - right_wire_dist}}, - {id_F2, {Right, grp_lut_y[1] - 2. * right_wire_dist}}, - {id_OF2, {Right, grp_lut_y[1] - 3. * right_wire_dist}}, - {id_OF1, {Right, grp_lut_y[1] - 4. * right_wire_dist}}, - {id_OF0, {Right, grp_lut_y[1] - 5. * right_wire_dist}}, - {id_SEL1, {Right, grp_lut_y[1] - 6. * right_wire_dist}}, - {id_OF7, {Right, grp_lut_y[1] - 7. * right_wire_dist}}, - {id_SEL0, {Right, grp_lut_y[1] - 8. * right_wire_dist}}, - {id_F1, {Right, grp_lut_y[1] - 9. * right_wire_dist}}, - {id_Q1, {Right, grp_lut_y[1] - 10. * right_wire_dist}}, - // wires between LUT3 and LUT4 - {id_Q4, {Right, grp_lut_y[2] - right_wire_dist}}, - {id_F4, {Right, grp_lut_y[2] - 2. * right_wire_dist}}, - {id_OF4, {Right, grp_lut_y[2] - 3. * right_wire_dist}}, - {id_OF5, {Right, grp_lut_y[2] - 4. * right_wire_dist}}, - {id_SEL7, {Right, grp_lut_y[2] - 5. * right_wire_dist}}, - {id_SEL3, {Right, grp_lut_y[2] - 6. * right_wire_dist}}, - {id_SEL2, {Right, grp_lut_y[2] - 7. * right_wire_dist}}, - {id_F3, {Right, grp_lut_y[2] - 8. * right_wire_dist}}, - {id_Q3, {Right, grp_lut_y[2] - 9. * right_wire_dist}}, - // wires between LUT5 and LUT6 - {id_F6, {Right, grp_lut_y[3] - right_wire_dist}}, - {id_SEL5, {Right, grp_lut_y[3] - 2. * right_wire_dist}}, - {id_SEL4, {Right, grp_lut_y[3] - 4. * right_wire_dist}}, - {id_F5, {Right, grp_lut_y[3] - 5. * right_wire_dist}}, - {id_Q5, {Right, grp_lut_y[3] - 6. * right_wire_dist}}, - // Q6, Q7 --- IOB - {id_Q6, {Right, grp_lut_y[3] + grp_lut_h * 0.33}}, - {id_Q7, {Right, grp_lut_y[3] + grp_lut_h * 0.66}}, - // wires above LUT7 - {id_F7, {Right, grp_lut_y[3] + grp_lut_h + right_wire_dist}}, - {id_SEL6, {Right, grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist}}, - {id_OF6, {Right, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist}}, - // DI0-5 - {id_DI5, {Right, cru_y + cru_h - 0.5 * right_wire_dist}}, - {id_DI4, {Right, cru_y + cru_h - 1. * right_wire_dist}}, - {id_DI3, {Right, cru_y + cru_h - 1.5 * right_wire_dist}}, - {id_DI2, {Right, cru_y + cru_h - 2. * right_wire_dist}}, - {id_DI1, {Right, cru_y + cru_h - 2.5 * right_wire_dist}}, - {id_DI0, {Right, cru_y + cru_h - 3. * right_wire_dist}}, - // Q6 - // CLK, CE, SET-RESET - {id_CLK0, {Right, lut_y[1] - clk_ce_set_vdist}}, - {id_CE0, {Right, lut_y[1] - 2. * clk_ce_set_vdist}}, - {id_LSR0, {Right, lut_y[1] - 3. * clk_ce_set_vdist}}, - {id_CLK1, {Right, lut_y[3] - clk_ce_set_vdist}}, - {id_CE1, {Right, lut_y[3] - 2. * clk_ce_set_vdist}}, - {id_LSR1, {Right, lut_y[3] - 3. * clk_ce_set_vdist}}, - {id_CLK2, {Right, lut_y[5] - clk_ce_set_vdist}}, - {id_CE2, {Right, lut_y[5] - 2. * clk_ce_set_vdist}}, - {id_LSR2, {Right, lut_y[5] - 3. * clk_ce_set_vdist}}, - // SN - // 1 hop - {id_S100, {Left, cru_y + 1. * left_wire_dist}}, - {id_S101, {Left, cru_y + 2. * left_wire_dist}}, - {id_S130, {Left, cru_y + 3. * left_wire_dist}}, - {id_S131, {Left, cru_y + 4. * left_wire_dist}}, - {id_N101, {Left, cru_y + 5. * left_wire_dist}}, - {id_N100, {Left, cru_y + 6. * left_wire_dist}}, - {id_N131, {Left, cru_y + 7. * left_wire_dist}}, - {id_N130, {Left, cru_y + 8. * left_wire_dist}}, - // 1 hop SN - {id_N111, {Left, cru_y + 9. * left_wire_dist}}, - {id_SN10, {Left, cru_y + 10. * left_wire_dist}}, - {id_S111, {Left, cru_y + 11. * left_wire_dist}}, - {id_N121, {Left, cru_y + 12. * left_wire_dist}}, - {id_SN20, {Left, cru_y + 13. * left_wire_dist}}, - {id_S121, {Left, cru_y + 14. * left_wire_dist}}, - // 2 hop - {id_S200, {Left, cru_y + 15. * left_wire_dist}}, - {id_S201, {Left, cru_y + 16. * left_wire_dist}}, - {id_N202, {Left, cru_y + 17. * left_wire_dist}}, - {id_S202, {Left, cru_y + 18. * left_wire_dist}}, - {id_N201, {Left, cru_y + 19. * left_wire_dist}}, - {id_N200, {Left, cru_y + 20. * left_wire_dist}}, - - {id_S210, {Left, cru_y + 21. * left_wire_dist}}, - {id_S211, {Left, cru_y + 22. * left_wire_dist}}, - {id_N212, {Left, cru_y + 23. * left_wire_dist}}, - {id_S212, {Left, cru_y + 24. * left_wire_dist}}, - {id_N211, {Left, cru_y + 25. * left_wire_dist}}, - {id_N210, {Left, cru_y + 26. * left_wire_dist}}, - - {id_S220, {Left, cru_y + 27. * left_wire_dist}}, - {id_S221, {Left, cru_y + 28. * left_wire_dist}}, - {id_N222, {Left, cru_y + 29. * left_wire_dist}}, - {id_S222, {Left, cru_y + 30. * left_wire_dist}}, - {id_N221, {Left, cru_y + 31. * left_wire_dist}}, - {id_N220, {Left, cru_y + 32. * left_wire_dist}}, - - {id_S230, {Left, cru_y + 33. * left_wire_dist}}, - {id_S231, {Left, cru_y + 34. * left_wire_dist}}, - {id_N232, {Left, cru_y + 35. * left_wire_dist}}, - {id_S232, {Left, cru_y + 36. * left_wire_dist}}, - {id_N231, {Left, cru_y + 37. * left_wire_dist}}, - {id_N230, {Left, cru_y + 38. * left_wire_dist}}, - - {id_S240, {Left, cru_y + 39. * left_wire_dist}}, - {id_S241, {Left, cru_y + 40. * left_wire_dist}}, - {id_N242, {Left, cru_y + 41. * left_wire_dist}}, - {id_S242, {Left, cru_y + 42. * left_wire_dist}}, - {id_N241, {Left, cru_y + 43. * left_wire_dist}}, - {id_N240, {Left, cru_y + 44. * left_wire_dist}}, - - {id_S250, {Left, cru_y + 45. * left_wire_dist}}, - {id_S251, {Left, cru_y + 46. * left_wire_dist}}, - {id_N252, {Left, cru_y + 47. * left_wire_dist}}, - {id_S252, {Left, cru_y + 48. * left_wire_dist}}, - {id_N251, {Left, cru_y + 49. * left_wire_dist}}, - {id_N250, {Left, cru_y + 50. * left_wire_dist}}, - - {id_S260, {Left, cru_y + 51. * left_wire_dist}}, - {id_S261, {Left, cru_y + 52. * left_wire_dist}}, - {id_N262, {Left, cru_y + 53. * left_wire_dist}}, - {id_S262, {Left, cru_y + 54. * left_wire_dist}}, - {id_N261, {Left, cru_y + 55. * left_wire_dist}}, - {id_N260, {Left, cru_y + 56. * left_wire_dist}}, - - {id_S270, {Left, cru_y + 57. * left_wire_dist}}, - {id_S271, {Left, cru_y + 58. * left_wire_dist}}, - {id_N272, {Left, cru_y + 59. * left_wire_dist}}, - {id_S272, {Left, cru_y + 60. * left_wire_dist}}, - {id_N271, {Left, cru_y + 61. * left_wire_dist}}, - {id_N270, {Left, cru_y + 62. * left_wire_dist}}, - - // Clocks - {id_GT10, {Left, cru_y + 63. * left_wire_dist}}, - {id_GT00, {Left, cru_y + 68. * left_wire_dist}}, - - // 4 hop - {id_N808, {Left, cru_y + 73. * left_wire_dist}}, - {id_S800, {Left, cru_y + 74. * left_wire_dist}}, - {id_S804, {Left, cru_y + 75. * left_wire_dist}}, - {id_N804, {Left, cru_y + 76. * left_wire_dist}}, - {id_N800, {Left, cru_y + 77. * left_wire_dist}}, - {id_S808, {Left, cru_y + 78. * left_wire_dist}}, - - {id_N818, {Left, cru_y + 79. * left_wire_dist}}, - {id_S810, {Left, cru_y + 80. * left_wire_dist}}, - {id_S814, {Left, cru_y + 81. * left_wire_dist}}, - {id_N814, {Left, cru_y + 82. * left_wire_dist}}, - {id_N810, {Left, cru_y + 83. * left_wire_dist}}, - {id_S818, {Left, cru_y + 84. * left_wire_dist}}, - - {id_N828, {Left, cru_y + 85. * left_wire_dist}}, - {id_S820, {Left, cru_y + 86. * left_wire_dist}}, - {id_S824, {Left, cru_y + 87. * left_wire_dist}}, - {id_N824, {Left, cru_y + 88. * left_wire_dist}}, - {id_N820, {Left, cru_y + 89. * left_wire_dist}}, - {id_S828, {Left, cru_y + 90. * left_wire_dist}}, - - {id_N838, {Left, cru_y + 91. * left_wire_dist}}, - {id_S830, {Left, cru_y + 92. * left_wire_dist}}, - {id_S834, {Left, cru_y + 93. * left_wire_dist}}, - {id_N834, {Left, cru_y + 94. * left_wire_dist}}, - {id_N830, {Left, cru_y + 95. * left_wire_dist}}, - {id_S838, {Left, cru_y + 96. * left_wire_dist}}, - - // EW - // 1 hop - {id_E101, {Top, cru_x + 1. * top_wire_dist}}, - {id_E100, {Top, cru_x + 2. * top_wire_dist}}, - {id_E131, {Top, cru_x + 3. * top_wire_dist}}, - {id_E130, {Top, cru_x + 4. * top_wire_dist}}, - {id_W100, {Top, cru_x + 5. * top_wire_dist}}, - {id_W101, {Top, cru_x + 6. * top_wire_dist}}, - {id_W130, {Top, cru_x + 7. * top_wire_dist}}, - {id_W131, {Top, cru_x + 8. * top_wire_dist}}, - // 1 hop EW - {id_E111, {Top, cru_x + 9. * top_wire_dist}}, - {id_EW10, {Top, cru_x + 10. * top_wire_dist}}, - {id_W111, {Top, cru_x + 11. * top_wire_dist}}, - {id_E121, {Top, cru_x + 12. * top_wire_dist}}, - {id_EW20, {Top, cru_x + 13. * top_wire_dist}}, - {id_W121, {Top, cru_x + 14. * top_wire_dist}}, - // 2 hop - {id_E202, {Top, cru_x + 15. * top_wire_dist}}, - {id_E201, {Top, cru_x + 16. * top_wire_dist}}, - {id_W200, {Top, cru_x + 17. * top_wire_dist}}, - {id_E200, {Top, cru_x + 18. * top_wire_dist}}, - {id_W201, {Top, cru_x + 19. * top_wire_dist}}, - {id_W202, {Top, cru_x + 20. * top_wire_dist}}, - - {id_E212, {Top, cru_x + 21. * top_wire_dist}}, - {id_E211, {Top, cru_x + 22. * top_wire_dist}}, - {id_W210, {Top, cru_x + 23. * top_wire_dist}}, - {id_E210, {Top, cru_x + 24. * top_wire_dist}}, - {id_W211, {Top, cru_x + 25. * top_wire_dist}}, - {id_W212, {Top, cru_x + 26. * top_wire_dist}}, - - {id_E222, {Top, cru_x + 27. * top_wire_dist}}, - {id_E221, {Top, cru_x + 28. * top_wire_dist}}, - {id_W220, {Top, cru_x + 29. * top_wire_dist}}, - {id_E220, {Top, cru_x + 30. * top_wire_dist}}, - {id_W221, {Top, cru_x + 31. * top_wire_dist}}, - {id_W222, {Top, cru_x + 32. * top_wire_dist}}, - - {id_E232, {Top, cru_x + 33. * top_wire_dist}}, - {id_E231, {Top, cru_x + 34. * top_wire_dist}}, - {id_W230, {Top, cru_x + 35. * top_wire_dist}}, - {id_E230, {Top, cru_x + 36. * top_wire_dist}}, - {id_W231, {Top, cru_x + 37. * top_wire_dist}}, - {id_W232, {Top, cru_x + 38. * top_wire_dist}}, - - {id_E242, {Top, cru_x + 39. * top_wire_dist}}, - {id_E241, {Top, cru_x + 40. * top_wire_dist}}, - {id_W240, {Top, cru_x + 41. * top_wire_dist}}, - {id_E240, {Top, cru_x + 42. * top_wire_dist}}, - {id_W241, {Top, cru_x + 43. * top_wire_dist}}, - {id_W242, {Top, cru_x + 44. * top_wire_dist}}, - - {id_E252, {Top, cru_x + 45. * top_wire_dist}}, - {id_E251, {Top, cru_x + 46. * top_wire_dist}}, - {id_W250, {Top, cru_x + 47. * top_wire_dist}}, - {id_E250, {Top, cru_x + 48. * top_wire_dist}}, - {id_W251, {Top, cru_x + 49. * top_wire_dist}}, - {id_W252, {Top, cru_x + 50. * top_wire_dist}}, - - {id_E262, {Top, cru_x + 51. * top_wire_dist}}, - {id_E261, {Top, cru_x + 52. * top_wire_dist}}, - {id_W260, {Top, cru_x + 53. * top_wire_dist}}, - {id_E260, {Top, cru_x + 54. * top_wire_dist}}, - {id_W261, {Top, cru_x + 55. * top_wire_dist}}, - {id_W262, {Top, cru_x + 56. * top_wire_dist}}, - - {id_E272, {Top, cru_x + 57. * top_wire_dist}}, - {id_E271, {Top, cru_x + 58. * top_wire_dist}}, - {id_W270, {Top, cru_x + 59. * top_wire_dist}}, - {id_E270, {Top, cru_x + 60. * top_wire_dist}}, - {id_W271, {Top, cru_x + 61. * top_wire_dist}}, - {id_W272, {Top, cru_x + 62. * top_wire_dist}}, - - // Global taps -> bracnhes - {id_GBO0, {Top, cru_x + 63. * top_wire_dist}}, - {id_GB00, {Top, cru_x + 64. * top_wire_dist}}, - {id_GB10, {Top, cru_x + 65. * top_wire_dist}}, - {id_GB20, {Top, cru_x + 66. * top_wire_dist}}, - {id_GB30, {Top, cru_x + 67. * top_wire_dist}}, - {id_GBO1, {Top, cru_x + 68. * top_wire_dist}}, - {id_GB40, {Top, cru_x + 68. * top_wire_dist}}, - {id_GB50, {Top, cru_x + 69. * top_wire_dist}}, - {id_GB60, {Top, cru_x + 70. * top_wire_dist}}, - {id_GB70, {Top, cru_x + 71. * top_wire_dist}}, - - // 4 hop - {id_E808, {Top, cru_x + 72. * top_wire_dist}}, - {id_W800, {Top, cru_x + 73. * top_wire_dist}}, - {id_W804, {Top, cru_x + 74. * top_wire_dist}}, - {id_E804, {Top, cru_x + 75. * top_wire_dist}}, - {id_E800, {Top, cru_x + 76. * top_wire_dist}}, - {id_W808, {Top, cru_x + 77. * top_wire_dist}}, - - {id_E818, {Top, cru_x + 78. * top_wire_dist}}, - {id_W810, {Top, cru_x + 79. * top_wire_dist}}, - {id_W814, {Top, cru_x + 80. * top_wire_dist}}, - {id_E814, {Top, cru_x + 81. * top_wire_dist}}, - {id_E810, {Top, cru_x + 82. * top_wire_dist}}, - {id_W818, {Top, cru_x + 83. * top_wire_dist}}, - - {id_E828, {Top, cru_x + 84. * top_wire_dist}}, - {id_W820, {Top, cru_x + 85. * top_wire_dist}}, - {id_W824, {Top, cru_x + 86. * top_wire_dist}}, - {id_E824, {Top, cru_x + 87. * top_wire_dist}}, - {id_E820, {Top, cru_x + 88. * top_wire_dist}}, - {id_W828, {Top, cru_x + 89. * top_wire_dist}}, - - {id_E838, {Top, cru_x + 90. * top_wire_dist}}, - {id_W830, {Top, cru_x + 91. * top_wire_dist}}, - {id_W834, {Top, cru_x + 92. * top_wire_dist}}, - {id_E834, {Top, cru_x + 93. * top_wire_dist}}, - {id_E830, {Top, cru_x + 94. * top_wire_dist}}, - {id_W838, {Top, cru_x + 95. * top_wire_dist}}, - -}; - -// wire -const std::vector decalless_wires = {id_X01, id_X02, id_X03, id_X04, id_X05, id_X06, id_X07}; - -const float clk_ce_set_hdist = dff_w / 4.; -const float dff_f_x = (grp_lut_x + grp_lut_w + dff_x + dff_w) / 2.; -const float mux5i_x = (grp_lut_x + grp_lut_w + mux2lut5_x) / 2.; - -// id, {x1, y1, x2, y2} -const dict>> sliceLocalWires = { - // dff - {id_CLK0, - {{cru_x + cru_w, pipPoint.at(id_CLK0).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK0).second}, - {dff_x + clk_ce_set_hdist, lut_y[1], dff_x + clk_ce_set_hdist, lut_y[0] + lut_h}}}, - {id_CE0, - {{cru_x + cru_w, pipPoint.at(id_CE0).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE0).second}, - {dff_x + 2 * clk_ce_set_hdist, lut_y[1], dff_x + 2. * clk_ce_set_hdist, lut_y[0] + lut_h}}}, - {id_LSR0, - {{cru_x + cru_w, pipPoint.at(id_LSR0).second, dff_x + 3. * clk_ce_set_hdist, pipPoint.at(id_LSR0).second}, - {dff_x + 3 * clk_ce_set_hdist, lut_y[1], dff_x + 3. * clk_ce_set_hdist, lut_y[0] + lut_h}}}, - {id_CLK1, - {{cru_x + cru_w, pipPoint.at(id_CLK1).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK1).second}, - {dff_x + clk_ce_set_hdist, lut_y[3], dff_x + clk_ce_set_hdist, lut_y[2] + lut_h}}}, - {id_CE1, - {{cru_x + cru_w, pipPoint.at(id_CE1).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE1).second}, - {dff_x + 2 * clk_ce_set_hdist, lut_y[3], dff_x + 2. * clk_ce_set_hdist, lut_y[2] + lut_h}}}, - {id_LSR1, - {{cru_x + cru_w, pipPoint.at(id_LSR1).second, dff_x + 3. * clk_ce_set_hdist, pipPoint.at(id_LSR1).second}, - {dff_x + 3 * clk_ce_set_hdist, lut_y[3], dff_x + 3. * clk_ce_set_hdist, lut_y[2] + lut_h}}}, - {id_CLK2, - {{cru_x + cru_w, pipPoint.at(id_CLK2).second, dff_x + clk_ce_set_hdist, pipPoint.at(id_CLK2).second}, - {dff_x + clk_ce_set_hdist, lut_y[5], dff_x + clk_ce_set_hdist, lut_y[4] + lut_h}}}, - {id_CE2, - {{cru_x + cru_w, pipPoint.at(id_CE2).second, dff_x + 2. * clk_ce_set_hdist, pipPoint.at(id_CE2).second}, - {dff_x + 2 * clk_ce_set_hdist, lut_y[5], dff_x + 2. * clk_ce_set_hdist, lut_y[4] + lut_h}}}, - {id_LSR2, - {{cru_x + cru_w, pipPoint.at(id_LSR2).second, dff_x + 3 * clk_ce_set_hdist, pipPoint.at(id_LSR2).second}, - {dff_x + 3 * clk_ce_set_hdist, lut_y[5], dff_x + 3. * clk_ce_set_hdist, lut_y[4] + lut_h}}}, - // lut - {id_A0, {{cru_x + cru_w, lut_y[0] + lut_A_off, lut_x, lut_y[0] + lut_A_off}}}, - {id_B0, {{cru_x + cru_w, lut_y[0] + lut_B_off, lut_x, lut_y[0] + lut_B_off}}}, - {id_C0, {{cru_x + cru_w, lut_y[0] + lut_C_off, lut_x, lut_y[0] + lut_C_off}}}, - {id_D0, {{cru_x + cru_w, lut_y[0] + lut_D_off, lut_x, lut_y[0] + lut_D_off}}}, - {id_A1, {{cru_x + cru_w, lut_y[1] + lut_A_off, lut_x, lut_y[1] + lut_A_off}}}, - {id_B1, {{cru_x + cru_w, lut_y[1] + lut_B_off, lut_x, lut_y[1] + lut_B_off}}}, - {id_C1, {{cru_x + cru_w, lut_y[1] + lut_C_off, lut_x, lut_y[1] + lut_C_off}}}, - {id_D1, {{cru_x + cru_w, lut_y[1] + lut_D_off, lut_x, lut_y[1] + lut_D_off}}}, - {id_A2, {{cru_x + cru_w, lut_y[2] + lut_A_off, lut_x, lut_y[2] + lut_A_off}}}, - {id_B2, {{cru_x + cru_w, lut_y[2] + lut_B_off, lut_x, lut_y[2] + lut_B_off}}}, - {id_C2, {{cru_x + cru_w, lut_y[2] + lut_C_off, lut_x, lut_y[2] + lut_C_off}}}, - {id_D2, {{cru_x + cru_w, lut_y[2] + lut_D_off, lut_x, lut_y[2] + lut_D_off}}}, - {id_A3, {{cru_x + cru_w, lut_y[3] + lut_A_off, lut_x, lut_y[3] + lut_A_off}}}, - {id_B3, {{cru_x + cru_w, lut_y[3] + lut_B_off, lut_x, lut_y[3] + lut_B_off}}}, - {id_C3, {{cru_x + cru_w, lut_y[3] + lut_C_off, lut_x, lut_y[3] + lut_C_off}}}, - {id_D3, {{cru_x + cru_w, lut_y[3] + lut_D_off, lut_x, lut_y[3] + lut_D_off}}}, - {id_A4, {{cru_x + cru_w, lut_y[4] + lut_A_off, lut_x, lut_y[4] + lut_A_off}}}, - {id_B4, {{cru_x + cru_w, lut_y[4] + lut_B_off, lut_x, lut_y[4] + lut_B_off}}}, - {id_C4, {{cru_x + cru_w, lut_y[4] + lut_C_off, lut_x, lut_y[4] + lut_C_off}}}, - {id_D4, {{cru_x + cru_w, lut_y[4] + lut_D_off, lut_x, lut_y[4] + lut_D_off}}}, - {id_A5, {{cru_x + cru_w, lut_y[5] + lut_A_off, lut_x, lut_y[5] + lut_A_off}}}, - {id_B5, {{cru_x + cru_w, lut_y[5] + lut_B_off, lut_x, lut_y[5] + lut_B_off}}}, - {id_C5, {{cru_x + cru_w, lut_y[5] + lut_C_off, lut_x, lut_y[5] + lut_C_off}}}, - {id_D5, {{cru_x + cru_w, lut_y[5] + lut_D_off, lut_x, lut_y[5] + lut_D_off}}}, - {id_A6, {{cru_x + cru_w, lut_y[6] + lut_A_off, lut_x, lut_y[6] + lut_A_off}}}, - {id_B6, {{cru_x + cru_w, lut_y[6] + lut_B_off, lut_x, lut_y[6] + lut_B_off}}}, - {id_C6, {{cru_x + cru_w, lut_y[6] + lut_C_off, lut_x, lut_y[6] + lut_C_off}}}, - {id_D6, {{cru_x + cru_w, lut_y[6] + lut_D_off, lut_x, lut_y[6] + lut_D_off}}}, - {id_A7, {{cru_x + cru_w, lut_y[7] + lut_A_off, lut_x, lut_y[7] + lut_A_off}}}, - {id_B7, {{cru_x + cru_w, lut_y[7] + lut_B_off, lut_x, lut_y[7] + lut_B_off}}}, - {id_C7, {{cru_x + cru_w, lut_y[7] + lut_C_off, lut_x, lut_y[7] + lut_C_off}}}, - {id_D7, {{cru_x + cru_w, lut_y[7] + lut_D_off, lut_x, lut_y[7] + lut_D_off}}}, - // wires below LUT0 - {id_Q0, - {{cru_x + cru_w, grp_lut_y[0] - right_wire_dist, dff_f_x, grp_lut_y[0] - right_wire_dist}, - {dff_f_x, grp_lut_y[0] - right_wire_dist, dff_f_x, lut_y[0] + lut_h / 2.}, - {dff_f_x, lut_y[0] + lut_h / 2., dff_x + dff_w, lut_y[0] + lut_h / 2.}}}, - {id_F0, - {{cru_x + cru_w, grp_lut_y[0] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[0] - 2. * right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[0] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[0] + lut_h / 2.}, - {lut_x + lut_w, lut_y[0] + lut_h / 2., dff_x, lut_y[0] + lut_h / 2.}}}, - {id_I0MUX0, - {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[0] - 2. * right_wire_dist, mux5i_x, - grp_lut_y[0] - 2. * right_wire_dist}, - {mux5i_x, grp_lut_y[0] - 2. * right_wire_dist, mux5i_x, lut_y[0] + lut_h / 2.}, - {mux5i_x, lut_y[0] + lut_h / 2., mux2lut5_x, lut_y[0] + lut_h / 2.}}}, - {id_OF3, - {{cru_x + cru_w, grp_lut_y[0] - 3. * right_wire_dist, mux2lut7_x + 4. / 3. * mux_w, - grp_lut_y[0] - 3. * right_wire_dist}, - {mux2lut7_x + 4. / 3. * mux_w, grp_lut_y[0] - 3. * right_wire_dist, mux2lut7_x + 4. / 3. * mux_w, - mux2lut7_y + mux_h / 2.}, - {mux2lut7_x + 4. / 3. * mux_w, mux2lut7_y + mux_h / 2., mux2lut7_x + mux_w, mux2lut7_y + mux_h / 2.}}}, - {id_I1MUX7, {{mux2lut7_x + 4. / 3. * mux_w, mux2lut7_y + mux_h / 2., mux2lut8_x, mux2lut7_y + mux_h / 2.}}}, - // wires between LUT1 and LUT2 - {id_Q1, - {{cru_x + cru_w, grp_lut_y[1] - 10. * right_wire_dist, dff_f_x, grp_lut_y[1] - 10. * right_wire_dist}, - {dff_f_x, grp_lut_y[1] - 10. * right_wire_dist, dff_f_x, lut_y[1] + lut_h / 2.}, - {dff_f_x, lut_y[1] + lut_h / 2., dff_x + dff_w, lut_y[1] + lut_h / 2.}}}, - {id_F1, - {{cru_x + cru_w, grp_lut_y[1] - 9. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[1] - 9. * right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 9. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[1] + lut_h / 2.}, - {lut_x + lut_w, lut_y[1] + lut_h / 2., dff_x, lut_y[1] + lut_h / 2.}}}, - {id_I1MUX0, - {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 9. * right_wire_dist, mux5i_x, - grp_lut_y[1] - 9. * right_wire_dist}, - {mux5i_x, grp_lut_y[1] - 9. * right_wire_dist, mux5i_x, lut_y[1] + lut_h / 2.}, - {mux5i_x, lut_y[1] + lut_h / 2., mux2lut5_x, lut_y[1] + lut_h / 2.}}}, - {id_SEL0, - {{cru_x + cru_w, grp_lut_y[1] - 8. * right_wire_dist, mux2lut5_x + mux_w / 2., - grp_lut_y[1] - 8. * right_wire_dist}, - {mux2lut5_x + mux_w / 2., grp_lut_y[1] - 8. * right_wire_dist, mux2lut5_x + mux_w / 2., - mux2lut5_y[0] + mux_h - mux_f / 2.}}}, - {id_OF7, - {{cru_x + cru_w, grp_lut_y[1] - 7. * right_wire_dist, mux2lut8_x + 4. / 3. * mux_w, - grp_lut_y[1] - 7. * right_wire_dist}, - {mux2lut8_x + 4. / 3. * mux_w, grp_lut_y[1] - 7. * right_wire_dist, mux2lut8_x + 4. / 3. * mux_w, - mux2lut8_y + mux_h / 2.}, - {mux2lut8_x + 4. / 3. * mux_w, mux2lut8_y + mux_h / 2., mux2lut8_x + mux_w, mux2lut8_y + mux_h / 2.}}}, - {id_SEL1, - {{cru_x + cru_w, grp_lut_y[1] - 6. * right_wire_dist, mux2lut6_x + mux_w / 2., - grp_lut_y[1] - 6. * right_wire_dist}, - {mux2lut6_x + mux_w / 2., grp_lut_y[1] - 6. * right_wire_dist, mux2lut6_x + mux_w / 2., - mux2lut6_y[0] + mux_h - mux_f / 2.}}}, - {id_OF0, - {{cru_x + cru_w, grp_lut_y[1] - 5. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, - grp_lut_y[1] - 5. * right_wire_dist}, - {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[1] - 5. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, - mux2lut5_y[0] + mux_h / 2.}, - {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[0] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[0] + mux_h / 2.}}}, - {id_I1MUX1, - {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[0] + mux_h / 2., mux2lut6_x, mux2lut5_y[0] + mux_h / 2.}}}, - {id_OF1, - {{cru_x + cru_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, - grp_lut_y[1] - 4. * right_wire_dist}, - {mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, - mux2lut6_y[0] + mux_h / 2.}, - {mux2lut6_x + 4. / 3. * mux_w, mux2lut6_y[0] + mux_h / 2., mux2lut6_x + mux_w, mux2lut6_y[0] + mux_h / 2.}}}, - {id_I1MUX3, - {{mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[1] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, - mux2lut7_y + mux_h * 1. / 4.}, - {mux2lut6_x + 4. / 3. * mux_w, mux2lut7_y + mux_h * 1. / 4., mux2lut7_x, mux2lut7_y + mux_h * 1. / 4.}}}, - {id_OF2, - {{cru_x + cru_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, - grp_lut_y[1] - 3. * right_wire_dist}, - {mux2lut5_x + 5. / 3. * mux_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, - mux2lut5_y[1] + mux_h / 2.}, - {mux2lut5_x + 5. / 3. * mux_w, mux2lut5_y[1] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[1] + mux_h / 2.}}}, - {id_I0MUX1, - {{mux2lut5_x + 5. / 3. * mux_w, grp_lut_y[1] - 3. * right_wire_dist, mux2lut5_x + 5. / 3. * mux_w, - mux2lut6_y[0] + mux_h * 3. / 4.}, - {mux2lut5_x + 5. / 3. * mux_w, mux2lut6_y[0] + mux_h * 3. / 4., mux2lut6_x, - mux2lut6_y[0] + mux_h * 3. / 4.}}}, - {id_F2, - {{cru_x + cru_w, grp_lut_y[1] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[1] - 2. * right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[2] + lut_h / 2.}, - {lut_x + lut_w, lut_y[2] + lut_h / 2., dff_x, lut_y[2] + lut_h / 2.}}}, - {id_I0MUX2, - {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[1] - 2. * right_wire_dist, mux5i_x, - grp_lut_y[1] - 2. * right_wire_dist}, - {mux5i_x, grp_lut_y[1] - 2. * right_wire_dist, mux5i_x, lut_y[2] + lut_h / 2.}, - {mux5i_x, lut_y[2] + lut_h / 2., mux2lut5_x, lut_y[2] + lut_h / 2.}}}, - {id_Q2, - {{cru_x + cru_w, grp_lut_y[1] - right_wire_dist, dff_f_x, grp_lut_y[1] - right_wire_dist}, - {dff_f_x, grp_lut_y[1] - right_wire_dist, dff_f_x, lut_y[2] + lut_h / 2.}, - {dff_f_x, lut_y[2] + lut_h / 2., dff_x + dff_w, lut_y[2] + lut_h / 2.}}}, - // wires between LUT3 and LUT4 - {id_Q3, - {{cru_x + cru_w, grp_lut_y[2] - 9. * right_wire_dist, dff_f_x, grp_lut_y[2] - 9. * right_wire_dist}, - {dff_f_x, grp_lut_y[2] - 9. * right_wire_dist, dff_f_x, lut_y[3] + lut_h / 2.}, - {dff_f_x, lut_y[3] + lut_h / 2., dff_x + dff_w, lut_y[3] + lut_h / 2.}}}, - {id_F3, - {{cru_x + cru_w, grp_lut_y[2] - 8. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[2] - 8. * right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 8. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[3] + lut_h / 2.}, - {lut_x + lut_w, lut_y[3] + lut_h / 2., dff_x, lut_y[3] + lut_h / 2.}}}, - {id_I1MUX2, - {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 8. * right_wire_dist, mux5i_x, - grp_lut_y[2] - 8. * right_wire_dist}, - {mux5i_x, grp_lut_y[2] - 8. * right_wire_dist, mux5i_x, lut_y[3] + lut_h / 2.}, - {mux5i_x, lut_y[3] + lut_h / 2., mux2lut5_x, lut_y[3] + lut_h / 2.}}}, - {id_SEL2, - {{cru_x + cru_w, grp_lut_y[2] - 7. * right_wire_dist, mux2lut5_x + mux_w / 2., - grp_lut_y[2] - 7. * right_wire_dist}, - {mux2lut5_x + mux_w / 2., grp_lut_y[2] - 7. * right_wire_dist, mux2lut5_x + mux_w / 2., - mux2lut5_y[1] + mux_h - mux_f / 2.}}}, - {id_SEL3, - {{cru_x + cru_w, grp_lut_y[2] - 6. * right_wire_dist, mux2lut7_x + mux_w / 2., - grp_lut_y[2] - 6. * right_wire_dist}, - {mux2lut7_x + mux_w / 2., grp_lut_y[2] - 6. * right_wire_dist, mux2lut7_x + mux_w / 2., - mux2lut7_y + mux_h - mux_f / 2.}}}, - {id_SEL7, - {{cru_x + cru_w, grp_lut_y[2] - 5. * right_wire_dist, mux2lut8_x + mux_w / 2., - grp_lut_y[2] - 5. * right_wire_dist}, - {mux2lut8_x + mux_w / 2., grp_lut_y[2] - 5. * right_wire_dist, mux2lut8_x + mux_w / 2., - mux2lut8_y + mux_h - mux_f / 2.}}}, - {id_OF5, - {{cru_x + cru_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, - grp_lut_y[2] - 4. * right_wire_dist}, - {mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, - mux2lut6_y[1] + mux_h / 2.}, - {mux2lut6_x + 4. / 3. * mux_w, mux2lut6_y[1] + mux_h / 2., mux2lut6_x + mux_w, mux2lut6_y[1] + mux_h / 2.}}}, - {id_I0MUX3, - {{mux2lut6_x + 4. / 3. * mux_w, grp_lut_y[2] - 4. * right_wire_dist, mux2lut6_x + 4. / 3. * mux_w, - mux2lut7_y + mux_h * 3. / 4.}, - {mux2lut6_x + 4. / 3. * mux_w, mux2lut7_y + mux_h * 3. / 4., mux2lut7_x, mux2lut7_y + mux_h * 3. / 4.}}}, - {id_OF4, - {{cru_x + cru_w, grp_lut_y[2] - 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, - grp_lut_y[2] - 3. * right_wire_dist}, - {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[2] - 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, - mux2lut5_y[2] + mux_h / 2.}, - {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[2] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[2] + mux_h / 2.}}}, - {id_I1MUX5, - {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[2] + mux_h / 2., mux2lut6_x, mux2lut5_y[2] + mux_h / 2.}}}, - {id_F4, - {{cru_x + cru_w, grp_lut_y[2] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[2] - 2. * right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 2. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[4] + lut_h / 2.}, - {lut_x + lut_w, lut_y[4] + lut_h / 2., dff_x, lut_y[4] + lut_h / 2.}}}, - {id_I0MUX4, - {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[2] - 2. * right_wire_dist, mux5i_x, - grp_lut_y[2] - 2. * right_wire_dist}, - {mux5i_x, grp_lut_y[2] - 2. * right_wire_dist, mux5i_x, lut_y[4] + lut_h / 2.}, - {mux5i_x, lut_y[4] + lut_h / 2., mux2lut5_x, lut_y[4] + lut_h / 2.}}}, - {id_Q4, - {{cru_x + cru_w, grp_lut_y[2] - right_wire_dist, dff_f_x, grp_lut_y[2] - right_wire_dist}, - {dff_f_x, grp_lut_y[2] - right_wire_dist, dff_f_x, lut_y[4] + lut_h / 2.}, - {dff_f_x, lut_y[4] + lut_h / 2., dff_x + dff_w, lut_y[4] + lut_h / 2.}}}, - // wires between LUT5 and LUT6 - {id_Q5, - {{cru_x + cru_w, grp_lut_y[3] - 6. * right_wire_dist, dff_f_x, grp_lut_y[3] - 6. * right_wire_dist}, - {dff_f_x, grp_lut_y[3] - 6. * right_wire_dist, dff_f_x, lut_y[5] + lut_h / 2.}, - {dff_f_x, lut_y[5] + lut_h / 2., dff_x + dff_w, lut_y[5] + lut_h / 2.}}}, - {id_F5, - {{cru_x + cru_w, grp_lut_y[3] - 5. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[3] - 5. * right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - 5. * right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[5] + lut_h / 2.}, - {lut_x + lut_w, lut_y[5] + lut_h / 2., dff_x, lut_y[5] + lut_h / 2.}}}, - {id_I1MUX4, - {{(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - 5. * right_wire_dist, mux5i_x, - grp_lut_y[3] - 5. * right_wire_dist}, - {mux5i_x, grp_lut_y[3] - 5. * right_wire_dist, mux5i_x, lut_y[5] + lut_h / 2.}, - {mux5i_x, lut_y[5] + lut_h / 2., mux2lut5_x, lut_y[5] + lut_h / 2.}}}, - {id_SEL4, - {{cru_x + cru_w, grp_lut_y[3] - 4. * right_wire_dist, mux2lut5_x + mux_w / 2., - grp_lut_y[3] - 4. * right_wire_dist}, - {mux2lut5_x + mux_w / 2., grp_lut_y[3] - 4. * right_wire_dist, mux2lut5_x + mux_w / 2., - mux2lut5_y[2] + mux_h - mux_f / 2.}}}, - {id_SEL5, - {{cru_x + cru_w, grp_lut_y[3] - 2. * right_wire_dist, mux2lut6_x + mux_w / 2., - grp_lut_y[3] - 2. * right_wire_dist}, - {mux2lut6_x + mux_w / 2., grp_lut_y[3] - 2. * right_wire_dist, mux2lut6_x + mux_w / 2., - mux2lut6_y[1] + mux_h - mux_f / 2.}}}, - {id_F6, - {{cru_x + cru_w, grp_lut_y[3] - right_wire_dist, (lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] - right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[6] + lut_h / 2.}, - {lut_x + lut_w, lut_y[6] + lut_h / 2., (lut_x + lut_w + dff_x) / 2., lut_y[6] + lut_h / 2.}}}, - {id_I0MUX6, {{(lut_x + lut_w + dff_x) / 2., lut_y[6] + lut_h / 2., mux2lut5_x, lut_y[6] + lut_h / 2.}}}, - // wires above LUT7 - {id_F7, - {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + right_wire_dist, (lut_x + lut_w + dff_x) / 2., - grp_lut_y[3] + grp_lut_h + right_wire_dist}, - {(lut_x + lut_w + dff_x) / 2., grp_lut_y[3] + grp_lut_h + right_wire_dist, (lut_x + lut_w + dff_x) / 2., - lut_y[7] + lut_h / 2.}, - {lut_x + lut_w, lut_y[7] + lut_h / 2., (lut_x + lut_w + dff_x) / 2., lut_y[7] + lut_h / 2.}}}, - {id_I1MUX6, {{(lut_x + lut_w + dff_x) / 2., lut_y[7] + lut_h / 2., mux2lut5_x, lut_y[7] + lut_h / 2.}}}, - {id_SEL6, - {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist, mux2lut5_x + mux_w / 2., - grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist}, - {mux2lut5_x + mux_w / 2., grp_lut_y[3] + grp_lut_h + 2. * right_wire_dist, mux2lut5_x + mux_w / 2., - mux2lut5_y[3] + mux_h - mux_f / 2.}}}, - {id_OF6, - {{cru_x + cru_w, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, - grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist}, - {mux2lut5_x + 4. / 3. * mux_w, grp_lut_y[3] + grp_lut_h + 3. * right_wire_dist, mux2lut5_x + 4. / 3. * mux_w, - mux2lut5_y[3] + mux_h / 2.}, - {mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[3] + mux_h / 2., mux2lut5_x + mux_w, mux2lut5_y[3] + mux_h / 2.}}}, - {id_I0MUX5, - {{mux2lut5_x + 4. / 3. * mux_w, mux2lut5_y[3] + mux_h / 2., mux2lut5_x + 4. / 3. * mux_w, - mux2lut6_y[1] + mux_h * 3. / 4.}, - {mux2lut5_x + 4. / 3. * mux_w, mux2lut6_y[1] + mux_h * 3. / 4., mux2lut6_x, - mux2lut6_y[1] + mux_h * 3. / 4.}}}, -}; - -const dict>> globalSimpleWires = { - {id_I0MUX7, - {{mux2lut8_x, mux2lut8_y + mux_h / 4., mux2lut8_x - 1. / 3. * mux_w, mux2lut8_y + mux_h / 4.}, - {mux2lut8_x - 1. / 3. * mux_w, mux2lut8_y + mux_h / 4., mux2lut8_x - 1. / 3. * mux_w, - cru_y - 2. * right_wire_dist}, - {mux2lut8_x - 1. / 3. * mux_w, cru_y - 2. * right_wire_dist, 1. + mux2lut7_x + 4. / 3. * mux_w, - cru_y - 2. * right_wire_dist}, - {1. + mux2lut7_x + 4. / 3. * mux_w, cru_y - 2. * right_wire_dist, 1. + mux2lut7_x + 4. / 3. * mux_w, - grp_lut_y[0] - 3. * right_wire_dist}}}, -}; - -dict>> const globalWires = { -#define PIP_Y(pip_id) (pipPoint.at(pip_id).second) -#define WIRE_X(offset) (cru_x - ((float)offset) * sn_dist) - // 1 hop - {id_S10, - {{WIRE_X(0), PIP_Y(id_S100), WIRE_X(1), PIP_Y(id_S100)}, - {WIRE_X(1), PIP_Y(id_S100), WIRE_X(1), -1. + PIP_Y(id_S101)}, - {WIRE_X(1), -1. + PIP_Y(id_S101), WIRE_X(0), -1. + PIP_Y(id_S101)}}}, - {id_N10, - {{WIRE_X(0), PIP_Y(id_N100), WIRE_X(2), PIP_Y(id_N100)}, - {WIRE_X(2), PIP_Y(id_N100), WIRE_X(2), 1. + PIP_Y(id_N101)}, - {WIRE_X(2), 1. + PIP_Y(id_N101), WIRE_X(0), 1. + PIP_Y(id_N101)}}}, - {id_S10_loop0, - {{WIRE_X(0), PIP_Y(id_S100), WIRE_X(1), PIP_Y(id_S100)}, - {WIRE_X(1), PIP_Y(id_S100), WIRE_X(1), -1. * wrap_len}, - {WIRE_X(1), -1. * wrap_len, WIRE_X(2), -1. * wrap_len}, - {WIRE_X(2), -1. * wrap_len, WIRE_X(2), PIP_Y(id_N101)}, - {WIRE_X(2), PIP_Y(id_N101), WIRE_X(0), PIP_Y(id_N101)}}}, - {id_N10_loop0, - {{WIRE_X(0), PIP_Y(id_N100), WIRE_X(2), PIP_Y(id_N100)}, - {WIRE_X(2), PIP_Y(id_N100), WIRE_X(2), 1. + 1. * wrap_len}, - {WIRE_X(2), 1. + 1. * wrap_len, WIRE_X(1), 1. + 1. * wrap_len}, - {WIRE_X(1), 1. + 1. * wrap_len, WIRE_X(1), PIP_Y(id_S101)}, - {WIRE_X(1), PIP_Y(id_S101), WIRE_X(0), PIP_Y(id_S101)}}}, - {id_S13, - {{WIRE_X(0), PIP_Y(id_S130), WIRE_X(3), PIP_Y(id_S130)}, - {WIRE_X(3), PIP_Y(id_S130), WIRE_X(3), -1. + PIP_Y(id_S131)}, - {WIRE_X(3), -1. + PIP_Y(id_S131), WIRE_X(0), -1. + PIP_Y(id_S131)}}}, - {id_N13, - {{WIRE_X(0), PIP_Y(id_N130), WIRE_X(4), PIP_Y(id_N130)}, - {WIRE_X(4), PIP_Y(id_N130), WIRE_X(4), 1. + PIP_Y(id_N131)}, - {WIRE_X(4), 1. + PIP_Y(id_N131), WIRE_X(0), 1. + PIP_Y(id_N131)}}}, - {id_S13_loop0, - {{WIRE_X(0), PIP_Y(id_S130), WIRE_X(3), PIP_Y(id_S130)}, - {WIRE_X(3), PIP_Y(id_S130), WIRE_X(3), -1. * wrap_len}, - {WIRE_X(3), -1. * wrap_len, WIRE_X(4), -1. * wrap_len}, - {WIRE_X(4), -1. * wrap_len, WIRE_X(4), PIP_Y(id_N131)}, - {WIRE_X(4), PIP_Y(id_N131), WIRE_X(0), PIP_Y(id_N131)}}}, - {id_N13_loop0, - {{WIRE_X(0), PIP_Y(id_N130), WIRE_X(4), PIP_Y(id_N130)}, - {WIRE_X(4), PIP_Y(id_N130), WIRE_X(4), 1. + 1. * wrap_len}, - {WIRE_X(4), 1. + 1. * wrap_len, WIRE_X(3), 1. + 1. * wrap_len}, - {WIRE_X(3), 1. + 1. * wrap_len, WIRE_X(3), PIP_Y(id_S131)}, - {WIRE_X(3), PIP_Y(id_S131), WIRE_X(0), PIP_Y(id_S131)}}}, - // 1 hop SN - {id_SN10, - {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, - {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + PIP_Y(id_N111)}, - {WIRE_X(6), 1. + PIP_Y(id_N111), WIRE_X(0), 1. + PIP_Y(id_N111)}, - {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. + PIP_Y(id_S111)}, - {WIRE_X(5), -1. + PIP_Y(id_S111), WIRE_X(0), -1. + PIP_Y(id_S111)}}}, - {id_SN10_loop_n, - {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, - {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + 1. * wrap_len}, - {WIRE_X(6), 1. + 1. * wrap_len, WIRE_X(5), 1. + 1. * wrap_len}, - {WIRE_X(5), 1. + 1. * wrap_len, WIRE_X(5), PIP_Y(id_SN10)}, - {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. + PIP_Y(id_S111)}, - {WIRE_X(5), -1. + PIP_Y(id_S111), WIRE_X(0), -1. + PIP_Y(id_S111)}}}, - {id_SN10_loop_s, - {{WIRE_X(0), PIP_Y(id_SN10), WIRE_X(6), PIP_Y(id_SN10)}, - {WIRE_X(6), PIP_Y(id_SN10), WIRE_X(6), 1. + PIP_Y(id_N111)}, - {WIRE_X(6), 1. + PIP_Y(id_N111), WIRE_X(0), 1. + PIP_Y(id_N111)}, - {WIRE_X(5), PIP_Y(id_SN10), WIRE_X(5), -1. * wrap_len}, - {WIRE_X(5), -1. * wrap_len, WIRE_X(6), -1. * wrap_len}, - {WIRE_X(6), -1. * wrap_len, WIRE_X(6), PIP_Y(id_N111)}, - {WIRE_X(6), PIP_Y(id_N111), WIRE_X(0), PIP_Y(id_N111)}}}, - {id_SN20, - {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, - {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + PIP_Y(id_N121)}, - {WIRE_X(8), 1. + PIP_Y(id_N121), WIRE_X(0), 1. + PIP_Y(id_N121)}, - {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. + PIP_Y(id_S121)}, - {WIRE_X(7), -1. + PIP_Y(id_S121), WIRE_X(0), -1. + PIP_Y(id_S121)}}}, - {id_SN20_loop_n, - {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, - {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + 1. * wrap_len}, - {WIRE_X(8), 1. + 1. * wrap_len, WIRE_X(7), 1. + 1. * wrap_len}, - {WIRE_X(7), 1. + 1. * wrap_len, WIRE_X(7), PIP_Y(id_SN10)}, - {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. + PIP_Y(id_S121)}, - {WIRE_X(7), -1. + PIP_Y(id_S121), WIRE_X(0), -1. + PIP_Y(id_S121)}}}, - {id_SN20_loop_s, - {{WIRE_X(0), PIP_Y(id_SN20), WIRE_X(8), PIP_Y(id_SN20)}, - {WIRE_X(8), PIP_Y(id_SN20), WIRE_X(8), 1. + PIP_Y(id_N121)}, - {WIRE_X(8), 1. + PIP_Y(id_N121), WIRE_X(0), 1. + PIP_Y(id_N121)}, - {WIRE_X(7), PIP_Y(id_SN20), WIRE_X(7), -1. * wrap_len}, - {WIRE_X(7), -1. * wrap_len, WIRE_X(8), -1. * wrap_len}, - {WIRE_X(8), -1. * wrap_len, WIRE_X(8), PIP_Y(id_N121)}, - {WIRE_X(8), PIP_Y(id_N121), WIRE_X(0), PIP_Y(id_N121)}}}, - // 2 hop - {id_S20, - {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, - {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. + PIP_Y(id_S201)}, - {WIRE_X(11), -1. + PIP_Y(id_S201), WIRE_X(0), -1. + PIP_Y(id_S201)}, - {WIRE_X(9), -1. + PIP_Y(id_S201), WIRE_X(9), -2. + PIP_Y(id_S202)}, - {WIRE_X(9), -2. + PIP_Y(id_S202), WIRE_X(0), -2. + PIP_Y(id_S202)}}}, - {id_N20, - {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, - {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + PIP_Y(id_N201)}, - {WIRE_X(12), 1. + PIP_Y(id_N201), WIRE_X(0), 1. + PIP_Y(id_N201)}, - {WIRE_X(10), 1. + PIP_Y(id_N201), WIRE_X(10), 2. + PIP_Y(id_N202)}, - {WIRE_X(10), 2. + PIP_Y(id_N202), WIRE_X(0), 2. + PIP_Y(id_N202)}}}, - {id_S20_loop0, - {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, - {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. * wrap_len}, - {WIRE_X(11), -1. * wrap_len, WIRE_X(12), -1. * wrap_len}, - {WIRE_X(12), -1. * wrap_len, WIRE_X(12), PIP_Y(id_N201)}, - {WIRE_X(12), PIP_Y(id_N201), WIRE_X(0), PIP_Y(id_N201)}, - {WIRE_X(10), PIP_Y(id_N201), WIRE_X(10), 1. + PIP_Y(id_N202)}, - {WIRE_X(10), 1. + PIP_Y(id_N202), WIRE_X(0), 1. + PIP_Y(id_N202)}}}, - {id_N20_loop0, - {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, - {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + 1. * wrap_len}, - {WIRE_X(12), 1. + 1. * wrap_len, WIRE_X(11), 1. + 1. * wrap_len}, - {WIRE_X(11), 1. + 1. * wrap_len, WIRE_X(11), PIP_Y(id_S201)}, - {WIRE_X(11), PIP_Y(id_S201), WIRE_X(0), PIP_Y(id_S201)}, - {WIRE_X(9), PIP_Y(id_S201), WIRE_X(9), -1. + PIP_Y(id_S202)}, - {WIRE_X(9), -1. + PIP_Y(id_S202), WIRE_X(0), -1. + PIP_Y(id_S202)}}}, - {id_S20_loop1, - {{WIRE_X(0), PIP_Y(id_S200), WIRE_X(11), PIP_Y(id_S200)}, - {WIRE_X(11), PIP_Y(id_S200), WIRE_X(11), -1. + PIP_Y(id_S201)}, - {WIRE_X(11), -1. + PIP_Y(id_S201), WIRE_X(0), -1. + PIP_Y(id_S201)}, - {WIRE_X(9), -1. + PIP_Y(id_S201), WIRE_X(9), -1. + -1. * wrap_len}, - {WIRE_X(9), -1. + -1. * wrap_len, WIRE_X(10), -1. + -1. * wrap_len}, - {WIRE_X(10), -1. + -1. * wrap_len, WIRE_X(10), -1. + PIP_Y(id_N202)}, - {WIRE_X(10), -1. + PIP_Y(id_N202), WIRE_X(0), -1. + PIP_Y(id_N202)}}}, - {id_N20_loop1, - {{WIRE_X(0), PIP_Y(id_N200), WIRE_X(12), PIP_Y(id_N200)}, - {WIRE_X(12), PIP_Y(id_N200), WIRE_X(12), 1. + PIP_Y(id_N201)}, - {WIRE_X(12), 1. + PIP_Y(id_N201), WIRE_X(0), 1. + PIP_Y(id_N201)}, - {WIRE_X(10), 1. + PIP_Y(id_N201), WIRE_X(10), 2. + 1. * wrap_len}, - {WIRE_X(10), 2. + 1. * wrap_len, WIRE_X(9), 2. + 1. * wrap_len}, - {WIRE_X(9), 2. + 1. * wrap_len, WIRE_X(9), 1. + PIP_Y(id_S202)}, - {WIRE_X(9), 1. + PIP_Y(id_S202), WIRE_X(0), 1. + PIP_Y(id_S202)}}}, - {id_S21, - {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, - {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. + PIP_Y(id_S211)}, - {WIRE_X(15), -1. + PIP_Y(id_S211), WIRE_X(0), -1. + PIP_Y(id_S211)}, - {WIRE_X(13), -1. + PIP_Y(id_S211), WIRE_X(13), -2. + PIP_Y(id_S212)}, - {WIRE_X(13), -2. + PIP_Y(id_S212), WIRE_X(0), -2. + PIP_Y(id_S212)}}}, - {id_N21, - {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, - {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + PIP_Y(id_N211)}, - {WIRE_X(16), 1. + PIP_Y(id_N211), WIRE_X(0), 1. + PIP_Y(id_N211)}, - {WIRE_X(14), 1. + PIP_Y(id_N211), WIRE_X(14), 2. + PIP_Y(id_N212)}, - {WIRE_X(14), 2. + PIP_Y(id_N212), WIRE_X(0), 2. + PIP_Y(id_N212)}}}, - {id_S21_loop0, - {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, - {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. * wrap_len}, - {WIRE_X(15), -1. * wrap_len, WIRE_X(16), -1. * wrap_len}, - {WIRE_X(16), -1. * wrap_len, WIRE_X(16), PIP_Y(id_N211)}, - {WIRE_X(16), PIP_Y(id_N211), WIRE_X(0), PIP_Y(id_N211)}, - {WIRE_X(14), PIP_Y(id_N211), WIRE_X(14), 1. + PIP_Y(id_N212)}, - {WIRE_X(14), 1. + PIP_Y(id_N212), WIRE_X(0), 1. + PIP_Y(id_N212)}}}, - {id_N21_loop0, - {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, - {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + 1. * wrap_len}, - {WIRE_X(16), 1. + 1. * wrap_len, WIRE_X(15), 1. + 1. * wrap_len}, - {WIRE_X(15), 1. + 1. * wrap_len, WIRE_X(15), PIP_Y(id_S211)}, - {WIRE_X(15), PIP_Y(id_S211), WIRE_X(0), PIP_Y(id_S211)}, - {WIRE_X(13), PIP_Y(id_S211), WIRE_X(13), -1. + PIP_Y(id_S212)}, - {WIRE_X(13), -1. + PIP_Y(id_S212), WIRE_X(0), -1. + PIP_Y(id_S212)}}}, - {id_S21_loop1, - {{WIRE_X(0), PIP_Y(id_S210), WIRE_X(15), PIP_Y(id_S210)}, - {WIRE_X(15), PIP_Y(id_S210), WIRE_X(15), -1. + PIP_Y(id_S211)}, - {WIRE_X(15), -1. + PIP_Y(id_S211), WIRE_X(0), -1. + PIP_Y(id_S211)}, - {WIRE_X(13), -1. + PIP_Y(id_S211), WIRE_X(13), -1. + -1. * wrap_len}, - {WIRE_X(13), -1. + -1. * wrap_len, WIRE_X(14), -1. + -1. * wrap_len}, - {WIRE_X(14), -1. + -1. * wrap_len, WIRE_X(14), -1. + PIP_Y(id_N212)}, - {WIRE_X(14), -1. + PIP_Y(id_N212), WIRE_X(0), -1. + PIP_Y(id_N212)}}}, - {id_N21_loop1, - {{WIRE_X(0), PIP_Y(id_N210), WIRE_X(16), PIP_Y(id_N210)}, - {WIRE_X(16), PIP_Y(id_N210), WIRE_X(16), 1. + PIP_Y(id_N211)}, - {WIRE_X(16), 1. + PIP_Y(id_N211), WIRE_X(0), 1. + PIP_Y(id_N211)}, - {WIRE_X(14), 1. + PIP_Y(id_N211), WIRE_X(14), 2. + 1. * wrap_len}, - {WIRE_X(14), 2. + 1. * wrap_len, WIRE_X(13), 2. + 1. * wrap_len}, - {WIRE_X(13), 2. + 1. * wrap_len, WIRE_X(13), 1. + PIP_Y(id_S212)}, - {WIRE_X(13), 1. + PIP_Y(id_S212), WIRE_X(0), 1. + PIP_Y(id_S212)}}}, - {id_S22, - {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, - {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. + PIP_Y(id_S221)}, - {WIRE_X(19), -1. + PIP_Y(id_S221), WIRE_X(0), -1. + PIP_Y(id_S221)}, - {WIRE_X(17), -1. + PIP_Y(id_S221), WIRE_X(17), -2. + PIP_Y(id_S222)}, - {WIRE_X(17), -2. + PIP_Y(id_S222), WIRE_X(0), -2. + PIP_Y(id_S222)}}}, - {id_N22, - {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, - {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + PIP_Y(id_N221)}, - {WIRE_X(20), 1. + PIP_Y(id_N221), WIRE_X(0), 1. + PIP_Y(id_N221)}, - {WIRE_X(18), 1. + PIP_Y(id_N221), WIRE_X(18), 2. + PIP_Y(id_N222)}, - {WIRE_X(18), 2. + PIP_Y(id_N222), WIRE_X(0), 2. + PIP_Y(id_N222)}}}, - {id_S22_loop0, - {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, - {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. * wrap_len}, - {WIRE_X(19), -1. * wrap_len, WIRE_X(20), -1. * wrap_len}, - {WIRE_X(20), -1. * wrap_len, WIRE_X(20), PIP_Y(id_N221)}, - {WIRE_X(20), PIP_Y(id_N221), WIRE_X(0), PIP_Y(id_N221)}, - {WIRE_X(18), PIP_Y(id_N221), WIRE_X(18), 1. + PIP_Y(id_N222)}, - {WIRE_X(18), 1. + PIP_Y(id_N222), WIRE_X(0), 1. + PIP_Y(id_N222)}}}, - {id_N22_loop0, - {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, - {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + 1. * wrap_len}, - {WIRE_X(20), 1. + 1. * wrap_len, WIRE_X(19), 1. + 1. * wrap_len}, - {WIRE_X(19), 1. + 1. * wrap_len, WIRE_X(19), PIP_Y(id_S221)}, - {WIRE_X(19), PIP_Y(id_S221), WIRE_X(0), PIP_Y(id_S221)}, - {WIRE_X(17), PIP_Y(id_S221), WIRE_X(17), -1. + PIP_Y(id_S222)}, - {WIRE_X(17), -1. + PIP_Y(id_S222), WIRE_X(0), -1. + PIP_Y(id_S222)}}}, - {id_S22_loop1, - {{WIRE_X(0), PIP_Y(id_S220), WIRE_X(19), PIP_Y(id_S220)}, - {WIRE_X(19), PIP_Y(id_S220), WIRE_X(19), -1. + PIP_Y(id_S221)}, - {WIRE_X(19), -1. + PIP_Y(id_S221), WIRE_X(0), -1. + PIP_Y(id_S221)}, - {WIRE_X(17), -1. + PIP_Y(id_S221), WIRE_X(17), -1. + -1. * wrap_len}, - {WIRE_X(17), -1. + -1. * wrap_len, WIRE_X(18), -1. + -1. * wrap_len}, - {WIRE_X(18), -1. + -1. * wrap_len, WIRE_X(18), -1. + PIP_Y(id_N222)}, - {WIRE_X(18), -1. + PIP_Y(id_N222), WIRE_X(0), -1. + PIP_Y(id_N222)}}}, - {id_N22_loop1, - {{WIRE_X(0), PIP_Y(id_N220), WIRE_X(20), PIP_Y(id_N220)}, - {WIRE_X(20), PIP_Y(id_N220), WIRE_X(20), 1. + PIP_Y(id_N221)}, - {WIRE_X(20), 1. + PIP_Y(id_N221), WIRE_X(0), 1. + PIP_Y(id_N221)}, - {WIRE_X(18), 1. + PIP_Y(id_N221), WIRE_X(18), 2. + 1. * wrap_len}, - {WIRE_X(18), 2. + 1. * wrap_len, WIRE_X(17), 2. + 1. * wrap_len}, - {WIRE_X(17), 2. + 1. * wrap_len, WIRE_X(17), 1. + PIP_Y(id_S222)}, - {WIRE_X(17), 1. + PIP_Y(id_S222), WIRE_X(0), 1. + PIP_Y(id_S222)}}}, - {id_S23, - {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, - {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. + PIP_Y(id_S231)}, - {WIRE_X(23), -1. + PIP_Y(id_S231), WIRE_X(0), -1. + PIP_Y(id_S231)}, - {WIRE_X(21), -1. + PIP_Y(id_S231), WIRE_X(21), -2. + PIP_Y(id_S232)}, - {WIRE_X(21), -2. + PIP_Y(id_S232), WIRE_X(0), -2. + PIP_Y(id_S232)}}}, - {id_N23, - {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, - {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + PIP_Y(id_N231)}, - {WIRE_X(24), 1. + PIP_Y(id_N231), WIRE_X(0), 1. + PIP_Y(id_N231)}, - {WIRE_X(22), 1. + PIP_Y(id_N231), WIRE_X(22), 2. + PIP_Y(id_N232)}, - {WIRE_X(22), 2. + PIP_Y(id_N232), WIRE_X(0), 2. + PIP_Y(id_N232)}}}, - {id_S23_loop0, - {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, - {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. * wrap_len}, - {WIRE_X(23), -1. * wrap_len, WIRE_X(24), -1. * wrap_len}, - {WIRE_X(24), -1. * wrap_len, WIRE_X(24), PIP_Y(id_N231)}, - {WIRE_X(24), PIP_Y(id_N231), WIRE_X(0), PIP_Y(id_N231)}, - {WIRE_X(22), PIP_Y(id_N231), WIRE_X(22), 1. + PIP_Y(id_N232)}, - {WIRE_X(22), 1. + PIP_Y(id_N232), WIRE_X(0), 1. + PIP_Y(id_N232)}}}, - {id_N23_loop0, - {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, - {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + 1. * wrap_len}, - {WIRE_X(24), 1. + 1. * wrap_len, WIRE_X(23), 1. + 1. * wrap_len}, - {WIRE_X(23), 1. + 1. * wrap_len, WIRE_X(23), PIP_Y(id_S231)}, - {WIRE_X(23), PIP_Y(id_S231), WIRE_X(0), PIP_Y(id_S231)}, - {WIRE_X(21), PIP_Y(id_S231), WIRE_X(21), -1. + PIP_Y(id_S232)}, - {WIRE_X(21), -1. + PIP_Y(id_S232), WIRE_X(0), -1. + PIP_Y(id_S232)}}}, - {id_S23_loop1, - {{WIRE_X(0), PIP_Y(id_S230), WIRE_X(23), PIP_Y(id_S230)}, - {WIRE_X(23), PIP_Y(id_S230), WIRE_X(23), -1. + PIP_Y(id_S231)}, - {WIRE_X(23), -1. + PIP_Y(id_S231), WIRE_X(0), -1. + PIP_Y(id_S231)}, - {WIRE_X(21), -1. + PIP_Y(id_S231), WIRE_X(21), -1. + -1. * wrap_len}, - {WIRE_X(21), -1. + -1. * wrap_len, WIRE_X(22), -1. + -1. * wrap_len}, - {WIRE_X(22), -1. + -1. * wrap_len, WIRE_X(22), -1. + PIP_Y(id_N232)}, - {WIRE_X(22), -1. + PIP_Y(id_N232), WIRE_X(0), -1. + PIP_Y(id_N232)}}}, - {id_N23_loop1, - {{WIRE_X(0), PIP_Y(id_N230), WIRE_X(24), PIP_Y(id_N230)}, - {WIRE_X(24), PIP_Y(id_N230), WIRE_X(24), 1. + PIP_Y(id_N231)}, - {WIRE_X(24), 1. + PIP_Y(id_N231), WIRE_X(0), 1. + PIP_Y(id_N231)}, - {WIRE_X(22), 1. + PIP_Y(id_N231), WIRE_X(22), 2. + 1. * wrap_len}, - {WIRE_X(22), 2. + 1. * wrap_len, WIRE_X(21), 2. + 1. * wrap_len}, - {WIRE_X(21), 2. + 1. * wrap_len, WIRE_X(21), 1. + PIP_Y(id_S232)}, - {WIRE_X(21), 1. + PIP_Y(id_S232), WIRE_X(0), 1. + PIP_Y(id_S232)}}}, - {id_S24, - {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, - {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. + PIP_Y(id_S241)}, - {WIRE_X(27), -1. + PIP_Y(id_S241), WIRE_X(0), -1. + PIP_Y(id_S241)}, - {WIRE_X(25), -1. + PIP_Y(id_S241), WIRE_X(25), -2. + PIP_Y(id_S242)}, - {WIRE_X(25), -2. + PIP_Y(id_S242), WIRE_X(0), -2. + PIP_Y(id_S242)}}}, - {id_N24, - {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, - {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + PIP_Y(id_N241)}, - {WIRE_X(28), 1. + PIP_Y(id_N241), WIRE_X(0), 1. + PIP_Y(id_N241)}, - {WIRE_X(26), 1. + PIP_Y(id_N241), WIRE_X(26), 2. + PIP_Y(id_N242)}, - {WIRE_X(26), 2. + PIP_Y(id_N242), WIRE_X(0), 2. + PIP_Y(id_N242)}}}, - {id_S24_loop0, - {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, - {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. * wrap_len}, - {WIRE_X(27), -1. * wrap_len, WIRE_X(28), -1. * wrap_len}, - {WIRE_X(28), -1. * wrap_len, WIRE_X(28), PIP_Y(id_N241)}, - {WIRE_X(28), PIP_Y(id_N241), WIRE_X(0), PIP_Y(id_N241)}, - {WIRE_X(26), PIP_Y(id_N241), WIRE_X(26), 1. + PIP_Y(id_N242)}, - {WIRE_X(26), 1. + PIP_Y(id_N242), WIRE_X(0), 1. + PIP_Y(id_N242)}}}, - {id_N24_loop0, - {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, - {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + 1. * wrap_len}, - {WIRE_X(28), 1. + 1. * wrap_len, WIRE_X(27), 1. + 1. * wrap_len}, - {WIRE_X(27), 1. + 1. * wrap_len, WIRE_X(27), PIP_Y(id_S241)}, - {WIRE_X(27), PIP_Y(id_S241), WIRE_X(0), PIP_Y(id_S241)}, - {WIRE_X(25), PIP_Y(id_S241), WIRE_X(25), -1. + PIP_Y(id_S242)}, - {WIRE_X(25), -1. + PIP_Y(id_S242), WIRE_X(0), -1. + PIP_Y(id_S242)}}}, - {id_S24_loop1, - {{WIRE_X(0), PIP_Y(id_S240), WIRE_X(27), PIP_Y(id_S240)}, - {WIRE_X(27), PIP_Y(id_S240), WIRE_X(27), -1. + PIP_Y(id_S241)}, - {WIRE_X(27), -1. + PIP_Y(id_S241), WIRE_X(0), -1. + PIP_Y(id_S241)}, - {WIRE_X(25), -1. + PIP_Y(id_S241), WIRE_X(25), -1. + -1. * wrap_len}, - {WIRE_X(25), -1. + -1. * wrap_len, WIRE_X(26), -1. + -1. * wrap_len}, - {WIRE_X(26), -1. + -1. * wrap_len, WIRE_X(26), -1. + PIP_Y(id_N242)}, - {WIRE_X(26), -1. + PIP_Y(id_N242), WIRE_X(0), -1. + PIP_Y(id_N242)}}}, - {id_N24_loop1, - {{WIRE_X(0), PIP_Y(id_N240), WIRE_X(28), PIP_Y(id_N240)}, - {WIRE_X(28), PIP_Y(id_N240), WIRE_X(28), 1. + PIP_Y(id_N241)}, - {WIRE_X(28), 1. + PIP_Y(id_N241), WIRE_X(0), 1. + PIP_Y(id_N241)}, - {WIRE_X(26), 1. + PIP_Y(id_N241), WIRE_X(26), 2. + 1. * wrap_len}, - {WIRE_X(26), 2. + 1. * wrap_len, WIRE_X(25), 2. + 1. * wrap_len}, - {WIRE_X(25), 2. + 1. * wrap_len, WIRE_X(25), 1. + PIP_Y(id_S242)}, - {WIRE_X(25), 1. + PIP_Y(id_S242), WIRE_X(0), 1. + PIP_Y(id_S242)}}}, - {id_S25, - {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, - {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. + PIP_Y(id_S251)}, - {WIRE_X(31), -1. + PIP_Y(id_S251), WIRE_X(0), -1. + PIP_Y(id_S251)}, - {WIRE_X(29), -1. + PIP_Y(id_S251), WIRE_X(29), -2. + PIP_Y(id_S252)}, - {WIRE_X(29), -2. + PIP_Y(id_S252), WIRE_X(0), -2. + PIP_Y(id_S252)}}}, - {id_N25, - {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, - {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + PIP_Y(id_N251)}, - {WIRE_X(32), 1. + PIP_Y(id_N251), WIRE_X(0), 1. + PIP_Y(id_N251)}, - {WIRE_X(30), 1. + PIP_Y(id_N251), WIRE_X(30), 2. + PIP_Y(id_N252)}, - {WIRE_X(30), 2. + PIP_Y(id_N252), WIRE_X(0), 2. + PIP_Y(id_N252)}}}, - {id_S25_loop0, - {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, - {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. * wrap_len}, - {WIRE_X(31), -1. * wrap_len, WIRE_X(32), -1. * wrap_len}, - {WIRE_X(32), -1. * wrap_len, WIRE_X(32), PIP_Y(id_N251)}, - {WIRE_X(32), PIP_Y(id_N251), WIRE_X(0), PIP_Y(id_N251)}, - {WIRE_X(30), PIP_Y(id_N251), WIRE_X(30), 1. + PIP_Y(id_N252)}, - {WIRE_X(30), 1. + PIP_Y(id_N252), WIRE_X(0), 1. + PIP_Y(id_N252)}}}, - {id_N25_loop0, - {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, - {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + 1. * wrap_len}, - {WIRE_X(32), 1. + 1. * wrap_len, WIRE_X(31), 1. + 1. * wrap_len}, - {WIRE_X(31), 1. + 1. * wrap_len, WIRE_X(31), PIP_Y(id_S251)}, - {WIRE_X(31), PIP_Y(id_S251), WIRE_X(0), PIP_Y(id_S251)}, - {WIRE_X(29), PIP_Y(id_S251), WIRE_X(29), -1. + PIP_Y(id_S252)}, - {WIRE_X(29), -1. + PIP_Y(id_S252), WIRE_X(0), -1. + PIP_Y(id_S252)}}}, - {id_S25_loop1, - {{WIRE_X(0), PIP_Y(id_S250), WIRE_X(31), PIP_Y(id_S250)}, - {WIRE_X(31), PIP_Y(id_S250), WIRE_X(31), -1. + PIP_Y(id_S251)}, - {WIRE_X(31), -1. + PIP_Y(id_S251), WIRE_X(0), -1. + PIP_Y(id_S251)}, - {WIRE_X(29), -1. + PIP_Y(id_S251), WIRE_X(29), -1. + -1. * wrap_len}, - {WIRE_X(29), -1. + -1. * wrap_len, WIRE_X(30), -1. + -1. * wrap_len}, - {WIRE_X(30), -1. + -1. * wrap_len, WIRE_X(30), -1. + PIP_Y(id_N252)}, - {WIRE_X(30), -1. + PIP_Y(id_N252), WIRE_X(0), -1. + PIP_Y(id_N252)}}}, - {id_N25_loop1, - {{WIRE_X(0), PIP_Y(id_N250), WIRE_X(32), PIP_Y(id_N250)}, - {WIRE_X(32), PIP_Y(id_N250), WIRE_X(32), 1. + PIP_Y(id_N251)}, - {WIRE_X(32), 1. + PIP_Y(id_N251), WIRE_X(0), 1. + PIP_Y(id_N251)}, - {WIRE_X(30), 1. + PIP_Y(id_N251), WIRE_X(30), 2. + 1. * wrap_len}, - {WIRE_X(30), 2. + 1. * wrap_len, WIRE_X(29), 2. + 1. * wrap_len}, - {WIRE_X(29), 2. + 1. * wrap_len, WIRE_X(29), 1. + PIP_Y(id_S252)}, - {WIRE_X(29), 1. + PIP_Y(id_S252), WIRE_X(0), 1. + PIP_Y(id_S252)}}}, - {id_S26, - {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, - {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. + PIP_Y(id_S261)}, - {WIRE_X(35), -1. + PIP_Y(id_S261), WIRE_X(0), -1. + PIP_Y(id_S261)}, - {WIRE_X(33), -1. + PIP_Y(id_S261), WIRE_X(33), -2. + PIP_Y(id_S262)}, - {WIRE_X(33), -2. + PIP_Y(id_S262), WIRE_X(0), -2. + PIP_Y(id_S262)}}}, - {id_N26, - {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, - {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + PIP_Y(id_N261)}, - {WIRE_X(36), 1. + PIP_Y(id_N261), WIRE_X(0), 1. + PIP_Y(id_N261)}, - {WIRE_X(34), 1. + PIP_Y(id_N261), WIRE_X(34), 2. + PIP_Y(id_N262)}, - {WIRE_X(34), 2. + PIP_Y(id_N262), WIRE_X(0), 2. + PIP_Y(id_N262)}}}, - {id_S26_loop0, - {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, - {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. * wrap_len}, - {WIRE_X(35), -1. * wrap_len, WIRE_X(36), -1. * wrap_len}, - {WIRE_X(36), -1. * wrap_len, WIRE_X(36), PIP_Y(id_N261)}, - {WIRE_X(36), PIP_Y(id_N261), WIRE_X(0), PIP_Y(id_N261)}, - {WIRE_X(34), PIP_Y(id_N261), WIRE_X(34), 1. + PIP_Y(id_N262)}, - {WIRE_X(34), 1. + PIP_Y(id_N262), WIRE_X(0), 1. + PIP_Y(id_N262)}}}, - {id_N26_loop0, - {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, - {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + 1. * wrap_len}, - {WIRE_X(36), 1. + 1. * wrap_len, WIRE_X(35), 1. + 1. * wrap_len}, - {WIRE_X(35), 1. + 1. * wrap_len, WIRE_X(35), PIP_Y(id_S261)}, - {WIRE_X(35), PIP_Y(id_S261), WIRE_X(0), PIP_Y(id_S261)}, - {WIRE_X(33), PIP_Y(id_S261), WIRE_X(33), -1. + PIP_Y(id_S262)}, - {WIRE_X(33), -1. + PIP_Y(id_S262), WIRE_X(0), -1. + PIP_Y(id_S262)}}}, - {id_S26_loop1, - {{WIRE_X(0), PIP_Y(id_S260), WIRE_X(35), PIP_Y(id_S260)}, - {WIRE_X(35), PIP_Y(id_S260), WIRE_X(35), -1. + PIP_Y(id_S261)}, - {WIRE_X(35), -1. + PIP_Y(id_S261), WIRE_X(0), -1. + PIP_Y(id_S261)}, - {WIRE_X(33), -1. + PIP_Y(id_S261), WIRE_X(33), -1. + -1. * wrap_len}, - {WIRE_X(33), -1. + -1. * wrap_len, WIRE_X(34), -1. + -1. * wrap_len}, - {WIRE_X(34), -1. + -1. * wrap_len, WIRE_X(34), -1. + PIP_Y(id_N262)}, - {WIRE_X(34), -1. + PIP_Y(id_N262), WIRE_X(0), -1. + PIP_Y(id_N262)}}}, - {id_N26_loop1, - {{WIRE_X(0), PIP_Y(id_N260), WIRE_X(36), PIP_Y(id_N260)}, - {WIRE_X(36), PIP_Y(id_N260), WIRE_X(36), 1. + PIP_Y(id_N261)}, - {WIRE_X(36), 1. + PIP_Y(id_N261), WIRE_X(0), 1. + PIP_Y(id_N261)}, - {WIRE_X(34), 1. + PIP_Y(id_N261), WIRE_X(34), 2. + 1. * wrap_len}, - {WIRE_X(34), 2. + 1. * wrap_len, WIRE_X(33), 2. + 1. * wrap_len}, - {WIRE_X(33), 2. + 1. * wrap_len, WIRE_X(33), 1. + PIP_Y(id_S262)}, - {WIRE_X(33), 1. + PIP_Y(id_S262), WIRE_X(0), 1. + PIP_Y(id_S262)}}}, - {id_S27, - {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, - {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. + PIP_Y(id_S271)}, - {WIRE_X(39), -1. + PIP_Y(id_S271), WIRE_X(0), -1. + PIP_Y(id_S271)}, - {WIRE_X(37), -1. + PIP_Y(id_S271), WIRE_X(37), -2. + PIP_Y(id_S272)}, - {WIRE_X(37), -2. + PIP_Y(id_S272), WIRE_X(0), -2. + PIP_Y(id_S272)}}}, - {id_N27, - {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, - {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + PIP_Y(id_N271)}, - {WIRE_X(40), 1. + PIP_Y(id_N271), WIRE_X(0), 1. + PIP_Y(id_N271)}, - {WIRE_X(38), 1. + PIP_Y(id_N271), WIRE_X(38), 2. + PIP_Y(id_N272)}, - {WIRE_X(38), 2. + PIP_Y(id_N272), WIRE_X(0), 2. + PIP_Y(id_N272)}}}, - {id_S27_loop0, - {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, - {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. * wrap_len}, - {WIRE_X(39), -1. * wrap_len, WIRE_X(40), -1. * wrap_len}, - {WIRE_X(40), -1. * wrap_len, WIRE_X(40), PIP_Y(id_N271)}, - {WIRE_X(40), PIP_Y(id_N271), WIRE_X(0), PIP_Y(id_N271)}, - {WIRE_X(38), PIP_Y(id_N271), WIRE_X(38), 1. + PIP_Y(id_N272)}, - {WIRE_X(38), 1. + PIP_Y(id_N272), WIRE_X(0), 1. + PIP_Y(id_N272)}}}, - {id_N27_loop0, - {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, - {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + 1. * wrap_len}, - {WIRE_X(40), 1. + 1. * wrap_len, WIRE_X(39), 1. + 1. * wrap_len}, - {WIRE_X(39), 1. + 1. * wrap_len, WIRE_X(39), PIP_Y(id_S271)}, - {WIRE_X(39), PIP_Y(id_S271), WIRE_X(0), PIP_Y(id_S271)}, - {WIRE_X(37), PIP_Y(id_S271), WIRE_X(37), -1. + PIP_Y(id_S272)}, - {WIRE_X(37), -1. + PIP_Y(id_S272), WIRE_X(0), -1. + PIP_Y(id_S272)}}}, - {id_S27_loop1, - {{WIRE_X(0), PIP_Y(id_S270), WIRE_X(39), PIP_Y(id_S270)}, - {WIRE_X(39), PIP_Y(id_S270), WIRE_X(39), -1. + PIP_Y(id_S271)}, - {WIRE_X(39), -1. + PIP_Y(id_S271), WIRE_X(0), -1. + PIP_Y(id_S271)}, - {WIRE_X(37), -1. + PIP_Y(id_S271), WIRE_X(37), -1. + -1. * wrap_len}, - {WIRE_X(37), -1. + -1. * wrap_len, WIRE_X(38), -1. + -1. * wrap_len}, - {WIRE_X(38), -1. + -1. * wrap_len, WIRE_X(38), -1. + PIP_Y(id_N272)}, - {WIRE_X(38), -1. + PIP_Y(id_N272), WIRE_X(0), -1. + PIP_Y(id_N272)}}}, - {id_N27_loop1, - {{WIRE_X(0), PIP_Y(id_N270), WIRE_X(40), PIP_Y(id_N270)}, - {WIRE_X(40), PIP_Y(id_N270), WIRE_X(40), 1. + PIP_Y(id_N271)}, - {WIRE_X(40), 1. + PIP_Y(id_N271), WIRE_X(0), 1. + PIP_Y(id_N271)}, - {WIRE_X(38), 1. + PIP_Y(id_N271), WIRE_X(38), 2. + 1. * wrap_len}, - {WIRE_X(38), 2. + 1. * wrap_len, WIRE_X(37), 2. + 1. * wrap_len}, - {WIRE_X(37), 2. + 1. * wrap_len, WIRE_X(37), 1. + PIP_Y(id_S272)}, - {WIRE_X(37), 1. + PIP_Y(id_S272), WIRE_X(0), 1. + PIP_Y(id_S272)}}}, -// clock taps -#define CLK_GT00_X 41.f -#define CLK_GT10_X 46.f -// 4 hop -#define HOP4X_START (CLK_GT00_X + 10.f) -#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START) - {id_S80, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, - {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, - {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, - {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, - {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, - {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N808) - 7.}, - {HOP4X(2), PIP_Y(id_N808) - 7., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S808) - 8.}, - {HOP4X(0), PIP_Y(id_S808) - 8., WIRE_X(0), PIP_Y(id_S808) - 8.}}}, - {id_N80, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, - {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, - {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, - {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, - {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, - {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N800) + 7.}, - {HOP4X(3), PIP_Y(id_N800) + 7., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N808) + 8.}, - {HOP4X(1), PIP_Y(id_N808) + 8., WIRE_X(0), PIP_Y(id_N808) + 8.}}}, - {id_S80_loop0, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, - {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, - {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N800) - 0.}, - {HOP4X(15), PIP_Y(id_N800) - 0., HOP4X(13), PIP_Y(id_N800) + left_wire_dist - 0.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N800) + 1.}, - {HOP4X(13), PIP_Y(id_N800) + 1., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N800) + 2.}, - {HOP4X(11), PIP_Y(id_N800) + 2., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N800) + 3.}, - {HOP4X(9), PIP_Y(id_N800) + 3., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N804) + 3., WIRE_X(0), PIP_Y(id_N804) + 3.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N800) + 4.}, - {HOP4X(7), PIP_Y(id_N800) + 4., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N800) + 5.}, - {HOP4X(5), PIP_Y(id_N800) + 5., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 5.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N800) + 6.}, - {HOP4X(3), PIP_Y(id_N800) + 6., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 6.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N808) + 7.}, - {HOP4X(1), PIP_Y(id_N808) + 7., WIRE_X(0), PIP_Y(id_N808) + 7.}}}, - {id_S80_loop1, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, - {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, - {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N800) - 1.}, - {HOP4X(13), PIP_Y(id_N800) - 1., HOP4X(11), PIP_Y(id_N800) + left_wire_dist - 1.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N800) - 0.}, - {HOP4X(11), PIP_Y(id_N800) - 0., HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 0.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N800) + 1.}, - {HOP4X(9), PIP_Y(id_N800) + 1., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(9), PIP_Y(id_N804) + 1., WIRE_X(0), PIP_Y(id_N804) + 1.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N800) + 2.}, - {HOP4X(7), PIP_Y(id_N800) + 2., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N800) + 3.}, - {HOP4X(5), PIP_Y(id_N800) + 3., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N800) + 4.}, - {HOP4X(3), PIP_Y(id_N800) + 4., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N808) + 5.}, - {HOP4X(1), PIP_Y(id_N808) + 5., WIRE_X(0), PIP_Y(id_N808) + 5.}}}, - {id_S80_loop2, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, - {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, - {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N800) - 2.}, - {HOP4X(11), PIP_Y(id_N800) - 2., HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 2.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N800) - 1.}, - {HOP4X(9), PIP_Y(id_N800) - 1., HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 1.}, - {HOP4X(9), PIP_Y(id_N804) - 1., WIRE_X(0), PIP_Y(id_N804) - 1.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N800) - 0.}, - {HOP4X(7), PIP_Y(id_N800) - 0., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 0.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N800) + 1.}, - {HOP4X(5), PIP_Y(id_N800) + 1., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N800) + 2.}, - {HOP4X(3), PIP_Y(id_N800) + 2., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N808) + 3.}, - {HOP4X(1), PIP_Y(id_N808) + 3., WIRE_X(0), PIP_Y(id_N808) + 3.}}}, - {id_S80_loop3, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, - {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, - {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, - {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N800) - 3.}, - {HOP4X(9), PIP_Y(id_N804) - 3., WIRE_X(0), PIP_Y(id_N804) - 3.}, - {HOP4X(9), PIP_Y(id_N800) - 3., HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 3.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N800) - 2.}, - {HOP4X(7), PIP_Y(id_N800) - 2., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 2.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N800) - 1.}, - {HOP4X(5), PIP_Y(id_N800) - 1., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 1.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N800) - 0.}, - {HOP4X(3), PIP_Y(id_N800) - 0., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 0.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N808) + 1.}, - {HOP4X(1), PIP_Y(id_N808) + 1., WIRE_X(0), PIP_Y(id_N808) + 1.}}}, - {id_S80_loop4, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, - {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, - {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, - {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, - {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, - {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N800) - 4.}, - {HOP4X(7), PIP_Y(id_N800) - 4., HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 4.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N800) - 3.}, - {HOP4X(5), PIP_Y(id_N800) - 3., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 3.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N800) - 2.}, - {HOP4X(3), PIP_Y(id_N800) - 2., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 2.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N808) - 1.}, - {HOP4X(1), PIP_Y(id_N808) - 1., WIRE_X(0), PIP_Y(id_N808) - 1.}}}, - {id_S80_loop5, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, - {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, - {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, - {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, - {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, - {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, - {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N800) - 5.}, - {HOP4X(5), PIP_Y(id_N800) - 5., HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 5.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N800) - 4.}, - {HOP4X(3), PIP_Y(id_N800) - 4., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 4.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N808) - 3.}, - {HOP4X(1), PIP_Y(id_N808) - 3., WIRE_X(0), PIP_Y(id_N808) - 3.}}}, - {id_S80_loop6, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, - {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, - {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, - {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, - {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, - {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, - {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, - {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N800) - 6.}, - {HOP4X(3), PIP_Y(id_N800) - 6., HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 6.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N808) - 5.}, - {HOP4X(1), PIP_Y(id_N808) - 5., WIRE_X(0), PIP_Y(id_N808) - 5.}}}, - {id_S80_loop7, - {{WIRE_X(0), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_S800)}, - {HOP4X(16), PIP_Y(id_S800), HOP4X(16), PIP_Y(id_N808)}, - {HOP4X(16), PIP_Y(id_N808) - 0., HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N808) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N808) - 1.}, - {HOP4X(14), PIP_Y(id_N808) - 1., HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N808) - 2.}, - {HOP4X(12), PIP_Y(id_N808) - 2., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N808) - 3.}, - {HOP4X(10), PIP_Y(id_N808) - 3., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N808) - 4.}, - {HOP4X(8), PIP_Y(id_S804) - 4., WIRE_X(0), PIP_Y(id_S804) - 4.}, - {HOP4X(8), PIP_Y(id_N808) - 4., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N808) - 5.}, - {HOP4X(6), PIP_Y(id_N808) - 5., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N808) - 6.}, - {HOP4X(4), PIP_Y(id_N808) - 6., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N808) - 7.}, - {HOP4X(2), PIP_Y(id_N808) - 7., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, - {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, - {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N808) - 7.}, - {HOP4X(1), PIP_Y(id_N808) - 7., WIRE_X(0), PIP_Y(id_N808) - 7.}}}, - {id_N80_loop0, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, - {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, - {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N808) + 0.}, - {HOP4X(14), PIP_Y(id_N808) + 0., HOP4X(12), PIP_Y(id_N808) - left_wire_dist + 0.}, - {HOP4X(12), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N808) - 1.}, - {HOP4X(12), PIP_Y(id_N808) - 1., HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N808) - 2.}, - {HOP4X(10), PIP_Y(id_N808) - 2., HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N808) - 3.}, - {HOP4X(8), PIP_Y(id_S804) - 3., WIRE_X(0), PIP_Y(id_S804) - 3.}, - {HOP4X(8), PIP_Y(id_N808) - 3., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N808) - 4.}, - {HOP4X(6), PIP_Y(id_N808) - 4., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N808) - 5.}, - {HOP4X(4), PIP_Y(id_N808) - 5., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 5.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N808) - 6.}, - {HOP4X(2), PIP_Y(id_N808) - 6., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 6.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S808) - 7.}, - {HOP4X(0), PIP_Y(id_S808) - 7., WIRE_X(0), PIP_Y(id_S808) - 7.}}}, - {id_N80_loop1, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, - {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, - {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N808) + 1.}, - {HOP4X(12), PIP_Y(id_N808) + 1., HOP4X(10), PIP_Y(id_N808) - left_wire_dist + 1.}, - {HOP4X(10), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N808) + 0.}, - {HOP4X(10), PIP_Y(id_N808) + 0., HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 0.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N808) - 1.}, - {HOP4X(8), PIP_Y(id_S804) - 1., WIRE_X(0), PIP_Y(id_S804) - 1.}, - {HOP4X(8), PIP_Y(id_N808) - 1., HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N808) - 2.}, - {HOP4X(6), PIP_Y(id_N808) - 2., HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N808) - 3.}, - {HOP4X(4), PIP_Y(id_N808) - 3., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 3.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N808) - 4.}, - {HOP4X(2), PIP_Y(id_N808) - 4., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 4.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S808) - 5.}, - {HOP4X(0), PIP_Y(id_S808) - 5., WIRE_X(0), PIP_Y(id_S808) - 5.}}}, - {id_N80_loop2, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, - {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, - {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N808) + 2.}, - {HOP4X(10), PIP_Y(id_N808) + 2., HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 2.}, - {HOP4X(8), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N808) + 1.}, - {HOP4X(8), PIP_Y(id_S804) + 1., WIRE_X(0), PIP_Y(id_S804) + 1.}, - {HOP4X(8), PIP_Y(id_N808) + 1., HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 1.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N808) + 0.}, - {HOP4X(6), PIP_Y(id_N808) + 0., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 0.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N808) - 1.}, - {HOP4X(4), PIP_Y(id_N808) - 1., HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 1.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N808) - 2.}, - {HOP4X(2), PIP_Y(id_N808) - 2., HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 2.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S808) - 3.}, - {HOP4X(0), PIP_Y(id_S808) - 3., WIRE_X(0), PIP_Y(id_S808) - 3.}}}, - {id_N80_loop3, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, - {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, - {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, - {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N808) + 3.}, - {HOP4X(8), PIP_Y(id_S804) + 3., WIRE_X(0), PIP_Y(id_S804) + 3.}, - {HOP4X(8), PIP_Y(id_N808) + 3., HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 3.}, - {HOP4X(6), PIP_Y(id_N808) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N808) + 2.}, - {HOP4X(6), PIP_Y(id_N808) + 2., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 2.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N808) + 1.}, - {HOP4X(4), PIP_Y(id_N808) + 1., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 1.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N808) + 0.}, - {HOP4X(2), PIP_Y(id_N808) + 0., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 0.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S808) - 1.}, - {HOP4X(0), PIP_Y(id_S808) - 1., WIRE_X(0), PIP_Y(id_S808) - 1.}}}, - {id_N80_loop4, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, - {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, - {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, - {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, - {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, - {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N808) + 4.}, - {HOP4X(6), PIP_Y(id_N808) + 4., HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 4.}, - {HOP4X(4), PIP_Y(id_N808) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N808) + 3.}, - {HOP4X(4), PIP_Y(id_N808) + 3., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 3.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N808) + 2.}, - {HOP4X(2), PIP_Y(id_N808) + 2., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 2.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S808) + 1.}, - {HOP4X(0), PIP_Y(id_S808) + 1., WIRE_X(0), PIP_Y(id_S808) + 1.}}}, - {id_N80_loop5, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, - {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, - {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, - {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, - {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, - {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, - {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N808) + 5.}, - {HOP4X(4), PIP_Y(id_N808) + 5., HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 5.}, - {HOP4X(2), PIP_Y(id_N808) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N808) + 4.}, - {HOP4X(2), PIP_Y(id_N808) + 4., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 4.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S808) + 3.}, - {HOP4X(0), PIP_Y(id_S808) + 3., WIRE_X(0), PIP_Y(id_S808) + 3.}}}, - {id_N80_loop6, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, - {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, - {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, - {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, - {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, - {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, - {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, - {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N808) + 6.}, - {HOP4X(2), PIP_Y(id_N808) + 6., HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 6.}, - {HOP4X(0), PIP_Y(id_N808) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S808) + 5.}, - {HOP4X(0), PIP_Y(id_S808) + 5., WIRE_X(0), PIP_Y(id_S808) + 5.}}}, - {id_N80_loop7, - {{WIRE_X(0), PIP_Y(id_N800), HOP4X(17), PIP_Y(id_N800)}, - {HOP4X(17), PIP_Y(id_N800) + 0., HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N800) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N800) + 1.}, - {HOP4X(15), PIP_Y(id_N800) + 1., HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N800) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N800) + 2.}, - {HOP4X(13), PIP_Y(id_N800) + 2., HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N800) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N800) + 3.}, - {HOP4X(11), PIP_Y(id_N800) + 3., HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N800) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N800) + 4.}, - {HOP4X(9), PIP_Y(id_N804) + 4., WIRE_X(0), PIP_Y(id_N804) + 4.}, - {HOP4X(9), PIP_Y(id_N800) + 4., HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N800) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N800) + 5.}, - {HOP4X(7), PIP_Y(id_N800) + 5., HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N800) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N800) + 6.}, - {HOP4X(5), PIP_Y(id_N800) + 6., HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N800) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N800) + 7.}, - {HOP4X(3), PIP_Y(id_N800) + 7., HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N800) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, - {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, - {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S808) + 7.}, - {HOP4X(0), PIP_Y(id_S808) + 7., WIRE_X(0), PIP_Y(id_S808) + 7.}}}, - -#undef HOP4X -#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f) - {id_S81, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, - {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, - {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, - {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, - {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, - {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N818) - 7.}, - {HOP4X(2), PIP_Y(id_N818) - 7., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S818) - 8.}, - {HOP4X(0), PIP_Y(id_S818) - 8., WIRE_X(0), PIP_Y(id_S818) - 8.}}}, - {id_N81, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, - {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, - {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, - {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, - {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, - {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N810) + 7.}, - {HOP4X(3), PIP_Y(id_N810) + 7., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N818) + 8.}, - {HOP4X(1), PIP_Y(id_N818) + 8., WIRE_X(0), PIP_Y(id_N818) + 8.}}}, - {id_S81_loop0, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, - {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, - {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N810) - 0.}, - {HOP4X(15), PIP_Y(id_N810) - 0., HOP4X(13), PIP_Y(id_N810) + left_wire_dist - 0.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N810) + 1.}, - {HOP4X(13), PIP_Y(id_N810) + 1., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N810) + 2.}, - {HOP4X(11), PIP_Y(id_N810) + 2., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N810) + 3.}, - {HOP4X(9), PIP_Y(id_N810) + 3., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N814) + 3., WIRE_X(0), PIP_Y(id_N814) + 3.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N810) + 4.}, - {HOP4X(7), PIP_Y(id_N810) + 4., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N810) + 5.}, - {HOP4X(5), PIP_Y(id_N810) + 5., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 5.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N810) + 6.}, - {HOP4X(3), PIP_Y(id_N810) + 6., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 6.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N818) + 7.}, - {HOP4X(1), PIP_Y(id_N818) + 7., WIRE_X(0), PIP_Y(id_N818) + 7.}}}, - {id_S81_loop1, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, - {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, - {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N810) - 1.}, - {HOP4X(13), PIP_Y(id_N810) - 1., HOP4X(11), PIP_Y(id_N810) + left_wire_dist - 1.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N810) - 0.}, - {HOP4X(11), PIP_Y(id_N810) - 0., HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 0.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N810) + 1.}, - {HOP4X(9), PIP_Y(id_N810) + 1., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(9), PIP_Y(id_N814) + 1., WIRE_X(0), PIP_Y(id_N814) + 1.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N810) + 2.}, - {HOP4X(7), PIP_Y(id_N810) + 2., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N810) + 3.}, - {HOP4X(5), PIP_Y(id_N810) + 3., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N810) + 4.}, - {HOP4X(3), PIP_Y(id_N810) + 4., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N818) + 5.}, - {HOP4X(1), PIP_Y(id_N818) + 5., WIRE_X(0), PIP_Y(id_N818) + 5.}}}, - {id_S81_loop2, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, - {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, - {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N810) - 2.}, - {HOP4X(11), PIP_Y(id_N810) - 2., HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 2.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N810) - 1.}, - {HOP4X(9), PIP_Y(id_N810) - 1., HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 1.}, - {HOP4X(9), PIP_Y(id_N814) - 1., WIRE_X(0), PIP_Y(id_N814) - 1.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N810) - 0.}, - {HOP4X(7), PIP_Y(id_N810) - 0., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 0.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N810) + 1.}, - {HOP4X(5), PIP_Y(id_N810) + 1., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N810) + 2.}, - {HOP4X(3), PIP_Y(id_N810) + 2., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N818) + 3.}, - {HOP4X(1), PIP_Y(id_N818) + 3., WIRE_X(0), PIP_Y(id_N818) + 3.}}}, - {id_S81_loop3, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, - {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, - {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, - {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N810) - 3.}, - {HOP4X(9), PIP_Y(id_N814) - 3., WIRE_X(0), PIP_Y(id_N814) - 3.}, - {HOP4X(9), PIP_Y(id_N810) - 3., HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 3.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N810) - 2.}, - {HOP4X(7), PIP_Y(id_N810) - 2., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 2.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N810) - 1.}, - {HOP4X(5), PIP_Y(id_N810) - 1., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 1.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N810) - 0.}, - {HOP4X(3), PIP_Y(id_N810) - 0., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 0.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N818) + 1.}, - {HOP4X(1), PIP_Y(id_N818) + 1., WIRE_X(0), PIP_Y(id_N818) + 1.}}}, - {id_S81_loop4, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, - {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, - {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, - {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, - {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, - {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N810) - 4.}, - {HOP4X(7), PIP_Y(id_N810) - 4., HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 4.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N810) - 3.}, - {HOP4X(5), PIP_Y(id_N810) - 3., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 3.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N810) - 2.}, - {HOP4X(3), PIP_Y(id_N810) - 2., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 2.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N818) - 1.}, - {HOP4X(1), PIP_Y(id_N818) - 1., WIRE_X(0), PIP_Y(id_N818) - 1.}}}, - {id_S81_loop5, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, - {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, - {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, - {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, - {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, - {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, - {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N810) - 5.}, - {HOP4X(5), PIP_Y(id_N810) - 5., HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 5.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N810) - 4.}, - {HOP4X(3), PIP_Y(id_N810) - 4., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 4.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N818) - 3.}, - {HOP4X(1), PIP_Y(id_N818) - 3., WIRE_X(0), PIP_Y(id_N818) - 3.}}}, - {id_S81_loop6, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, - {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, - {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, - {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, - {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, - {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, - {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, - {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N810) - 6.}, - {HOP4X(3), PIP_Y(id_N810) - 6., HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 6.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N818) - 5.}, - {HOP4X(1), PIP_Y(id_N818) - 5., WIRE_X(0), PIP_Y(id_N818) - 5.}}}, - {id_S81_loop7, - {{WIRE_X(0), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_S810)}, - {HOP4X(16), PIP_Y(id_S810), HOP4X(16), PIP_Y(id_N818)}, - {HOP4X(16), PIP_Y(id_N818) - 0., HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N818) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N818) - 1.}, - {HOP4X(14), PIP_Y(id_N818) - 1., HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N818) - 2.}, - {HOP4X(12), PIP_Y(id_N818) - 2., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N818) - 3.}, - {HOP4X(10), PIP_Y(id_N818) - 3., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N818) - 4.}, - {HOP4X(8), PIP_Y(id_S814) - 4., WIRE_X(0), PIP_Y(id_S814) - 4.}, - {HOP4X(8), PIP_Y(id_N818) - 4., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N818) - 5.}, - {HOP4X(6), PIP_Y(id_N818) - 5., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N818) - 6.}, - {HOP4X(4), PIP_Y(id_N818) - 6., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N818) - 7.}, - {HOP4X(2), PIP_Y(id_N818) - 7., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, - {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, - {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N818) - 7.}, - {HOP4X(1), PIP_Y(id_N818) - 7., WIRE_X(0), PIP_Y(id_N818) - 7.}}}, - {id_N81_loop0, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, - {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, - {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N818) + 0.}, - {HOP4X(14), PIP_Y(id_N818) + 0., HOP4X(12), PIP_Y(id_N818) - left_wire_dist + 0.}, - {HOP4X(12), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N818) - 1.}, - {HOP4X(12), PIP_Y(id_N818) - 1., HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N818) - 2.}, - {HOP4X(10), PIP_Y(id_N818) - 2., HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N818) - 3.}, - {HOP4X(8), PIP_Y(id_S814) - 3., WIRE_X(0), PIP_Y(id_S814) - 3.}, - {HOP4X(8), PIP_Y(id_N818) - 3., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N818) - 4.}, - {HOP4X(6), PIP_Y(id_N818) - 4., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N818) - 5.}, - {HOP4X(4), PIP_Y(id_N818) - 5., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 5.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N818) - 6.}, - {HOP4X(2), PIP_Y(id_N818) - 6., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 6.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S818) - 7.}, - {HOP4X(0), PIP_Y(id_S818) - 7., WIRE_X(0), PIP_Y(id_S818) - 7.}}}, - {id_N81_loop1, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, - {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, - {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N818) + 1.}, - {HOP4X(12), PIP_Y(id_N818) + 1., HOP4X(10), PIP_Y(id_N818) - left_wire_dist + 1.}, - {HOP4X(10), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N818) + 0.}, - {HOP4X(10), PIP_Y(id_N818) + 0., HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 0.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N818) - 1.}, - {HOP4X(8), PIP_Y(id_S814) - 1., WIRE_X(0), PIP_Y(id_S814) - 1.}, - {HOP4X(8), PIP_Y(id_N818) - 1., HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N818) - 2.}, - {HOP4X(6), PIP_Y(id_N818) - 2., HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N818) - 3.}, - {HOP4X(4), PIP_Y(id_N818) - 3., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 3.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N818) - 4.}, - {HOP4X(2), PIP_Y(id_N818) - 4., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 4.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S818) - 5.}, - {HOP4X(0), PIP_Y(id_S818) - 5., WIRE_X(0), PIP_Y(id_S818) - 5.}}}, - {id_N81_loop2, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, - {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, - {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N818) + 2.}, - {HOP4X(10), PIP_Y(id_N818) + 2., HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 2.}, - {HOP4X(8), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N818) + 1.}, - {HOP4X(8), PIP_Y(id_S814) + 1., WIRE_X(0), PIP_Y(id_S814) + 1.}, - {HOP4X(8), PIP_Y(id_N818) + 1., HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 1.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N818) + 0.}, - {HOP4X(6), PIP_Y(id_N818) + 0., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 0.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N818) - 1.}, - {HOP4X(4), PIP_Y(id_N818) - 1., HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 1.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N818) - 2.}, - {HOP4X(2), PIP_Y(id_N818) - 2., HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 2.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S818) - 3.}, - {HOP4X(0), PIP_Y(id_S818) - 3., WIRE_X(0), PIP_Y(id_S818) - 3.}}}, - {id_N81_loop3, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, - {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, - {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, - {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N818) + 3.}, - {HOP4X(8), PIP_Y(id_S814) + 3., WIRE_X(0), PIP_Y(id_S814) + 3.}, - {HOP4X(8), PIP_Y(id_N818) + 3., HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 3.}, - {HOP4X(6), PIP_Y(id_N818) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N818) + 2.}, - {HOP4X(6), PIP_Y(id_N818) + 2., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 2.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N818) + 1.}, - {HOP4X(4), PIP_Y(id_N818) + 1., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 1.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N818) + 0.}, - {HOP4X(2), PIP_Y(id_N818) + 0., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 0.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S818) - 1.}, - {HOP4X(0), PIP_Y(id_S818) - 1., WIRE_X(0), PIP_Y(id_S818) - 1.}}}, - {id_N81_loop4, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, - {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, - {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, - {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, - {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, - {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N818) + 4.}, - {HOP4X(6), PIP_Y(id_N818) + 4., HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 4.}, - {HOP4X(4), PIP_Y(id_N818) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N818) + 3.}, - {HOP4X(4), PIP_Y(id_N818) + 3., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 3.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N818) + 2.}, - {HOP4X(2), PIP_Y(id_N818) + 2., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 2.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S818) + 1.}, - {HOP4X(0), PIP_Y(id_S818) + 1., WIRE_X(0), PIP_Y(id_S818) + 1.}}}, - {id_N81_loop5, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, - {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, - {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, - {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, - {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, - {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, - {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N818) + 5.}, - {HOP4X(4), PIP_Y(id_N818) + 5., HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 5.}, - {HOP4X(2), PIP_Y(id_N818) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N818) + 4.}, - {HOP4X(2), PIP_Y(id_N818) + 4., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 4.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S818) + 3.}, - {HOP4X(0), PIP_Y(id_S818) + 3., WIRE_X(0), PIP_Y(id_S818) + 3.}}}, - {id_N81_loop6, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, - {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, - {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, - {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, - {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, - {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, - {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, - {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N818) + 6.}, - {HOP4X(2), PIP_Y(id_N818) + 6., HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 6.}, - {HOP4X(0), PIP_Y(id_N818) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S818) + 5.}, - {HOP4X(0), PIP_Y(id_S818) + 5., WIRE_X(0), PIP_Y(id_S818) + 5.}}}, - {id_N81_loop7, - {{WIRE_X(0), PIP_Y(id_N810), HOP4X(17), PIP_Y(id_N810)}, - {HOP4X(17), PIP_Y(id_N810) + 0., HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N810) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N810) + 1.}, - {HOP4X(15), PIP_Y(id_N810) + 1., HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N810) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N810) + 2.}, - {HOP4X(13), PIP_Y(id_N810) + 2., HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N810) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N810) + 3.}, - {HOP4X(11), PIP_Y(id_N810) + 3., HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N810) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N810) + 4.}, - {HOP4X(9), PIP_Y(id_N814) + 4., WIRE_X(0), PIP_Y(id_N814) + 4.}, - {HOP4X(9), PIP_Y(id_N810) + 4., HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N810) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N810) + 5.}, - {HOP4X(7), PIP_Y(id_N810) + 5., HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N810) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N810) + 6.}, - {HOP4X(5), PIP_Y(id_N810) + 6., HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N810) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N810) + 7.}, - {HOP4X(3), PIP_Y(id_N810) + 7., HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N810) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, - {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, - {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S818) + 7.}, - {HOP4X(0), PIP_Y(id_S818) + 7., WIRE_X(0), PIP_Y(id_S818) + 7.}}}, - -#undef HOP4X -#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f + 18.f) - {id_S82, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, - {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, - {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, - {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, - {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, - {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N828) - 7.}, - {HOP4X(2), PIP_Y(id_N828) - 7., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S828) - 8.}, - {HOP4X(0), PIP_Y(id_S828) - 8., WIRE_X(0), PIP_Y(id_S828) - 8.}}}, - {id_N82, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, - {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, - {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, - {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, - {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, - {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N820) + 7.}, - {HOP4X(3), PIP_Y(id_N820) + 7., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N828) + 8.}, - {HOP4X(1), PIP_Y(id_N828) + 8., WIRE_X(0), PIP_Y(id_N828) + 8.}}}, - {id_S82_loop0, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, - {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, - {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N820) - 0.}, - {HOP4X(15), PIP_Y(id_N820) - 0., HOP4X(13), PIP_Y(id_N820) + left_wire_dist - 0.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N820) + 1.}, - {HOP4X(13), PIP_Y(id_N820) + 1., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N820) + 2.}, - {HOP4X(11), PIP_Y(id_N820) + 2., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N820) + 3.}, - {HOP4X(9), PIP_Y(id_N820) + 3., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N824) + 3., WIRE_X(0), PIP_Y(id_N824) + 3.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N820) + 4.}, - {HOP4X(7), PIP_Y(id_N820) + 4., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N820) + 5.}, - {HOP4X(5), PIP_Y(id_N820) + 5., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 5.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N820) + 6.}, - {HOP4X(3), PIP_Y(id_N820) + 6., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 6.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N828) + 7.}, - {HOP4X(1), PIP_Y(id_N828) + 7., WIRE_X(0), PIP_Y(id_N828) + 7.}}}, - {id_S82_loop1, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, - {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, - {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N820) - 1.}, - {HOP4X(13), PIP_Y(id_N820) - 1., HOP4X(11), PIP_Y(id_N820) + left_wire_dist - 1.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N820) - 0.}, - {HOP4X(11), PIP_Y(id_N820) - 0., HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 0.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N820) + 1.}, - {HOP4X(9), PIP_Y(id_N820) + 1., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(9), PIP_Y(id_N824) + 1., WIRE_X(0), PIP_Y(id_N824) + 1.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N820) + 2.}, - {HOP4X(7), PIP_Y(id_N820) + 2., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N820) + 3.}, - {HOP4X(5), PIP_Y(id_N820) + 3., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N820) + 4.}, - {HOP4X(3), PIP_Y(id_N820) + 4., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N828) + 5.}, - {HOP4X(1), PIP_Y(id_N828) + 5., WIRE_X(0), PIP_Y(id_N828) + 5.}}}, - {id_S82_loop2, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, - {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, - {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N820) - 2.}, - {HOP4X(11), PIP_Y(id_N820) - 2., HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 2.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N820) - 1.}, - {HOP4X(9), PIP_Y(id_N820) - 1., HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 1.}, - {HOP4X(9), PIP_Y(id_N824) - 1., WIRE_X(0), PIP_Y(id_N824) - 1.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N820) - 0.}, - {HOP4X(7), PIP_Y(id_N820) - 0., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 0.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N820) + 1.}, - {HOP4X(5), PIP_Y(id_N820) + 1., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N820) + 2.}, - {HOP4X(3), PIP_Y(id_N820) + 2., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N828) + 3.}, - {HOP4X(1), PIP_Y(id_N828) + 3., WIRE_X(0), PIP_Y(id_N828) + 3.}}}, - {id_S82_loop3, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, - {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, - {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, - {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N820) - 3.}, - {HOP4X(9), PIP_Y(id_N824) - 3., WIRE_X(0), PIP_Y(id_N824) - 3.}, - {HOP4X(9), PIP_Y(id_N820) - 3., HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 3.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N820) - 2.}, - {HOP4X(7), PIP_Y(id_N820) - 2., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 2.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N820) - 1.}, - {HOP4X(5), PIP_Y(id_N820) - 1., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 1.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N820) - 0.}, - {HOP4X(3), PIP_Y(id_N820) - 0., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 0.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N828) + 1.}, - {HOP4X(1), PIP_Y(id_N828) + 1., WIRE_X(0), PIP_Y(id_N828) + 1.}}}, - {id_S82_loop4, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, - {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, - {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, - {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, - {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, - {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N820) - 4.}, - {HOP4X(7), PIP_Y(id_N820) - 4., HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 4.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N820) - 3.}, - {HOP4X(5), PIP_Y(id_N820) - 3., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 3.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N820) - 2.}, - {HOP4X(3), PIP_Y(id_N820) - 2., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 2.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N828) - 1.}, - {HOP4X(1), PIP_Y(id_N828) - 1., WIRE_X(0), PIP_Y(id_N828) - 1.}}}, - {id_S82_loop5, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, - {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, - {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, - {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, - {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, - {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, - {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N820) - 5.}, - {HOP4X(5), PIP_Y(id_N820) - 5., HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 5.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N820) - 4.}, - {HOP4X(3), PIP_Y(id_N820) - 4., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 4.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N828) - 3.}, - {HOP4X(1), PIP_Y(id_N828) - 3., WIRE_X(0), PIP_Y(id_N828) - 3.}}}, - {id_S82_loop6, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, - {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, - {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, - {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, - {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, - {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, - {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, - {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N820) - 6.}, - {HOP4X(3), PIP_Y(id_N820) - 6., HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 6.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N828) - 5.}, - {HOP4X(1), PIP_Y(id_N828) - 5., WIRE_X(0), PIP_Y(id_N828) - 5.}}}, - {id_S82_loop7, - {{WIRE_X(0), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_S820)}, - {HOP4X(16), PIP_Y(id_S820), HOP4X(16), PIP_Y(id_N828)}, - {HOP4X(16), PIP_Y(id_N828) - 0., HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N828) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N828) - 1.}, - {HOP4X(14), PIP_Y(id_N828) - 1., HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N828) - 2.}, - {HOP4X(12), PIP_Y(id_N828) - 2., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N828) - 3.}, - {HOP4X(10), PIP_Y(id_N828) - 3., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N828) - 4.}, - {HOP4X(8), PIP_Y(id_S824) - 4., WIRE_X(0), PIP_Y(id_S824) - 4.}, - {HOP4X(8), PIP_Y(id_N828) - 4., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N828) - 5.}, - {HOP4X(6), PIP_Y(id_N828) - 5., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N828) - 6.}, - {HOP4X(4), PIP_Y(id_N828) - 6., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N828) - 7.}, - {HOP4X(2), PIP_Y(id_N828) - 7., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, - {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, - {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N828) - 7.}, - {HOP4X(1), PIP_Y(id_N828) - 7., WIRE_X(0), PIP_Y(id_N828) - 7.}}}, - {id_N82_loop0, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, - {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, - {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N828) + 0.}, - {HOP4X(14), PIP_Y(id_N828) + 0., HOP4X(12), PIP_Y(id_N828) - left_wire_dist + 0.}, - {HOP4X(12), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N828) - 1.}, - {HOP4X(12), PIP_Y(id_N828) - 1., HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N828) - 2.}, - {HOP4X(10), PIP_Y(id_N828) - 2., HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N828) - 3.}, - {HOP4X(8), PIP_Y(id_S824) - 3., WIRE_X(0), PIP_Y(id_S824) - 3.}, - {HOP4X(8), PIP_Y(id_N828) - 3., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N828) - 4.}, - {HOP4X(6), PIP_Y(id_N828) - 4., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N828) - 5.}, - {HOP4X(4), PIP_Y(id_N828) - 5., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 5.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N828) - 6.}, - {HOP4X(2), PIP_Y(id_N828) - 6., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 6.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S828) - 7.}, - {HOP4X(0), PIP_Y(id_S828) - 7., WIRE_X(0), PIP_Y(id_S828) - 7.}}}, - {id_N82_loop1, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, - {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, - {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N828) + 1.}, - {HOP4X(12), PIP_Y(id_N828) + 1., HOP4X(10), PIP_Y(id_N828) - left_wire_dist + 1.}, - {HOP4X(10), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N828) + 0.}, - {HOP4X(10), PIP_Y(id_N828) + 0., HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 0.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N828) - 1.}, - {HOP4X(8), PIP_Y(id_S824) - 1., WIRE_X(0), PIP_Y(id_S824) - 1.}, - {HOP4X(8), PIP_Y(id_N828) - 1., HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N828) - 2.}, - {HOP4X(6), PIP_Y(id_N828) - 2., HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N828) - 3.}, - {HOP4X(4), PIP_Y(id_N828) - 3., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 3.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N828) - 4.}, - {HOP4X(2), PIP_Y(id_N828) - 4., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 4.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S828) - 5.}, - {HOP4X(0), PIP_Y(id_S828) - 5., WIRE_X(0), PIP_Y(id_S828) - 5.}}}, - {id_N82_loop2, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, - {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, - {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N828) + 2.}, - {HOP4X(10), PIP_Y(id_N828) + 2., HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 2.}, - {HOP4X(8), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N828) + 1.}, - {HOP4X(8), PIP_Y(id_S824) + 1., WIRE_X(0), PIP_Y(id_S824) + 1.}, - {HOP4X(8), PIP_Y(id_N828) + 1., HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 1.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N828) + 0.}, - {HOP4X(6), PIP_Y(id_N828) + 0., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 0.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N828) - 1.}, - {HOP4X(4), PIP_Y(id_N828) - 1., HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 1.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N828) - 2.}, - {HOP4X(2), PIP_Y(id_N828) - 2., HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 2.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S828) - 3.}, - {HOP4X(0), PIP_Y(id_S828) - 3., WIRE_X(0), PIP_Y(id_S828) - 3.}}}, - {id_N82_loop3, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, - {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, - {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, - {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N828) + 3.}, - {HOP4X(8), PIP_Y(id_S824) + 3., WIRE_X(0), PIP_Y(id_S824) + 3.}, - {HOP4X(8), PIP_Y(id_N828) + 3., HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 3.}, - {HOP4X(6), PIP_Y(id_N828) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N828) + 2.}, - {HOP4X(6), PIP_Y(id_N828) + 2., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 2.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N828) + 1.}, - {HOP4X(4), PIP_Y(id_N828) + 1., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 1.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N828) + 0.}, - {HOP4X(2), PIP_Y(id_N828) + 0., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 0.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S828) - 1.}, - {HOP4X(0), PIP_Y(id_S828) - 1., WIRE_X(0), PIP_Y(id_S828) - 1.}}}, - {id_N82_loop4, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, - {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, - {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, - {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, - {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, - {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N828) + 4.}, - {HOP4X(6), PIP_Y(id_N828) + 4., HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 4.}, - {HOP4X(4), PIP_Y(id_N828) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N828) + 3.}, - {HOP4X(4), PIP_Y(id_N828) + 3., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 3.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N828) + 2.}, - {HOP4X(2), PIP_Y(id_N828) + 2., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 2.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S828) + 1.}, - {HOP4X(0), PIP_Y(id_S828) + 1., WIRE_X(0), PIP_Y(id_S828) + 1.}}}, - {id_N82_loop5, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, - {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, - {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, - {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, - {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, - {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, - {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N828) + 5.}, - {HOP4X(4), PIP_Y(id_N828) + 5., HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 5.}, - {HOP4X(2), PIP_Y(id_N828) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N828) + 4.}, - {HOP4X(2), PIP_Y(id_N828) + 4., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 4.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S828) + 3.}, - {HOP4X(0), PIP_Y(id_S828) + 3., WIRE_X(0), PIP_Y(id_S828) + 3.}}}, - {id_N82_loop6, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, - {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, - {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, - {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, - {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, - {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, - {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, - {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N828) + 6.}, - {HOP4X(2), PIP_Y(id_N828) + 6., HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 6.}, - {HOP4X(0), PIP_Y(id_N828) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S828) + 5.}, - {HOP4X(0), PIP_Y(id_S828) + 5., WIRE_X(0), PIP_Y(id_S828) + 5.}}}, - {id_N82_loop7, - {{WIRE_X(0), PIP_Y(id_N820), HOP4X(17), PIP_Y(id_N820)}, - {HOP4X(17), PIP_Y(id_N820) + 0., HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N820) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N820) + 1.}, - {HOP4X(15), PIP_Y(id_N820) + 1., HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N820) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N820) + 2.}, - {HOP4X(13), PIP_Y(id_N820) + 2., HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N820) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N820) + 3.}, - {HOP4X(11), PIP_Y(id_N820) + 3., HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N820) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N820) + 4.}, - {HOP4X(9), PIP_Y(id_N824) + 4., WIRE_X(0), PIP_Y(id_N824) + 4.}, - {HOP4X(9), PIP_Y(id_N820) + 4., HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N820) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N820) + 5.}, - {HOP4X(7), PIP_Y(id_N820) + 5., HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N820) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N820) + 6.}, - {HOP4X(5), PIP_Y(id_N820) + 6., HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N820) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N820) + 7.}, - {HOP4X(3), PIP_Y(id_N820) + 7., HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N820) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, - {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, - {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S828) + 7.}, - {HOP4X(0), PIP_Y(id_S828) + 7., WIRE_X(0), PIP_Y(id_S828) + 7.}}}, - -#undef HOP4X -#define HOP4X(offset) WIRE_X((float)offset + HOP4X_START + 18.f + 18.f + 18.f) - {id_S83, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, - {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, - {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, - {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, - {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, - {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N838) - 7.}, - {HOP4X(2), PIP_Y(id_N838) - 7., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7., HOP4X(0), PIP_Y(id_S838) - 8.}, - {HOP4X(0), PIP_Y(id_S838) - 8., WIRE_X(0), PIP_Y(id_S838) - 8.}}}, - {id_N83, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, - {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, - {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, - {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, - {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, - {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N830) + 7.}, - {HOP4X(3), PIP_Y(id_N830) + 7., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7., HOP4X(1), PIP_Y(id_N838) + 8.}, - {HOP4X(1), PIP_Y(id_N838) + 8., WIRE_X(0), PIP_Y(id_N838) + 8.}}}, - {id_S83_loop0, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), -wrap_len - 0.}, - {HOP4X(14), -wrap_len - 0., HOP4X(15), -wrap_len - 0.}, - {HOP4X(15), -wrap_len - 0., HOP4X(15), PIP_Y(id_N830) - 0.}, - {HOP4X(15), PIP_Y(id_N830) - 0., HOP4X(13), PIP_Y(id_N830) + left_wire_dist - 0.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(13), PIP_Y(id_N830) + 1.}, - {HOP4X(13), PIP_Y(id_N830) + 1., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(11), PIP_Y(id_N830) + 2.}, - {HOP4X(11), PIP_Y(id_N830) + 2., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(9), PIP_Y(id_N830) + 3.}, - {HOP4X(9), PIP_Y(id_N830) + 3., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N834) + 3., WIRE_X(0), PIP_Y(id_N834) + 3.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(7), PIP_Y(id_N830) + 4.}, - {HOP4X(7), PIP_Y(id_N830) + 4., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(5), PIP_Y(id_N830) + 5.}, - {HOP4X(5), PIP_Y(id_N830) + 5., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 5.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(3), PIP_Y(id_N830) + 6.}, - {HOP4X(3), PIP_Y(id_N830) + 6., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 6.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(1), PIP_Y(id_N838) + 7.}, - {HOP4X(1), PIP_Y(id_N838) + 7., WIRE_X(0), PIP_Y(id_N838) + 7.}}}, - {id_S83_loop1, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), -wrap_len - 1.}, - {HOP4X(12), -wrap_len - 1., HOP4X(13), -wrap_len - 1.}, - {HOP4X(13), -wrap_len - 1., HOP4X(13), PIP_Y(id_N830) - 1.}, - {HOP4X(13), PIP_Y(id_N830) - 1., HOP4X(11), PIP_Y(id_N830) + left_wire_dist - 1.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(11), PIP_Y(id_N830) - 0.}, - {HOP4X(11), PIP_Y(id_N830) - 0., HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 0.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(9), PIP_Y(id_N830) + 1.}, - {HOP4X(9), PIP_Y(id_N830) + 1., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(9), PIP_Y(id_N834) + 1., WIRE_X(0), PIP_Y(id_N834) + 1.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(7), PIP_Y(id_N830) + 2.}, - {HOP4X(7), PIP_Y(id_N830) + 2., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(5), PIP_Y(id_N830) + 3.}, - {HOP4X(5), PIP_Y(id_N830) + 3., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(3), PIP_Y(id_N830) + 4.}, - {HOP4X(3), PIP_Y(id_N830) + 4., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(1), PIP_Y(id_N838) + 5.}, - {HOP4X(1), PIP_Y(id_N838) + 5., WIRE_X(0), PIP_Y(id_N838) + 5.}}}, - {id_S83_loop2, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), -wrap_len - 2.}, - {HOP4X(10), -wrap_len - 2., HOP4X(11), -wrap_len - 2.}, - {HOP4X(11), -wrap_len - 2., HOP4X(11), PIP_Y(id_N830) - 2.}, - {HOP4X(11), PIP_Y(id_N830) - 2., HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 2.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(9), PIP_Y(id_N830) - 1.}, - {HOP4X(9), PIP_Y(id_N830) - 1., HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 1.}, - {HOP4X(9), PIP_Y(id_N834) - 1., WIRE_X(0), PIP_Y(id_N834) - 1.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(7), PIP_Y(id_N830) - 0.}, - {HOP4X(7), PIP_Y(id_N830) - 0., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 0.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(5), PIP_Y(id_N830) + 1.}, - {HOP4X(5), PIP_Y(id_N830) + 1., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(3), PIP_Y(id_N830) + 2.}, - {HOP4X(3), PIP_Y(id_N830) + 2., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(1), PIP_Y(id_N838) + 3.}, - {HOP4X(1), PIP_Y(id_N838) + 3., WIRE_X(0), PIP_Y(id_N838) + 3.}}}, - {id_S83_loop3, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, - {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), -wrap_len - 3.}, - {HOP4X(8), -wrap_len - 3., HOP4X(9), -wrap_len - 3.}, - {HOP4X(9), -wrap_len - 3., HOP4X(9), PIP_Y(id_N830) - 3.}, - {HOP4X(9), PIP_Y(id_N834) - 3., WIRE_X(0), PIP_Y(id_N834) - 3.}, - {HOP4X(9), PIP_Y(id_N830) - 3., HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 3.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist - 3., HOP4X(7), PIP_Y(id_N830) - 2.}, - {HOP4X(7), PIP_Y(id_N830) - 2., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 2.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(5), PIP_Y(id_N830) - 1.}, - {HOP4X(5), PIP_Y(id_N830) - 1., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 1.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 1., HOP4X(3), PIP_Y(id_N830) - 0.}, - {HOP4X(3), PIP_Y(id_N830) - 0., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 0.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 0., HOP4X(1), PIP_Y(id_N838) + 1.}, - {HOP4X(1), PIP_Y(id_N838) + 1., WIRE_X(0), PIP_Y(id_N838) + 1.}}}, - {id_S83_loop4, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, - {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, - {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, - {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), -wrap_len - 4.}, - {HOP4X(6), -wrap_len - 4., HOP4X(7), -wrap_len - 4.}, - {HOP4X(7), -wrap_len - 4., HOP4X(7), PIP_Y(id_N830) - 4.}, - {HOP4X(7), PIP_Y(id_N830) - 4., HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 4.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist - 4., HOP4X(5), PIP_Y(id_N830) - 3.}, - {HOP4X(5), PIP_Y(id_N830) - 3., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 3.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 3., HOP4X(3), PIP_Y(id_N830) - 2.}, - {HOP4X(3), PIP_Y(id_N830) - 2., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 2.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 2., HOP4X(1), PIP_Y(id_N838) - 1.}, - {HOP4X(1), PIP_Y(id_N838) - 1., WIRE_X(0), PIP_Y(id_N838) - 1.}}}, - {id_S83_loop5, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, - {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, - {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, - {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, - {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), -wrap_len - 5.}, - {HOP4X(4), -wrap_len - 5., HOP4X(5), -wrap_len - 5.}, - {HOP4X(5), -wrap_len - 5., HOP4X(5), PIP_Y(id_N830) - 5.}, - {HOP4X(5), PIP_Y(id_N830) - 5., HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 5.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist - 5., HOP4X(3), PIP_Y(id_N830) - 4.}, - {HOP4X(3), PIP_Y(id_N830) - 4., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 4.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 4., HOP4X(1), PIP_Y(id_N838) - 3.}, - {HOP4X(1), PIP_Y(id_N838) - 3., WIRE_X(0), PIP_Y(id_N838) - 3.}}}, - {id_S83_loop6, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, - {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, - {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, - {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, - {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, - {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), -wrap_len - 6.}, - {HOP4X(2), -wrap_len - 6., HOP4X(3), -wrap_len - 6.}, - {HOP4X(3), -wrap_len - 6., HOP4X(3), PIP_Y(id_N830) - 6.}, - {HOP4X(3), PIP_Y(id_N830) - 6., HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 6.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist - 6., HOP4X(1), PIP_Y(id_N838) - 5.}, - {HOP4X(1), PIP_Y(id_N838) - 5., WIRE_X(0), PIP_Y(id_N838) - 5.}}}, - {id_S83_loop7, - {{WIRE_X(0), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_S830)}, - {HOP4X(16), PIP_Y(id_S830), HOP4X(16), PIP_Y(id_N838)}, - {HOP4X(16), PIP_Y(id_N838) - 0., HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0.}, - {HOP4X(14), PIP_Y(id_N838) - left_wire_dist - 0., HOP4X(14), PIP_Y(id_N838) - 1.}, - {HOP4X(14), PIP_Y(id_N838) - 1., HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(12), PIP_Y(id_N838) - 2.}, - {HOP4X(12), PIP_Y(id_N838) - 2., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(10), PIP_Y(id_N838) - 3.}, - {HOP4X(10), PIP_Y(id_N838) - 3., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(8), PIP_Y(id_N838) - 4.}, - {HOP4X(8), PIP_Y(id_S834) - 4., WIRE_X(0), PIP_Y(id_S834) - 4.}, - {HOP4X(8), PIP_Y(id_N838) - 4., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(6), PIP_Y(id_N838) - 5.}, - {HOP4X(6), PIP_Y(id_N838) - 5., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(4), PIP_Y(id_N838) - 6.}, - {HOP4X(4), PIP_Y(id_N838) - 6., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(2), PIP_Y(id_N838) - 7.}, - {HOP4X(2), PIP_Y(id_N838) - 7., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 7., HOP4X(0), -wrap_len - 7.}, - {HOP4X(0), -wrap_len - 7., HOP4X(1), -wrap_len - 7.}, - {HOP4X(1), -wrap_len - 7., HOP4X(1), PIP_Y(id_N838) - 7.}, - {HOP4X(1), PIP_Y(id_N838) - 7., WIRE_X(0), PIP_Y(id_N838) - 7.}}}, - {id_N83_loop0, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), wrap_len + 1.}, - {HOP4X(15), wrap_len + 1., HOP4X(14), wrap_len + 1.}, - {HOP4X(14), wrap_len + 1., HOP4X(14), PIP_Y(id_N838) + 0.}, - {HOP4X(14), PIP_Y(id_N838) + 0., HOP4X(12), PIP_Y(id_N838) - left_wire_dist + 0.}, - {HOP4X(12), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(12), PIP_Y(id_N838) - 1.}, - {HOP4X(12), PIP_Y(id_N838) - 1., HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(10), PIP_Y(id_N838) - 2.}, - {HOP4X(10), PIP_Y(id_N838) - 2., HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(8), PIP_Y(id_N838) - 3.}, - {HOP4X(8), PIP_Y(id_S834) - 3., WIRE_X(0), PIP_Y(id_S834) - 3.}, - {HOP4X(8), PIP_Y(id_N838) - 3., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(6), PIP_Y(id_N838) - 4.}, - {HOP4X(6), PIP_Y(id_N838) - 4., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(4), PIP_Y(id_N838) - 5.}, - {HOP4X(4), PIP_Y(id_N838) - 5., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 5.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 5., HOP4X(2), PIP_Y(id_N838) - 6.}, - {HOP4X(2), PIP_Y(id_N838) - 6., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 6.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 6., HOP4X(0), PIP_Y(id_S838) - 7.}, - {HOP4X(0), PIP_Y(id_S838) - 7., WIRE_X(0), PIP_Y(id_S838) - 7.}}}, - {id_N83_loop1, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), wrap_len + 2.}, - {HOP4X(13), wrap_len + 2., HOP4X(12), wrap_len + 2.}, - {HOP4X(12), wrap_len + 2., HOP4X(12), PIP_Y(id_N838) + 1.}, - {HOP4X(12), PIP_Y(id_N838) + 1., HOP4X(10), PIP_Y(id_N838) - left_wire_dist + 1.}, - {HOP4X(10), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(10), PIP_Y(id_N838) + 0.}, - {HOP4X(10), PIP_Y(id_N838) + 0., HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 0.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(8), PIP_Y(id_N838) - 1.}, - {HOP4X(8), PIP_Y(id_S834) - 1., WIRE_X(0), PIP_Y(id_S834) - 1.}, - {HOP4X(8), PIP_Y(id_N838) - 1., HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(6), PIP_Y(id_N838) - 2.}, - {HOP4X(6), PIP_Y(id_N838) - 2., HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(4), PIP_Y(id_N838) - 3.}, - {HOP4X(4), PIP_Y(id_N838) - 3., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 3.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 3., HOP4X(2), PIP_Y(id_N838) - 4.}, - {HOP4X(2), PIP_Y(id_N838) - 4., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 4.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 4., HOP4X(0), PIP_Y(id_S838) - 5.}, - {HOP4X(0), PIP_Y(id_S838) - 5., WIRE_X(0), PIP_Y(id_S838) - 5.}}}, - {id_N83_loop2, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), wrap_len + 3.}, - {HOP4X(11), wrap_len + 3., HOP4X(10), wrap_len + 3.}, - {HOP4X(10), wrap_len + 3., HOP4X(10), PIP_Y(id_N838) + 2.}, - {HOP4X(10), PIP_Y(id_N838) + 2., HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 2.}, - {HOP4X(8), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(8), PIP_Y(id_N838) + 1.}, - {HOP4X(8), PIP_Y(id_S834) + 1., WIRE_X(0), PIP_Y(id_S834) + 1.}, - {HOP4X(8), PIP_Y(id_N838) + 1., HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 1.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(6), PIP_Y(id_N838) + 0.}, - {HOP4X(6), PIP_Y(id_N838) + 0., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 0.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(4), PIP_Y(id_N838) - 1.}, - {HOP4X(4), PIP_Y(id_N838) - 1., HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 1.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist - 1., HOP4X(2), PIP_Y(id_N838) - 2.}, - {HOP4X(2), PIP_Y(id_N838) - 2., HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 2.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist - 2., HOP4X(0), PIP_Y(id_S838) - 3.}, - {HOP4X(0), PIP_Y(id_S838) - 3., WIRE_X(0), PIP_Y(id_S838) - 3.}}}, - {id_N83_loop3, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, - {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), wrap_len + 4.}, - {HOP4X(9), wrap_len + 4., HOP4X(8), wrap_len + 4.}, - {HOP4X(8), wrap_len + 4., HOP4X(8), PIP_Y(id_N838) + 3.}, - {HOP4X(8), PIP_Y(id_S834) + 3., WIRE_X(0), PIP_Y(id_S834) + 3.}, - {HOP4X(8), PIP_Y(id_N838) + 3., HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 3.}, - {HOP4X(6), PIP_Y(id_N838) - left_wire_dist + 3., HOP4X(6), PIP_Y(id_N838) + 2.}, - {HOP4X(6), PIP_Y(id_N838) + 2., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 2.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(4), PIP_Y(id_N838) + 1.}, - {HOP4X(4), PIP_Y(id_N838) + 1., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 1.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 1., HOP4X(2), PIP_Y(id_N838) + 0.}, - {HOP4X(2), PIP_Y(id_N838) + 0., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 0.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 0., HOP4X(0), PIP_Y(id_S838) - 1.}, - {HOP4X(0), PIP_Y(id_S838) - 1., WIRE_X(0), PIP_Y(id_S838) - 1.}}}, - {id_N83_loop4, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, - {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, - {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, - {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), wrap_len + 5.}, - {HOP4X(7), wrap_len + 5., HOP4X(6), wrap_len + 5.}, - {HOP4X(6), wrap_len + 5., HOP4X(6), PIP_Y(id_N838) + 4.}, - {HOP4X(6), PIP_Y(id_N838) + 4., HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 4.}, - {HOP4X(4), PIP_Y(id_N838) - left_wire_dist + 4., HOP4X(4), PIP_Y(id_N838) + 3.}, - {HOP4X(4), PIP_Y(id_N838) + 3., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 3.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 3., HOP4X(2), PIP_Y(id_N838) + 2.}, - {HOP4X(2), PIP_Y(id_N838) + 2., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 2.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 2., HOP4X(0), PIP_Y(id_S838) + 1.}, - {HOP4X(0), PIP_Y(id_S838) + 1., WIRE_X(0), PIP_Y(id_S838) + 1.}}}, - {id_N83_loop5, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, - {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, - {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, - {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, - {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), wrap_len + 6.}, - {HOP4X(5), wrap_len + 6., HOP4X(4), wrap_len + 6.}, - {HOP4X(4), wrap_len + 6., HOP4X(4), PIP_Y(id_N838) + 5.}, - {HOP4X(4), PIP_Y(id_N838) + 5., HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 5.}, - {HOP4X(2), PIP_Y(id_N838) - left_wire_dist + 5., HOP4X(2), PIP_Y(id_N838) + 4.}, - {HOP4X(2), PIP_Y(id_N838) + 4., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 4.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 4., HOP4X(0), PIP_Y(id_S838) + 3.}, - {HOP4X(0), PIP_Y(id_S838) + 3., WIRE_X(0), PIP_Y(id_S838) + 3.}}}, - {id_N83_loop6, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, - {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, - {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, - {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, - {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, - {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), wrap_len + 7.}, - {HOP4X(3), wrap_len + 7., HOP4X(2), wrap_len + 7.}, - {HOP4X(2), wrap_len + 7., HOP4X(2), PIP_Y(id_N838) + 6.}, - {HOP4X(2), PIP_Y(id_N838) + 6., HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 6.}, - {HOP4X(0), PIP_Y(id_N838) - left_wire_dist + 6., HOP4X(0), PIP_Y(id_S838) + 5.}, - {HOP4X(0), PIP_Y(id_S838) + 5., WIRE_X(0), PIP_Y(id_S838) + 5.}}}, - {id_N83_loop7, - {{WIRE_X(0), PIP_Y(id_N830), HOP4X(17), PIP_Y(id_N830)}, - {HOP4X(17), PIP_Y(id_N830) + 0., HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0.}, - {HOP4X(15), PIP_Y(id_N830) + left_wire_dist + 0., HOP4X(15), PIP_Y(id_N830) + 1.}, - {HOP4X(15), PIP_Y(id_N830) + 1., HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1.}, - {HOP4X(13), PIP_Y(id_N830) + left_wire_dist + 1., HOP4X(13), PIP_Y(id_N830) + 2.}, - {HOP4X(13), PIP_Y(id_N830) + 2., HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2.}, - {HOP4X(11), PIP_Y(id_N830) + left_wire_dist + 2., HOP4X(11), PIP_Y(id_N830) + 3.}, - {HOP4X(11), PIP_Y(id_N830) + 3., HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3.}, - {HOP4X(9), PIP_Y(id_N830) + left_wire_dist + 3., HOP4X(9), PIP_Y(id_N830) + 4.}, - {HOP4X(9), PIP_Y(id_N834) + 4., WIRE_X(0), PIP_Y(id_N834) + 4.}, - {HOP4X(9), PIP_Y(id_N830) + 4., HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4.}, - {HOP4X(7), PIP_Y(id_N830) + left_wire_dist + 4., HOP4X(7), PIP_Y(id_N830) + 5.}, - {HOP4X(7), PIP_Y(id_N830) + 5., HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5.}, - {HOP4X(5), PIP_Y(id_N830) + left_wire_dist + 5., HOP4X(5), PIP_Y(id_N830) + 6.}, - {HOP4X(5), PIP_Y(id_N830) + 6., HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6.}, - {HOP4X(3), PIP_Y(id_N830) + left_wire_dist + 6., HOP4X(3), PIP_Y(id_N830) + 7.}, - {HOP4X(3), PIP_Y(id_N830) + 7., HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7.}, - {HOP4X(1), PIP_Y(id_N830) + left_wire_dist + 7., HOP4X(1), wrap_len + 8.}, - {HOP4X(1), wrap_len + 8., HOP4X(0), wrap_len + 8.}, - {HOP4X(0), wrap_len + 8., HOP4X(0), PIP_Y(id_S838) + 7.}, - {HOP4X(0), PIP_Y(id_S838) + 7., WIRE_X(0), PIP_Y(id_S838) + 7.}}}, - -#define PIP_X(pip_id) (pipPoint.at(pip_id).second) -#define WIRE_Y(offset) (cru_y + cru_h + ((float)offset) * ew_dist) - // 1 hop - {id_E10, - {{PIP_X(id_E100), WIRE_Y(0), PIP_X(id_E100), WIRE_Y(1)}, - {PIP_X(id_E100), WIRE_Y(1), PIP_X(id_E101) + 1., WIRE_Y(1)}, - {PIP_X(id_E101) + 1., WIRE_Y(1), PIP_X(id_E101) + 1., WIRE_Y(0)}}}, - {id_W10, - {{PIP_X(id_W100), WIRE_Y(0), PIP_X(id_W100), WIRE_Y(2)}, - {PIP_X(id_W100), WIRE_Y(2), PIP_X(id_W101) - 1., WIRE_Y(2)}, - {PIP_X(id_W101) - 1., WIRE_Y(2), PIP_X(id_W101) - 1., WIRE_Y(0)}}}, - {id_E10_loop0, - {{PIP_X(id_E100), WIRE_Y(0), PIP_X(id_E100), WIRE_Y(1)}, - {PIP_X(id_E100), WIRE_Y(1), 1. + wrap_len, WIRE_Y(1)}, - {1. + wrap_len, WIRE_Y(1), 1. + wrap_len, WIRE_Y(2)}, - {1. + wrap_len, WIRE_Y(2), PIP_X(id_W101), WIRE_Y(2)}, - {PIP_X(id_W101), WIRE_Y(2), PIP_X(id_W101), WIRE_Y(0)}}}, - {id_W10_loop0, - {{PIP_X(id_W100), WIRE_Y(0), PIP_X(id_W100), WIRE_Y(2)}, - {PIP_X(id_W100), WIRE_Y(2), -1. * wrap_len, WIRE_Y(2)}, - {-1. * wrap_len, WIRE_Y(2), -1. * wrap_len, WIRE_Y(1)}, - {-1. * wrap_len, WIRE_Y(1), PIP_X(id_E101), WIRE_Y(1)}, - {PIP_X(id_E101), WIRE_Y(1), PIP_X(id_E101), WIRE_Y(0)}}}, - {id_E13, - {{PIP_X(id_E130), WIRE_Y(0), PIP_X(id_E130), WIRE_Y(3)}, - {PIP_X(id_E130), WIRE_Y(3), PIP_X(id_E131) + 1., WIRE_Y(3)}, - {PIP_X(id_E131) + 1., WIRE_Y(3), PIP_X(id_E131) + 1., WIRE_Y(0)}}}, - {id_W13, - {{PIP_X(id_W130), WIRE_Y(0), PIP_X(id_W130), WIRE_Y(4)}, - {PIP_X(id_W130), WIRE_Y(4), PIP_X(id_W131) - 1., WIRE_Y(4)}, - {PIP_X(id_W131) - 1., WIRE_Y(4), PIP_X(id_W131) - 1., WIRE_Y(0)}}}, - {id_E13_loop0, - {{PIP_X(id_E130), WIRE_Y(0), PIP_X(id_E130), WIRE_Y(3)}, - {PIP_X(id_E130), WIRE_Y(3), 1. + wrap_len, WIRE_Y(3)}, - {1. + wrap_len, WIRE_Y(3), 1. + wrap_len, WIRE_Y(4)}, - {1. + wrap_len, WIRE_Y(4), PIP_X(id_W131), WIRE_Y(4)}, - {PIP_X(id_W131), WIRE_Y(4), PIP_X(id_W131), WIRE_Y(0)}}}, - {id_W13_loop0, - {{PIP_X(id_W130), WIRE_Y(0), PIP_X(id_W130), WIRE_Y(4)}, - {PIP_X(id_W130), WIRE_Y(4), -1. * wrap_len, WIRE_Y(4)}, - {-1. * wrap_len, WIRE_Y(4), -1. * wrap_len, WIRE_Y(3)}, - {-1. * wrap_len, WIRE_Y(3), PIP_X(id_E131), WIRE_Y(3)}, - {PIP_X(id_E131), WIRE_Y(3), PIP_X(id_E131), WIRE_Y(0)}}}, - // 1 hop EW - {id_EW10, - {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, - {PIP_X(id_EW10), WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(6)}, - {PIP_X(id_E111) + 1., WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(0)}, - {PIP_X(id_EW10), WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(5)}, - {PIP_X(id_W111) - 1., WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(0)}}}, - {id_EW10_loop_e, - {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, - {PIP_X(id_EW10), WIRE_Y(6), wrap_len + 1., WIRE_Y(6)}, - {wrap_len + 1., WIRE_Y(6), wrap_len + 1., WIRE_Y(5)}, - {wrap_len + 1., WIRE_Y(5), PIP_X(id_W111), WIRE_Y(5)}, - {PIP_X(id_W111), WIRE_Y(5), PIP_X(id_W111), WIRE_Y(0)}, - {PIP_X(id_EW10), WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(5)}, - {PIP_X(id_W111) - 1., WIRE_Y(5), PIP_X(id_W111) - 1., WIRE_Y(0)}}}, - {id_EW10_loop_w, - {{PIP_X(id_EW10), WIRE_Y(0), PIP_X(id_EW10), WIRE_Y(6)}, - {PIP_X(id_EW10), WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(6)}, - {PIP_X(id_E111) + 1., WIRE_Y(6), PIP_X(id_E111) + 1., WIRE_Y(0)}, - {PIP_X(id_EW10), WIRE_Y(5), -wrap_len, WIRE_Y(5)}, - {-wrap_len, WIRE_Y(5), -wrap_len, WIRE_Y(6)}, - {-wrap_len, WIRE_Y(6), PIP_X(id_E111), WIRE_Y(6)}, - {PIP_X(id_E111), WIRE_Y(6), PIP_X(id_E111), WIRE_Y(0)}}}, - {id_EW20, - {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, - {PIP_X(id_EW20), WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(8)}, - {PIP_X(id_E121) + 1., WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(0)}, - {PIP_X(id_EW20), WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(7)}, - {PIP_X(id_W121) - 1., WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(0)}}}, - {id_EW20_loop_e, - {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, - {PIP_X(id_EW20), WIRE_Y(8), wrap_len + 1., WIRE_Y(8)}, - {wrap_len + 1., WIRE_Y(8), wrap_len + 1., WIRE_Y(7)}, - {wrap_len + 1., WIRE_Y(7), PIP_X(id_W121), WIRE_Y(7)}, - {PIP_X(id_W121), WIRE_Y(7), PIP_X(id_W121), WIRE_Y(0)}, - {PIP_X(id_EW20), WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(7)}, - {PIP_X(id_W121) - 1., WIRE_Y(7), PIP_X(id_W121) - 1., WIRE_Y(0)}}}, - {id_EW20_loop_w, - {{PIP_X(id_EW20), WIRE_Y(0), PIP_X(id_EW20), WIRE_Y(8)}, - {PIP_X(id_EW20), WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(8)}, - {PIP_X(id_E121) + 1., WIRE_Y(8), PIP_X(id_E121) + 1., WIRE_Y(0)}, - {PIP_X(id_EW20), WIRE_Y(7), -wrap_len, WIRE_Y(7)}, - {-wrap_len, WIRE_Y(7), -wrap_len, WIRE_Y(8)}, - {-wrap_len, WIRE_Y(8), PIP_X(id_E121), WIRE_Y(8)}, - {PIP_X(id_E121), WIRE_Y(8), PIP_X(id_E121), WIRE_Y(0)}}}, -// 2 hop -#define HOP2Y(offset) WIRE_Y(offset + 9) - {id_E20, - {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, - {PIP_X(id_E200), HOP2Y(2), PIP_X(id_E201) + 1., HOP2Y(2)}, - {PIP_X(id_E201) + 1., HOP2Y(2), PIP_X(id_E201) + 1., WIRE_Y(0)}, - {PIP_X(id_E201) + 1., HOP2Y(0), PIP_X(id_E202) + 2., HOP2Y(0)}, - {PIP_X(id_E202) + 2., HOP2Y(0), PIP_X(id_E202) + 2., WIRE_Y(0)}}}, - {id_W20, - {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, - {PIP_X(id_W200), HOP2Y(3), PIP_X(id_W201) - 1., HOP2Y(3)}, - {PIP_X(id_W201) - 1., HOP2Y(3), PIP_X(id_W201) - 1., WIRE_Y(0)}, - {PIP_X(id_W201) - 1., HOP2Y(1), PIP_X(id_W202) - 2., HOP2Y(1)}, - {PIP_X(id_W202) - 2., HOP2Y(1), PIP_X(id_W202) - 2., WIRE_Y(0)}}}, - {id_E20_loop0, - {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, - {PIP_X(id_E200), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W201), HOP2Y(3)}, - {PIP_X(id_W201), HOP2Y(3), PIP_X(id_W201), WIRE_Y(0)}, - {PIP_X(id_W201), HOP2Y(1), PIP_X(id_W202) - 1., HOP2Y(1)}, - {PIP_X(id_W202) - 1., HOP2Y(1), PIP_X(id_W202) - 1., WIRE_Y(0)}}}, - {id_W20_loop0, - {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, - {PIP_X(id_W200), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E201), HOP2Y(2)}, - {PIP_X(id_E201), HOP2Y(2), PIP_X(id_E201), WIRE_Y(0)}, - {PIP_X(id_E201), HOP2Y(0), PIP_X(id_E202) + 1., HOP2Y(0)}, - {PIP_X(id_E202) + 1., HOP2Y(0), PIP_X(id_E202) + 1., WIRE_Y(0)}}}, - {id_E20_loop1, - {{PIP_X(id_E200), WIRE_Y(0), PIP_X(id_E200), HOP2Y(2)}, - {PIP_X(id_E200), HOP2Y(2), PIP_X(id_E201) + 1., HOP2Y(2)}, - {PIP_X(id_E201) + 1., HOP2Y(2), PIP_X(id_E201) + 1., WIRE_Y(0)}, - {PIP_X(id_E201) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W202) + 1., HOP2Y(1)}, - {PIP_X(id_W202) + 1., HOP2Y(1), PIP_X(id_W202) + 1., WIRE_Y(0)}}}, - {id_W20_loop1, - {{PIP_X(id_W200), WIRE_Y(0), PIP_X(id_W200), HOP2Y(3)}, - {PIP_X(id_W200), HOP2Y(3), PIP_X(id_W201) - 1., HOP2Y(3)}, - {PIP_X(id_W201) - 1., HOP2Y(3), PIP_X(id_W201) - 1., WIRE_Y(0)}, - {PIP_X(id_W201) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E202) - 1., HOP2Y(0)}, - {PIP_X(id_E202) - 1., HOP2Y(0), PIP_X(id_E202) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 1) - {id_E21, - {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, - {PIP_X(id_E210), HOP2Y(2), PIP_X(id_E211) + 1., HOP2Y(2)}, - {PIP_X(id_E211) + 1., HOP2Y(2), PIP_X(id_E211) + 1., WIRE_Y(0)}, - {PIP_X(id_E211) + 1., HOP2Y(0), PIP_X(id_E212) + 2., HOP2Y(0)}, - {PIP_X(id_E212) + 2., HOP2Y(0), PIP_X(id_E212) + 2., WIRE_Y(0)}}}, - {id_W21, - {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, - {PIP_X(id_W210), HOP2Y(3), PIP_X(id_W211) - 1., HOP2Y(3)}, - {PIP_X(id_W211) - 1., HOP2Y(3), PIP_X(id_W211) - 1., WIRE_Y(0)}, - {PIP_X(id_W211) - 1., HOP2Y(1), PIP_X(id_W212) - 2., HOP2Y(1)}, - {PIP_X(id_W212) - 2., HOP2Y(1), PIP_X(id_W212) - 2., WIRE_Y(0)}}}, - {id_E21_loop0, - {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, - {PIP_X(id_E210), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W211), HOP2Y(3)}, - {PIP_X(id_W211), HOP2Y(3), PIP_X(id_W211), WIRE_Y(0)}, - {PIP_X(id_W211), HOP2Y(1), PIP_X(id_W212) - 1., HOP2Y(1)}, - {PIP_X(id_W212) - 1., HOP2Y(1), PIP_X(id_W212) - 1., WIRE_Y(0)}}}, - {id_W21_loop0, - {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, - {PIP_X(id_W210), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E211), HOP2Y(2)}, - {PIP_X(id_E211), HOP2Y(2), PIP_X(id_E211), WIRE_Y(0)}, - {PIP_X(id_E211), HOP2Y(0), PIP_X(id_E212) + 1., HOP2Y(0)}, - {PIP_X(id_E212) + 1., HOP2Y(0), PIP_X(id_E212) + 1., WIRE_Y(0)}}}, - {id_E21_loop1, - {{PIP_X(id_E210), WIRE_Y(0), PIP_X(id_E210), HOP2Y(2)}, - {PIP_X(id_E210), HOP2Y(2), PIP_X(id_E211) + 1., HOP2Y(2)}, - {PIP_X(id_E211) + 1., HOP2Y(2), PIP_X(id_E211) + 1., WIRE_Y(0)}, - {PIP_X(id_E211) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W212) + 1., HOP2Y(1)}, - {PIP_X(id_W212) + 1., HOP2Y(1), PIP_X(id_W212) + 1., WIRE_Y(0)}}}, - {id_W21_loop1, - {{PIP_X(id_W210), WIRE_Y(0), PIP_X(id_W210), HOP2Y(3)}, - {PIP_X(id_W210), HOP2Y(3), PIP_X(id_W211) - 1., HOP2Y(3)}, - {PIP_X(id_W211) - 1., HOP2Y(3), PIP_X(id_W211) - 1., WIRE_Y(0)}, - {PIP_X(id_W211) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E212) - 1., HOP2Y(0)}, - {PIP_X(id_E212) - 1., HOP2Y(0), PIP_X(id_E212) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 2) - {id_E22, - {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, - {PIP_X(id_E220), HOP2Y(2), PIP_X(id_E221) + 1., HOP2Y(2)}, - {PIP_X(id_E221) + 1., HOP2Y(2), PIP_X(id_E221) + 1., WIRE_Y(0)}, - {PIP_X(id_E221) + 1., HOP2Y(0), PIP_X(id_E222) + 2., HOP2Y(0)}, - {PIP_X(id_E222) + 2., HOP2Y(0), PIP_X(id_E222) + 2., WIRE_Y(0)}}}, - {id_W22, - {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, - {PIP_X(id_W220), HOP2Y(3), PIP_X(id_W221) - 1., HOP2Y(3)}, - {PIP_X(id_W221) - 1., HOP2Y(3), PIP_X(id_W221) - 1., WIRE_Y(0)}, - {PIP_X(id_W221) - 1., HOP2Y(1), PIP_X(id_W222) - 2., HOP2Y(1)}, - {PIP_X(id_W222) - 2., HOP2Y(1), PIP_X(id_W222) - 2., WIRE_Y(0)}}}, - {id_E22_loop0, - {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, - {PIP_X(id_E220), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W221), HOP2Y(3)}, - {PIP_X(id_W221), HOP2Y(3), PIP_X(id_W221), WIRE_Y(0)}, - {PIP_X(id_W221), HOP2Y(1), PIP_X(id_W222) - 1., HOP2Y(1)}, - {PIP_X(id_W222) - 1., HOP2Y(1), PIP_X(id_W222) - 1., WIRE_Y(0)}}}, - {id_W22_loop0, - {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, - {PIP_X(id_W220), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E221), HOP2Y(2)}, - {PIP_X(id_E221), HOP2Y(2), PIP_X(id_E221), WIRE_Y(0)}, - {PIP_X(id_E221), HOP2Y(0), PIP_X(id_E222) + 1., HOP2Y(0)}, - {PIP_X(id_E222) + 1., HOP2Y(0), PIP_X(id_E222) + 1., WIRE_Y(0)}}}, - {id_E22_loop1, - {{PIP_X(id_E220), WIRE_Y(0), PIP_X(id_E220), HOP2Y(2)}, - {PIP_X(id_E220), HOP2Y(2), PIP_X(id_E221) + 1., HOP2Y(2)}, - {PIP_X(id_E221) + 1., HOP2Y(2), PIP_X(id_E221) + 1., WIRE_Y(0)}, - {PIP_X(id_E221) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W222) + 1., HOP2Y(1)}, - {PIP_X(id_W222) + 1., HOP2Y(1), PIP_X(id_W222) + 1., WIRE_Y(0)}}}, - {id_W22_loop1, - {{PIP_X(id_W220), WIRE_Y(0), PIP_X(id_W220), HOP2Y(3)}, - {PIP_X(id_W220), HOP2Y(3), PIP_X(id_W221) - 1., HOP2Y(3)}, - {PIP_X(id_W221) - 1., HOP2Y(3), PIP_X(id_W221) - 1., WIRE_Y(0)}, - {PIP_X(id_W221) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E222) - 1., HOP2Y(0)}, - {PIP_X(id_E222) - 1., HOP2Y(0), PIP_X(id_E222) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 3) - {id_E23, - {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, - {PIP_X(id_E230), HOP2Y(2), PIP_X(id_E231) + 1., HOP2Y(2)}, - {PIP_X(id_E231) + 1., HOP2Y(2), PIP_X(id_E231) + 1., WIRE_Y(0)}, - {PIP_X(id_E231) + 1., HOP2Y(0), PIP_X(id_E232) + 2., HOP2Y(0)}, - {PIP_X(id_E232) + 2., HOP2Y(0), PIP_X(id_E232) + 2., WIRE_Y(0)}}}, - {id_W23, - {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, - {PIP_X(id_W230), HOP2Y(3), PIP_X(id_W231) - 1., HOP2Y(3)}, - {PIP_X(id_W231) - 1., HOP2Y(3), PIP_X(id_W231) - 1., WIRE_Y(0)}, - {PIP_X(id_W231) - 1., HOP2Y(1), PIP_X(id_W232) - 2., HOP2Y(1)}, - {PIP_X(id_W232) - 2., HOP2Y(1), PIP_X(id_W232) - 2., WIRE_Y(0)}}}, - {id_E23_loop0, - {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, - {PIP_X(id_E230), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W231), HOP2Y(3)}, - {PIP_X(id_W231), HOP2Y(3), PIP_X(id_W231), WIRE_Y(0)}, - {PIP_X(id_W231), HOP2Y(1), PIP_X(id_W232) - 1., HOP2Y(1)}, - {PIP_X(id_W232) - 1., HOP2Y(1), PIP_X(id_W232) - 1., WIRE_Y(0)}}}, - {id_W23_loop0, - {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, - {PIP_X(id_W230), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E231), HOP2Y(2)}, - {PIP_X(id_E231), HOP2Y(2), PIP_X(id_E231), WIRE_Y(0)}, - {PIP_X(id_E231), HOP2Y(0), PIP_X(id_E232) + 1., HOP2Y(0)}, - {PIP_X(id_E232) + 1., HOP2Y(0), PIP_X(id_E232) + 1., WIRE_Y(0)}}}, - {id_E23_loop1, - {{PIP_X(id_E230), WIRE_Y(0), PIP_X(id_E230), HOP2Y(2)}, - {PIP_X(id_E230), HOP2Y(2), PIP_X(id_E231) + 1., HOP2Y(2)}, - {PIP_X(id_E231) + 1., HOP2Y(2), PIP_X(id_E231) + 1., WIRE_Y(0)}, - {PIP_X(id_E231) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W232) + 1., HOP2Y(1)}, - {PIP_X(id_W232) + 1., HOP2Y(1), PIP_X(id_W232) + 1., WIRE_Y(0)}}}, - {id_W23_loop1, - {{PIP_X(id_W230), WIRE_Y(0), PIP_X(id_W230), HOP2Y(3)}, - {PIP_X(id_W230), HOP2Y(3), PIP_X(id_W231) - 1., HOP2Y(3)}, - {PIP_X(id_W231) - 1., HOP2Y(3), PIP_X(id_W231) - 1., WIRE_Y(0)}, - {PIP_X(id_W231) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E232) - 1., HOP2Y(0)}, - {PIP_X(id_E232) - 1., HOP2Y(0), PIP_X(id_E232) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 4) - {id_E24, - {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, - {PIP_X(id_E240), HOP2Y(2), PIP_X(id_E241) + 1., HOP2Y(2)}, - {PIP_X(id_E241) + 1., HOP2Y(2), PIP_X(id_E241) + 1., WIRE_Y(0)}, - {PIP_X(id_E241) + 1., HOP2Y(0), PIP_X(id_E242) + 2., HOP2Y(0)}, - {PIP_X(id_E242) + 2., HOP2Y(0), PIP_X(id_E242) + 2., WIRE_Y(0)}}}, - {id_W24, - {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, - {PIP_X(id_W240), HOP2Y(3), PIP_X(id_W241) - 1., HOP2Y(3)}, - {PIP_X(id_W241) - 1., HOP2Y(3), PIP_X(id_W241) - 1., WIRE_Y(0)}, - {PIP_X(id_W241) - 1., HOP2Y(1), PIP_X(id_W242) - 2., HOP2Y(1)}, - {PIP_X(id_W242) - 2., HOP2Y(1), PIP_X(id_W242) - 2., WIRE_Y(0)}}}, - {id_E24_loop0, - {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, - {PIP_X(id_E240), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W241), HOP2Y(3)}, - {PIP_X(id_W241), HOP2Y(3), PIP_X(id_W241), WIRE_Y(0)}, - {PIP_X(id_W241), HOP2Y(1), PIP_X(id_W242) - 1., HOP2Y(1)}, - {PIP_X(id_W242) - 1., HOP2Y(1), PIP_X(id_W242) - 1., WIRE_Y(0)}}}, - {id_W24_loop0, - {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, - {PIP_X(id_W240), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E241), HOP2Y(2)}, - {PIP_X(id_E241), HOP2Y(2), PIP_X(id_E241), WIRE_Y(0)}, - {PIP_X(id_E241), HOP2Y(0), PIP_X(id_E242) + 1., HOP2Y(0)}, - {PIP_X(id_E242) + 1., HOP2Y(0), PIP_X(id_E242) + 1., WIRE_Y(0)}}}, - {id_E24_loop1, - {{PIP_X(id_E240), WIRE_Y(0), PIP_X(id_E240), HOP2Y(2)}, - {PIP_X(id_E240), HOP2Y(2), PIP_X(id_E241) + 1., HOP2Y(2)}, - {PIP_X(id_E241) + 1., HOP2Y(2), PIP_X(id_E241) + 1., WIRE_Y(0)}, - {PIP_X(id_E241) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W242) + 1., HOP2Y(1)}, - {PIP_X(id_W242) + 1., HOP2Y(1), PIP_X(id_W242) + 1., WIRE_Y(0)}}}, - {id_W24_loop1, - {{PIP_X(id_W240), WIRE_Y(0), PIP_X(id_W240), HOP2Y(3)}, - {PIP_X(id_W240), HOP2Y(3), PIP_X(id_W241) - 1., HOP2Y(3)}, - {PIP_X(id_W241) - 1., HOP2Y(3), PIP_X(id_W241) - 1., WIRE_Y(0)}, - {PIP_X(id_W241) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E242) - 1., HOP2Y(0)}, - {PIP_X(id_E242) - 1., HOP2Y(0), PIP_X(id_E242) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 5) - {id_E25, - {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, - {PIP_X(id_E250), HOP2Y(2), PIP_X(id_E251) + 1., HOP2Y(2)}, - {PIP_X(id_E251) + 1., HOP2Y(2), PIP_X(id_E251) + 1., WIRE_Y(0)}, - {PIP_X(id_E251) + 1., HOP2Y(0), PIP_X(id_E252) + 2., HOP2Y(0)}, - {PIP_X(id_E252) + 2., HOP2Y(0), PIP_X(id_E252) + 2., WIRE_Y(0)}}}, - {id_W25, - {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, - {PIP_X(id_W250), HOP2Y(3), PIP_X(id_W251) - 1., HOP2Y(3)}, - {PIP_X(id_W251) - 1., HOP2Y(3), PIP_X(id_W251) - 1., WIRE_Y(0)}, - {PIP_X(id_W251) - 1., HOP2Y(1), PIP_X(id_W252) - 2., HOP2Y(1)}, - {PIP_X(id_W252) - 2., HOP2Y(1), PIP_X(id_W252) - 2., WIRE_Y(0)}}}, - {id_E25_loop0, - {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, - {PIP_X(id_E250), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W251), HOP2Y(3)}, - {PIP_X(id_W251), HOP2Y(3), PIP_X(id_W251), WIRE_Y(0)}, - {PIP_X(id_W251), HOP2Y(1), PIP_X(id_W252) - 1., HOP2Y(1)}, - {PIP_X(id_W252) - 1., HOP2Y(1), PIP_X(id_W252) - 1., WIRE_Y(0)}}}, - {id_W25_loop0, - {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, - {PIP_X(id_W250), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E251), HOP2Y(2)}, - {PIP_X(id_E251), HOP2Y(2), PIP_X(id_E251), WIRE_Y(0)}, - {PIP_X(id_E251), HOP2Y(0), PIP_X(id_E252) + 1., HOP2Y(0)}, - {PIP_X(id_E252) + 1., HOP2Y(0), PIP_X(id_E252) + 1., WIRE_Y(0)}}}, - {id_E25_loop1, - {{PIP_X(id_E250), WIRE_Y(0), PIP_X(id_E250), HOP2Y(2)}, - {PIP_X(id_E250), HOP2Y(2), PIP_X(id_E251) + 1., HOP2Y(2)}, - {PIP_X(id_E251) + 1., HOP2Y(2), PIP_X(id_E251) + 1., WIRE_Y(0)}, - {PIP_X(id_E251) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W252) + 1., HOP2Y(1)}, - {PIP_X(id_W252) + 1., HOP2Y(1), PIP_X(id_W252) + 1., WIRE_Y(0)}}}, - {id_W25_loop1, - {{PIP_X(id_W250), WIRE_Y(0), PIP_X(id_W250), HOP2Y(3)}, - {PIP_X(id_W250), HOP2Y(3), PIP_X(id_W251) - 1., HOP2Y(3)}, - {PIP_X(id_W251) - 1., HOP2Y(3), PIP_X(id_W251) - 1., WIRE_Y(0)}, - {PIP_X(id_W251) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E252) - 1., HOP2Y(0)}, - {PIP_X(id_E252) - 1., HOP2Y(0), PIP_X(id_E252) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 6) - {id_E26, - {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, - {PIP_X(id_E260), HOP2Y(2), PIP_X(id_E261) + 1., HOP2Y(2)}, - {PIP_X(id_E261) + 1., HOP2Y(2), PIP_X(id_E261) + 1., WIRE_Y(0)}, - {PIP_X(id_E261) + 1., HOP2Y(0), PIP_X(id_E262) + 2., HOP2Y(0)}, - {PIP_X(id_E262) + 2., HOP2Y(0), PIP_X(id_E262) + 2., WIRE_Y(0)}}}, - {id_W26, - {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, - {PIP_X(id_W260), HOP2Y(3), PIP_X(id_W261) - 1., HOP2Y(3)}, - {PIP_X(id_W261) - 1., HOP2Y(3), PIP_X(id_W261) - 1., WIRE_Y(0)}, - {PIP_X(id_W261) - 1., HOP2Y(1), PIP_X(id_W262) - 2., HOP2Y(1)}, - {PIP_X(id_W262) - 2., HOP2Y(1), PIP_X(id_W262) - 2., WIRE_Y(0)}}}, - {id_E26_loop0, - {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, - {PIP_X(id_E260), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W261), HOP2Y(3)}, - {PIP_X(id_W261), HOP2Y(3), PIP_X(id_W261), WIRE_Y(0)}, - {PIP_X(id_W261), HOP2Y(1), PIP_X(id_W262) - 1., HOP2Y(1)}, - {PIP_X(id_W262) - 1., HOP2Y(1), PIP_X(id_W262) - 1., WIRE_Y(0)}}}, - {id_W26_loop0, - {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, - {PIP_X(id_W260), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E261), HOP2Y(2)}, - {PIP_X(id_E261), HOP2Y(2), PIP_X(id_E261), WIRE_Y(0)}, - {PIP_X(id_E261), HOP2Y(0), PIP_X(id_E262) + 1., HOP2Y(0)}, - {PIP_X(id_E262) + 1., HOP2Y(0), PIP_X(id_E262) + 1., WIRE_Y(0)}}}, - {id_E26_loop1, - {{PIP_X(id_E260), WIRE_Y(0), PIP_X(id_E260), HOP2Y(2)}, - {PIP_X(id_E260), HOP2Y(2), PIP_X(id_E261) + 1., HOP2Y(2)}, - {PIP_X(id_E261) + 1., HOP2Y(2), PIP_X(id_E261) + 1., WIRE_Y(0)}, - {PIP_X(id_E261) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W262) + 1., HOP2Y(1)}, - {PIP_X(id_W262) + 1., HOP2Y(1), PIP_X(id_W262) + 1., WIRE_Y(0)}}}, - {id_W26_loop1, - {{PIP_X(id_W260), WIRE_Y(0), PIP_X(id_W260), HOP2Y(3)}, - {PIP_X(id_W260), HOP2Y(3), PIP_X(id_W261) - 1., HOP2Y(3)}, - {PIP_X(id_W261) - 1., HOP2Y(3), PIP_X(id_W261) - 1., WIRE_Y(0)}, - {PIP_X(id_W261) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E262) - 1., HOP2Y(0)}, - {PIP_X(id_E262) - 1., HOP2Y(0), PIP_X(id_E262) - 1., WIRE_Y(0)}}}, - -#undef HOP2Y -#define HOP2Y(offset) WIRE_Y(offset + 9 + 4 * 7) - {id_E27, - {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, - {PIP_X(id_E270), HOP2Y(2), PIP_X(id_E271) + 1., HOP2Y(2)}, - {PIP_X(id_E271) + 1., HOP2Y(2), PIP_X(id_E271) + 1., WIRE_Y(0)}, - {PIP_X(id_E271) + 1., HOP2Y(0), PIP_X(id_E272) + 2., HOP2Y(0)}, - {PIP_X(id_E272) + 2., HOP2Y(0), PIP_X(id_E272) + 2., WIRE_Y(0)}}}, - {id_W27, - {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, - {PIP_X(id_W270), HOP2Y(3), PIP_X(id_W271) - 1., HOP2Y(3)}, - {PIP_X(id_W271) - 1., HOP2Y(3), PIP_X(id_W271) - 1., WIRE_Y(0)}, - {PIP_X(id_W271) - 1., HOP2Y(1), PIP_X(id_W272) - 2., HOP2Y(1)}, - {PIP_X(id_W272) - 2., HOP2Y(1), PIP_X(id_W272) - 2., WIRE_Y(0)}}}, - {id_E27_loop0, - {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, - {PIP_X(id_E270), HOP2Y(2), wrap_len + 1., HOP2Y(2)}, - {wrap_len + 1., HOP2Y(2), wrap_len + 1., HOP2Y(3)}, - {wrap_len + 1., HOP2Y(3), PIP_X(id_W271), HOP2Y(3)}, - {PIP_X(id_W271), HOP2Y(3), PIP_X(id_W271), WIRE_Y(0)}, - {PIP_X(id_W271), HOP2Y(1), PIP_X(id_W272) - 1., HOP2Y(1)}, - {PIP_X(id_W272) - 1., HOP2Y(1), PIP_X(id_W272) - 1., WIRE_Y(0)}}}, - {id_W27_loop0, - {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, - {PIP_X(id_W270), HOP2Y(3), -wrap_len, HOP2Y(3)}, - {-wrap_len, HOP2Y(3), -wrap_len, HOP2Y(2)}, - {-wrap_len, HOP2Y(2), PIP_X(id_E271), HOP2Y(2)}, - {PIP_X(id_E271), HOP2Y(2), PIP_X(id_E271), WIRE_Y(0)}, - {PIP_X(id_E271), HOP2Y(0), PIP_X(id_E272) + 1., HOP2Y(0)}, - {PIP_X(id_E272) + 1., HOP2Y(0), PIP_X(id_E272) + 1., WIRE_Y(0)}}}, - {id_E27_loop1, - {{PIP_X(id_E270), WIRE_Y(0), PIP_X(id_E270), HOP2Y(2)}, - {PIP_X(id_E270), HOP2Y(2), PIP_X(id_E271) + 1., HOP2Y(2)}, - {PIP_X(id_E271) + 1., HOP2Y(2), PIP_X(id_E271) + 1., WIRE_Y(0)}, - {PIP_X(id_E271) + 1., HOP2Y(0), wrap_len + 2., HOP2Y(0)}, - {wrap_len + 2., HOP2Y(0), wrap_len + 2., HOP2Y(1)}, - {wrap_len + 2., HOP2Y(1), PIP_X(id_W272) + 1., HOP2Y(1)}, - {PIP_X(id_W272) + 1., HOP2Y(1), PIP_X(id_W272) + 1., WIRE_Y(0)}}}, - {id_W27_loop1, - {{PIP_X(id_W270), WIRE_Y(0), PIP_X(id_W270), HOP2Y(3)}, - {PIP_X(id_W270), HOP2Y(3), PIP_X(id_W271) - 1., HOP2Y(3)}, - {PIP_X(id_W271) - 1., HOP2Y(3), PIP_X(id_W271) - 1., WIRE_Y(0)}, - {PIP_X(id_W271) - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(1)}, - {-wrap_len - 1., HOP2Y(1), -wrap_len - 1., HOP2Y(0)}, - {-wrap_len - 1., HOP2Y(0), PIP_X(id_E272) - 1., HOP2Y(0)}, - {PIP_X(id_E272) - 1., HOP2Y(0), PIP_X(id_E272) - 1., WIRE_Y(0)}}}, - -// clock branches -#define CLK_GBO0_Y 41.f -#define CLK_GBO1_Y 46.f -// 4 hop -#define HOP4Y_START (CLK_GBO0_Y + 10.f) -#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START) - {id_E80, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, - {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, - {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, - {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, - {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W808) + 7., HOP4Y(2)}, - {PIP_X(id_W808) + 7., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E808) + 8., HOP4Y(0)}, - {PIP_X(id_E808) + 8, HOP4Y(0), PIP_X(id_E808) + 8., WIRE_Y(0)}}}, - {id_W80, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, - {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, - {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, - {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, - {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W800) - 7., HOP4Y(3)}, - {PIP_X(id_W800) - 7., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W808) - 8., HOP4Y(1)}, - {PIP_X(id_W808) - 8, HOP4Y(1), PIP_X(id_W808) - 8., WIRE_Y(0)}}}, - {id_E80_loop0, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, - {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, - {wrap_len + 1., HOP4Y(15), PIP_X(id_W800) - 0., HOP4Y(15)}, - {PIP_X(id_W800) - 0., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W800) - 1., HOP4Y(13)}, - {PIP_X(id_W800) - 1., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W800) - 2., HOP4Y(11)}, - {PIP_X(id_W800) - 2., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W800) - 3., HOP4Y(9)}, - {PIP_X(id_W800) - 3., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(7)}, - {PIP_X(id_W804) - 3., HOP4Y(9), PIP_X(id_W804) - 3., WIRE_Y(0)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W800) - 4., HOP4Y(7)}, - {PIP_X(id_W800) - 4., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W800) - 5., HOP4Y(5)}, - {PIP_X(id_W800) - 5., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W800) - 6., HOP4Y(3)}, - {PIP_X(id_W800) - 6., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W808) - 7., HOP4Y(1)}, - {PIP_X(id_W808) - 7, HOP4Y(1), PIP_X(id_W808) - 7., WIRE_Y(0)}}}, - {id_E80_loop1, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, - {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, - {wrap_len + 2., HOP4Y(13), PIP_X(id_W800) + 1., HOP4Y(13)}, - {PIP_X(id_W800) + 1., HOP4Y(13), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W800) - 0., HOP4Y(11)}, - {PIP_X(id_W800) - 0., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W800) - 1., HOP4Y(9)}, - {PIP_X(id_W804) - 1., HOP4Y(9), PIP_X(id_W804) - 1., WIRE_Y(0)}, - {PIP_X(id_W800) - 1., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(7)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W800) - 2., HOP4Y(7)}, - {PIP_X(id_W800) - 2., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W800) - 3., HOP4Y(5)}, - {PIP_X(id_W800) - 3., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W800) - 4., HOP4Y(3)}, - {PIP_X(id_W800) - 4., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W808) - 5., HOP4Y(1)}, - {PIP_X(id_W808) - 5., HOP4Y(1), PIP_X(id_W808) - 5., WIRE_Y(0)}}}, - {id_E80_loop2, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, - {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, - {wrap_len + 3., HOP4Y(11), PIP_X(id_W800) + 2., HOP4Y(11)}, - {PIP_X(id_W800) + 2., HOP4Y(11), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W800) + 1., HOP4Y(9)}, - {PIP_X(id_W804) + 1., HOP4Y(9), PIP_X(id_W804) + 1., WIRE_Y(0)}, - {PIP_X(id_W800) + 1., HOP4Y(9), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(7)}, - {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W800) + 0., HOP4Y(7)}, - {PIP_X(id_W800) + 0., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W800) - 1., HOP4Y(5)}, - {PIP_X(id_W800) - 1., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W800) - 2., HOP4Y(3)}, - {PIP_X(id_W800) - 2., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W808) - 3., HOP4Y(1)}, - {PIP_X(id_W808) - 3., HOP4Y(1), PIP_X(id_W808) - 3., WIRE_Y(0)}}}, - {id_E80_loop3, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, - {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, - {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, - {wrap_len + 4., HOP4Y(9), PIP_X(id_W800) + 3., HOP4Y(9)}, - {PIP_X(id_W804) + 3., HOP4Y(9), PIP_X(id_W804) + 3., WIRE_Y(0)}, - {PIP_X(id_W800) + 3., HOP4Y(9), PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(7)}, - {PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W800) + 2., HOP4Y(7)}, - {PIP_X(id_W800) + 2., HOP4Y(7), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W800) + 1., HOP4Y(5)}, - {PIP_X(id_W800) + 1., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W800) - 0., HOP4Y(3)}, - {PIP_X(id_W800) - 0., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W808) - 1., HOP4Y(1)}, - {PIP_X(id_W808) - 1., HOP4Y(1), PIP_X(id_W808) - 1., WIRE_Y(0)}}}, - {id_E80_loop4, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, - {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, - {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, - {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, - {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, - {wrap_len + 5., HOP4Y(7), PIP_X(id_W800) + 4., HOP4Y(7)}, - {PIP_X(id_W800) + 4., HOP4Y(7), PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W800) + 3., HOP4Y(5)}, - {PIP_X(id_W800) + 3., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W800) + 2., HOP4Y(3)}, - {PIP_X(id_W800) + 2., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W808) + 1., HOP4Y(1)}, - {PIP_X(id_W808) + 1., HOP4Y(1), PIP_X(id_W808) + 1., WIRE_Y(0)}}}, - {id_E80_loop5, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, - {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, - {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, - {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, - {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, - {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, - {wrap_len + 6., HOP4Y(5), PIP_X(id_W800) + 5., HOP4Y(5)}, - {PIP_X(id_W800) + 5., HOP4Y(5), PIP_X(id_W800) - top_wire_dist + 5., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W800) + 4., HOP4Y(3)}, - {PIP_X(id_W800) + 4., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W808) + 3., HOP4Y(1)}, - {PIP_X(id_W808) + 3., HOP4Y(1), PIP_X(id_W808) + 3., WIRE_Y(0)}}}, - {id_E80_loop6, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, - {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, - {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, - {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, - {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, - {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, - {wrap_len + 7., HOP4Y(3), PIP_X(id_W800) + 6., HOP4Y(3)}, - {PIP_X(id_W800) + 6., HOP4Y(3), PIP_X(id_W800) - top_wire_dist + 6., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W808) + 5., HOP4Y(1)}, - {PIP_X(id_W808) + 5., HOP4Y(1), PIP_X(id_W808) + 5., WIRE_Y(0)}}}, - {id_E80_loop7, - {{PIP_X(id_E800), WIRE_Y(0), PIP_X(id_E800), HOP4Y(16)}, - {PIP_X(id_E800), HOP4Y(16), PIP_X(id_W808), HOP4Y(16)}, - {PIP_X(id_W808) + 0., HOP4Y(16), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W808) + 1., HOP4Y(14)}, - {PIP_X(id_W808) + 1., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W808) + 2., HOP4Y(12)}, - {PIP_X(id_W808) + 2., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W808) + 3., HOP4Y(10)}, - {PIP_X(id_W808) + 3., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W808) + 4., HOP4Y(8)}, - {PIP_X(id_W808) + 4., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E804) + 4., HOP4Y(8), PIP_X(id_E804) + 4., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W808) + 5., HOP4Y(6)}, - {PIP_X(id_W808) + 5., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W808) + 6., HOP4Y(4)}, - {PIP_X(id_W808) + 6., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W808) + 7., HOP4Y(2)}, - {PIP_X(id_W808) + 7., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, - {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, - {wrap_len + 8., HOP4Y(1), PIP_X(id_W808) + 7., HOP4Y(1)}, - {PIP_X(id_W808) + 7., HOP4Y(1), PIP_X(id_W808) + 7., WIRE_Y(0)}}}, - {id_W80_loop0, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, - {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, - {-wrap_len - 0., HOP4Y(14), PIP_X(id_W808) + 0., HOP4Y(14)}, - {PIP_X(id_W808) + 0., HOP4Y(14), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(12)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W808) + 1., HOP4Y(12)}, - {PIP_X(id_W808) + 1., HOP4Y(12), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W808) + 2., HOP4Y(10)}, - {PIP_X(id_W808) + 2., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W808) + 3., HOP4Y(8)}, - {PIP_X(id_W808) + 3., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(6)}, - {PIP_X(id_E804) + 3., HOP4Y(8), PIP_X(id_E804) + 3., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W808) + 4., HOP4Y(6)}, - {PIP_X(id_W808) + 4., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W808) + 5., HOP4Y(4)}, - {PIP_X(id_W808) + 5., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W808) + 6., HOP4Y(2)}, - {PIP_X(id_W808) + 6., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E808) + 7., HOP4Y(0)}, - {PIP_X(id_E808) + 7., HOP4Y(0), PIP_X(id_E808) + 7., WIRE_Y(0)}}}, - {id_W80_loop1, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, - {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, - {-wrap_len - 1., HOP4Y(12), PIP_X(id_W808) - 1., HOP4Y(12)}, - {PIP_X(id_W808) - 1., HOP4Y(12), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(10)}, - {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W808) + 0., HOP4Y(10)}, - {PIP_X(id_W808) + 0., HOP4Y(10), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W808) + 1., HOP4Y(8)}, - {PIP_X(id_W808) + 1., HOP4Y(8), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(6)}, - {PIP_X(id_E804) + 1., HOP4Y(8), PIP_X(id_E804) + 1., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W808) + 2., HOP4Y(6)}, - {PIP_X(id_W808) + 2., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W808) + 3., HOP4Y(4)}, - {PIP_X(id_W808) + 3., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W808) + 4., HOP4Y(2)}, - {PIP_X(id_W808) + 4., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E808) + 5., HOP4Y(0)}, - {PIP_X(id_E808) + 5., HOP4Y(0), PIP_X(id_E808) + 5., WIRE_Y(0)}}}, - {id_W80_loop2, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, - {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, - {-wrap_len - 2., HOP4Y(10), PIP_X(id_W808) - 2., HOP4Y(10)}, - {PIP_X(id_W808) - 2., HOP4Y(10), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(8)}, - {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W808) - 1., HOP4Y(8)}, - {PIP_X(id_W808) - 1., HOP4Y(8), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(6)}, - {PIP_X(id_E804) - 1., HOP4Y(8), PIP_X(id_E804) - 1., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W808) + 0., HOP4Y(6)}, - {PIP_X(id_W808) + 0., HOP4Y(6), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W808) + 1., HOP4Y(4)}, - {PIP_X(id_W808) + 1., HOP4Y(4), PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W808) + 2., HOP4Y(2)}, - {PIP_X(id_W808) + 2., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E808) + 3., HOP4Y(0)}, - {PIP_X(id_E808) + 3., HOP4Y(0), PIP_X(id_E808) + 3., WIRE_Y(0)}}}, - {id_W80_loop3, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, - {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, - {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, - {-wrap_len - 3., HOP4Y(8), PIP_X(id_W808) - 3., HOP4Y(8)}, - {PIP_X(id_W808) - 3., HOP4Y(8), PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(6)}, - {PIP_X(id_E804) - 3., HOP4Y(8), PIP_X(id_E804) - 3., WIRE_Y(0)}, - {PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W808) - 2., HOP4Y(6)}, - {PIP_X(id_W808) - 2., HOP4Y(6), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W808) - 1., HOP4Y(4)}, - {PIP_X(id_W808) - 1., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W808) + 0., HOP4Y(2)}, - {PIP_X(id_W808) + 0., HOP4Y(2), PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E808) + 1., HOP4Y(0)}, - {PIP_X(id_E808) + 1., HOP4Y(0), PIP_X(id_E808) + 1., WIRE_Y(0)}}}, - {id_W80_loop4, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, - {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, - {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, - {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, - {PIP_X(id_W804) - 4., HOP4Y(6), PIP_X(id_W804) - 4., WIRE_Y(0)}, - {-wrap_len - 4., HOP4Y(6), PIP_X(id_W808) - 4., HOP4Y(6)}, - {PIP_X(id_W808) - 4., HOP4Y(6), PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(4)}, - {PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W808) - 3., HOP4Y(4)}, - {PIP_X(id_W808) - 3., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W808) - 2., HOP4Y(2)}, - {PIP_X(id_W808) - 2., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E808) - 1., HOP4Y(0)}, - {PIP_X(id_E808) - 1., HOP4Y(0), PIP_X(id_E808) - 1., WIRE_Y(0)}}}, - {id_W80_loop5, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, - {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, - {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, - {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, - {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, - {-wrap_len - 5., HOP4Y(4), PIP_X(id_W808) - 5., HOP4Y(4)}, - {PIP_X(id_W808) - 5., HOP4Y(4), PIP_X(id_W808) + top_wire_dist - 5., HOP4Y(2)}, - {PIP_X(id_W808) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W808) - 4., HOP4Y(2)}, - {PIP_X(id_W808) - 4., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E808) - 3., HOP4Y(0)}, - {PIP_X(id_E808) - 3., HOP4Y(0), PIP_X(id_E808) - 3., WIRE_Y(0)}}}, - {id_W80_loop6, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, - {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, - {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, - {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, - {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, - {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, - {-wrap_len - 6., HOP4Y(2), PIP_X(id_W808) - 6., HOP4Y(2)}, - {PIP_X(id_W808) - 6., HOP4Y(2), PIP_X(id_W808) + top_wire_dist - 6., HOP4Y(0)}, - {PIP_X(id_W808) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E808) - 5., HOP4Y(0)}, - {PIP_X(id_E808) - 5., HOP4Y(0), PIP_X(id_E808) - 5., WIRE_Y(0)}}}, - {id_W80_loop7, - {{PIP_X(id_W800), WIRE_Y(0), PIP_X(id_W800), HOP4Y(17)}, - {PIP_X(id_W800) - 0., HOP4Y(17), PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W800) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W800) - 1., HOP4Y(15)}, - {PIP_X(id_W800) - 1., HOP4Y(15), PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W800) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W800) - 2., HOP4Y(13)}, - {PIP_X(id_W800) - 2., HOP4Y(13), PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W800) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W800) - 3., HOP4Y(11)}, - {PIP_X(id_W800) - 3., HOP4Y(11), PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W800) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W800) - 4., HOP4Y(9)}, - {PIP_X(id_W800) - 4., HOP4Y(9), PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W804) - 4., HOP4Y(9), PIP_X(id_W804) - 4., WIRE_Y(0)}, - {PIP_X(id_W800) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W800) - 5., HOP4Y(7)}, - {PIP_X(id_W800) - 5., HOP4Y(7), PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W800) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W800) - 6., HOP4Y(5)}, - {PIP_X(id_W800) - 6., HOP4Y(5), PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W800) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W800) - 7., HOP4Y(3)}, - {PIP_X(id_W800) - 7., HOP4Y(3), PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W800) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, - {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, - {-wrap_len - 7., HOP4Y(0), PIP_X(id_E808) - 7., HOP4Y(0)}, - {PIP_X(id_E808) - 7., HOP4Y(0), PIP_X(id_E808) - 7., WIRE_Y(0)}}}, - -#undef HOP4Y -#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f) - {id_E81, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, - {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, - {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, - {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, - {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W818) + 7., HOP4Y(2)}, - {PIP_X(id_W818) + 7., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E818) + 8., HOP4Y(0)}, - {PIP_X(id_E818) + 8, HOP4Y(0), PIP_X(id_E818) + 8., WIRE_Y(0)}}}, - {id_W81, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, - {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, - {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, - {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, - {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W810) - 7., HOP4Y(3)}, - {PIP_X(id_W810) - 7., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W818) - 8., HOP4Y(1)}, - {PIP_X(id_W818) - 8, HOP4Y(1), PIP_X(id_W818) - 8., WIRE_Y(0)}}}, - {id_E81_loop0, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, - {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, - {wrap_len + 1., HOP4Y(15), PIP_X(id_W810) - 0., HOP4Y(15)}, - {PIP_X(id_W810) - 0., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W810) - 1., HOP4Y(13)}, - {PIP_X(id_W810) - 1., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W810) - 2., HOP4Y(11)}, - {PIP_X(id_W810) - 2., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W810) - 3., HOP4Y(9)}, - {PIP_X(id_W810) - 3., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(7)}, - {PIP_X(id_W814) - 3., HOP4Y(9), PIP_X(id_W814) - 3., WIRE_Y(0)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W810) - 4., HOP4Y(7)}, - {PIP_X(id_W810) - 4., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W810) - 5., HOP4Y(5)}, - {PIP_X(id_W810) - 5., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W810) - 6., HOP4Y(3)}, - {PIP_X(id_W810) - 6., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W818) - 7., HOP4Y(1)}, - {PIP_X(id_W818) - 7, HOP4Y(1), PIP_X(id_W818) - 7., WIRE_Y(0)}}}, - {id_E81_loop1, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, - {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, - {wrap_len + 2., HOP4Y(13), PIP_X(id_W810) + 1., HOP4Y(13)}, - {PIP_X(id_W810) + 1., HOP4Y(13), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W810) - 0., HOP4Y(11)}, - {PIP_X(id_W810) - 0., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W810) - 1., HOP4Y(9)}, - {PIP_X(id_W814) - 1., HOP4Y(9), PIP_X(id_W814) - 1., WIRE_Y(0)}, - {PIP_X(id_W810) - 1., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(7)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W810) - 2., HOP4Y(7)}, - {PIP_X(id_W810) - 2., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W810) - 3., HOP4Y(5)}, - {PIP_X(id_W810) - 3., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W810) - 4., HOP4Y(3)}, - {PIP_X(id_W810) - 4., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W818) - 5., HOP4Y(1)}, - {PIP_X(id_W818) - 5., HOP4Y(1), PIP_X(id_W818) - 5., WIRE_Y(0)}}}, - {id_E81_loop2, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, - {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, - {wrap_len + 3., HOP4Y(11), PIP_X(id_W810) + 2., HOP4Y(11)}, - {PIP_X(id_W810) + 2., HOP4Y(11), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W810) + 1., HOP4Y(9)}, - {PIP_X(id_W814) + 1., HOP4Y(9), PIP_X(id_W814) + 1., WIRE_Y(0)}, - {PIP_X(id_W810) + 1., HOP4Y(9), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(7)}, - {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W810) + 0., HOP4Y(7)}, - {PIP_X(id_W810) + 0., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W810) - 1., HOP4Y(5)}, - {PIP_X(id_W810) - 1., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W810) - 2., HOP4Y(3)}, - {PIP_X(id_W810) - 2., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W818) - 3., HOP4Y(1)}, - {PIP_X(id_W818) - 3., HOP4Y(1), PIP_X(id_W818) - 3., WIRE_Y(0)}}}, - {id_E81_loop3, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, - {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, - {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, - {wrap_len + 4., HOP4Y(9), PIP_X(id_W810) + 3., HOP4Y(9)}, - {PIP_X(id_W814) + 3., HOP4Y(9), PIP_X(id_W814) + 3., WIRE_Y(0)}, - {PIP_X(id_W810) + 3., HOP4Y(9), PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(7)}, - {PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W810) + 2., HOP4Y(7)}, - {PIP_X(id_W810) + 2., HOP4Y(7), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W810) + 1., HOP4Y(5)}, - {PIP_X(id_W810) + 1., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W810) - 0., HOP4Y(3)}, - {PIP_X(id_W810) - 0., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W818) - 1., HOP4Y(1)}, - {PIP_X(id_W818) - 1., HOP4Y(1), PIP_X(id_W818) - 1., WIRE_Y(0)}}}, - {id_E81_loop4, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, - {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, - {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, - {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, - {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, - {wrap_len + 5., HOP4Y(7), PIP_X(id_W810) + 4., HOP4Y(7)}, - {PIP_X(id_W810) + 4., HOP4Y(7), PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W810) + 3., HOP4Y(5)}, - {PIP_X(id_W810) + 3., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W810) + 2., HOP4Y(3)}, - {PIP_X(id_W810) + 2., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W818) + 1., HOP4Y(1)}, - {PIP_X(id_W818) + 1., HOP4Y(1), PIP_X(id_W818) + 1., WIRE_Y(0)}}}, - {id_E81_loop5, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, - {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, - {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, - {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, - {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, - {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, - {wrap_len + 6., HOP4Y(5), PIP_X(id_W810) + 5., HOP4Y(5)}, - {PIP_X(id_W810) + 5., HOP4Y(5), PIP_X(id_W810) - top_wire_dist + 5., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W810) + 4., HOP4Y(3)}, - {PIP_X(id_W810) + 4., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W818) + 3., HOP4Y(1)}, - {PIP_X(id_W818) + 3., HOP4Y(1), PIP_X(id_W818) + 3., WIRE_Y(0)}}}, - {id_E81_loop6, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, - {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, - {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, - {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, - {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, - {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, - {wrap_len + 7., HOP4Y(3), PIP_X(id_W810) + 6., HOP4Y(3)}, - {PIP_X(id_W810) + 6., HOP4Y(3), PIP_X(id_W810) - top_wire_dist + 6., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W818) + 5., HOP4Y(1)}, - {PIP_X(id_W818) + 5., HOP4Y(1), PIP_X(id_W818) + 5., WIRE_Y(0)}}}, - {id_E81_loop7, - {{PIP_X(id_E810), WIRE_Y(0), PIP_X(id_E810), HOP4Y(16)}, - {PIP_X(id_E810), HOP4Y(16), PIP_X(id_W818), HOP4Y(16)}, - {PIP_X(id_W818) + 0., HOP4Y(16), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W818) + 1., HOP4Y(14)}, - {PIP_X(id_W818) + 1., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W818) + 2., HOP4Y(12)}, - {PIP_X(id_W818) + 2., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W818) + 3., HOP4Y(10)}, - {PIP_X(id_W818) + 3., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W818) + 4., HOP4Y(8)}, - {PIP_X(id_W818) + 4., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E814) + 4., HOP4Y(8), PIP_X(id_E814) + 4., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W818) + 5., HOP4Y(6)}, - {PIP_X(id_W818) + 5., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W818) + 6., HOP4Y(4)}, - {PIP_X(id_W818) + 6., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W818) + 7., HOP4Y(2)}, - {PIP_X(id_W818) + 7., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, - {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, - {wrap_len + 8., HOP4Y(1), PIP_X(id_W818) + 7., HOP4Y(1)}, - {PIP_X(id_W818) + 7., HOP4Y(1), PIP_X(id_W818) + 7., WIRE_Y(0)}}}, - {id_W81_loop0, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, - {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, - {-wrap_len - 0., HOP4Y(14), PIP_X(id_W818) + 0., HOP4Y(14)}, - {PIP_X(id_W818) + 0., HOP4Y(14), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(12)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W818) + 1., HOP4Y(12)}, - {PIP_X(id_W818) + 1., HOP4Y(12), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W818) + 2., HOP4Y(10)}, - {PIP_X(id_W818) + 2., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W818) + 3., HOP4Y(8)}, - {PIP_X(id_W818) + 3., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(6)}, - {PIP_X(id_E814) + 3., HOP4Y(8), PIP_X(id_E814) + 3., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W818) + 4., HOP4Y(6)}, - {PIP_X(id_W818) + 4., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W818) + 5., HOP4Y(4)}, - {PIP_X(id_W818) + 5., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W818) + 6., HOP4Y(2)}, - {PIP_X(id_W818) + 6., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E818) + 7., HOP4Y(0)}, - {PIP_X(id_E818) + 7., HOP4Y(0), PIP_X(id_E818) + 7., WIRE_Y(0)}}}, - {id_W81_loop1, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, - {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, - {-wrap_len - 1., HOP4Y(12), PIP_X(id_W818) - 1., HOP4Y(12)}, - {PIP_X(id_W818) - 1., HOP4Y(12), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(10)}, - {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W818) + 0., HOP4Y(10)}, - {PIP_X(id_W818) + 0., HOP4Y(10), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W818) + 1., HOP4Y(8)}, - {PIP_X(id_W818) + 1., HOP4Y(8), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(6)}, - {PIP_X(id_E814) + 1., HOP4Y(8), PIP_X(id_E814) + 1., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W818) + 2., HOP4Y(6)}, - {PIP_X(id_W818) + 2., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W818) + 3., HOP4Y(4)}, - {PIP_X(id_W818) + 3., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W818) + 4., HOP4Y(2)}, - {PIP_X(id_W818) + 4., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E818) + 5., HOP4Y(0)}, - {PIP_X(id_E818) + 5., HOP4Y(0), PIP_X(id_E818) + 5., WIRE_Y(0)}}}, - {id_W81_loop2, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, - {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, - {-wrap_len - 2., HOP4Y(10), PIP_X(id_W818) - 2., HOP4Y(10)}, - {PIP_X(id_W818) - 2., HOP4Y(10), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(8)}, - {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W818) - 1., HOP4Y(8)}, - {PIP_X(id_W818) - 1., HOP4Y(8), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(6)}, - {PIP_X(id_E814) - 1., HOP4Y(8), PIP_X(id_E814) - 1., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W818) + 0., HOP4Y(6)}, - {PIP_X(id_W818) + 0., HOP4Y(6), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W818) + 1., HOP4Y(4)}, - {PIP_X(id_W818) + 1., HOP4Y(4), PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W818) + 2., HOP4Y(2)}, - {PIP_X(id_W818) + 2., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E818) + 3., HOP4Y(0)}, - {PIP_X(id_E818) + 3., HOP4Y(0), PIP_X(id_E818) + 3., WIRE_Y(0)}}}, - {id_W81_loop3, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, - {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, - {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, - {-wrap_len - 3., HOP4Y(8), PIP_X(id_W818) - 3., HOP4Y(8)}, - {PIP_X(id_W818) - 3., HOP4Y(8), PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(6)}, - {PIP_X(id_E814) - 3., HOP4Y(8), PIP_X(id_E814) - 3., WIRE_Y(0)}, - {PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W818) - 2., HOP4Y(6)}, - {PIP_X(id_W818) - 2., HOP4Y(6), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W818) - 1., HOP4Y(4)}, - {PIP_X(id_W818) - 1., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W818) + 0., HOP4Y(2)}, - {PIP_X(id_W818) + 0., HOP4Y(2), PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E818) + 1., HOP4Y(0)}, - {PIP_X(id_E818) + 1., HOP4Y(0), PIP_X(id_E818) + 1., WIRE_Y(0)}}}, - {id_W81_loop4, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, - {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, - {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, - {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, - {PIP_X(id_W814) - 4., HOP4Y(6), PIP_X(id_W814) - 4., WIRE_Y(0)}, - {-wrap_len - 4., HOP4Y(6), PIP_X(id_W818) - 4., HOP4Y(6)}, - {PIP_X(id_W818) - 4., HOP4Y(6), PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(4)}, - {PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W818) - 3., HOP4Y(4)}, - {PIP_X(id_W818) - 3., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W818) - 2., HOP4Y(2)}, - {PIP_X(id_W818) - 2., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E818) - 1., HOP4Y(0)}, - {PIP_X(id_E818) - 1., HOP4Y(0), PIP_X(id_E818) - 1., WIRE_Y(0)}}}, - {id_W81_loop5, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, - {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, - {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, - {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, - {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, - {-wrap_len - 5., HOP4Y(4), PIP_X(id_W818) - 5., HOP4Y(4)}, - {PIP_X(id_W818) - 5., HOP4Y(4), PIP_X(id_W818) + top_wire_dist - 5., HOP4Y(2)}, - {PIP_X(id_W818) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W818) - 4., HOP4Y(2)}, - {PIP_X(id_W818) - 4., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E818) - 3., HOP4Y(0)}, - {PIP_X(id_E818) - 3., HOP4Y(0), PIP_X(id_E818) - 3., WIRE_Y(0)}}}, - {id_W81_loop6, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, - {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, - {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, - {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, - {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, - {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, - {-wrap_len - 6., HOP4Y(2), PIP_X(id_W818) - 6., HOP4Y(2)}, - {PIP_X(id_W818) - 6., HOP4Y(2), PIP_X(id_W818) + top_wire_dist - 6., HOP4Y(0)}, - {PIP_X(id_W818) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E818) - 5., HOP4Y(0)}, - {PIP_X(id_E818) - 5., HOP4Y(0), PIP_X(id_E818) - 5., WIRE_Y(0)}}}, - {id_W81_loop7, - {{PIP_X(id_W810), WIRE_Y(0), PIP_X(id_W810), HOP4Y(17)}, - {PIP_X(id_W810) - 0., HOP4Y(17), PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W810) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W810) - 1., HOP4Y(15)}, - {PIP_X(id_W810) - 1., HOP4Y(15), PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W810) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W810) - 2., HOP4Y(13)}, - {PIP_X(id_W810) - 2., HOP4Y(13), PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W810) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W810) - 3., HOP4Y(11)}, - {PIP_X(id_W810) - 3., HOP4Y(11), PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W810) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W810) - 4., HOP4Y(9)}, - {PIP_X(id_W810) - 4., HOP4Y(9), PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W814) - 4., HOP4Y(9), PIP_X(id_W814) - 4., WIRE_Y(0)}, - {PIP_X(id_W810) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W810) - 5., HOP4Y(7)}, - {PIP_X(id_W810) - 5., HOP4Y(7), PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W810) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W810) - 6., HOP4Y(5)}, - {PIP_X(id_W810) - 6., HOP4Y(5), PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W810) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W810) - 7., HOP4Y(3)}, - {PIP_X(id_W810) - 7., HOP4Y(3), PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W810) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, - {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, - {-wrap_len - 7., HOP4Y(0), PIP_X(id_E818) - 7., HOP4Y(0)}, - {PIP_X(id_E818) - 7., HOP4Y(0), PIP_X(id_E818) - 7., WIRE_Y(0)}}}, - -#undef HOP4Y -#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f + 18.f) - {id_E82, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, - {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, - {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, - {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, - {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W828) + 7., HOP4Y(2)}, - {PIP_X(id_W828) + 7., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E828) + 8., HOP4Y(0)}, - {PIP_X(id_E828) + 8, HOP4Y(0), PIP_X(id_E828) + 8., WIRE_Y(0)}}}, - {id_W82, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, - {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, - {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, - {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, - {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W820) - 7., HOP4Y(3)}, - {PIP_X(id_W820) - 7., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W828) - 8., HOP4Y(1)}, - {PIP_X(id_W828) - 8, HOP4Y(1), PIP_X(id_W828) - 8., WIRE_Y(0)}}}, - {id_E82_loop0, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, - {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, - {wrap_len + 1., HOP4Y(15), PIP_X(id_W820) - 0., HOP4Y(15)}, - {PIP_X(id_W820) - 0., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W820) - 1., HOP4Y(13)}, - {PIP_X(id_W820) - 1., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W820) - 2., HOP4Y(11)}, - {PIP_X(id_W820) - 2., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W820) - 3., HOP4Y(9)}, - {PIP_X(id_W820) - 3., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(7)}, - {PIP_X(id_W824) - 3., HOP4Y(9), PIP_X(id_W824) - 3., WIRE_Y(0)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W820) - 4., HOP4Y(7)}, - {PIP_X(id_W820) - 4., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W820) - 5., HOP4Y(5)}, - {PIP_X(id_W820) - 5., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W820) - 6., HOP4Y(3)}, - {PIP_X(id_W820) - 6., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W828) - 7., HOP4Y(1)}, - {PIP_X(id_W828) - 7, HOP4Y(1), PIP_X(id_W828) - 7., WIRE_Y(0)}}}, - {id_E82_loop1, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, - {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, - {wrap_len + 2., HOP4Y(13), PIP_X(id_W820) + 1., HOP4Y(13)}, - {PIP_X(id_W820) + 1., HOP4Y(13), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W820) - 0., HOP4Y(11)}, - {PIP_X(id_W820) - 0., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W820) - 1., HOP4Y(9)}, - {PIP_X(id_W824) - 1., HOP4Y(9), PIP_X(id_W824) - 1., WIRE_Y(0)}, - {PIP_X(id_W820) - 1., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(7)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W820) - 2., HOP4Y(7)}, - {PIP_X(id_W820) - 2., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W820) - 3., HOP4Y(5)}, - {PIP_X(id_W820) - 3., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W820) - 4., HOP4Y(3)}, - {PIP_X(id_W820) - 4., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W828) - 5., HOP4Y(1)}, - {PIP_X(id_W828) - 5., HOP4Y(1), PIP_X(id_W828) - 5., WIRE_Y(0)}}}, - {id_E82_loop2, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, - {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, - {wrap_len + 3., HOP4Y(11), PIP_X(id_W820) + 2., HOP4Y(11)}, - {PIP_X(id_W820) + 2., HOP4Y(11), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W820) + 1., HOP4Y(9)}, - {PIP_X(id_W824) + 1., HOP4Y(9), PIP_X(id_W824) + 1., WIRE_Y(0)}, - {PIP_X(id_W820) + 1., HOP4Y(9), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(7)}, - {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W820) + 0., HOP4Y(7)}, - {PIP_X(id_W820) + 0., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W820) - 1., HOP4Y(5)}, - {PIP_X(id_W820) - 1., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W820) - 2., HOP4Y(3)}, - {PIP_X(id_W820) - 2., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W828) - 3., HOP4Y(1)}, - {PIP_X(id_W828) - 3., HOP4Y(1), PIP_X(id_W828) - 3., WIRE_Y(0)}}}, - {id_E82_loop3, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, - {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, - {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, - {wrap_len + 4., HOP4Y(9), PIP_X(id_W820) + 3., HOP4Y(9)}, - {PIP_X(id_W824) + 3., HOP4Y(9), PIP_X(id_W824) + 3., WIRE_Y(0)}, - {PIP_X(id_W820) + 3., HOP4Y(9), PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(7)}, - {PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W820) + 2., HOP4Y(7)}, - {PIP_X(id_W820) + 2., HOP4Y(7), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W820) + 1., HOP4Y(5)}, - {PIP_X(id_W820) + 1., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W820) - 0., HOP4Y(3)}, - {PIP_X(id_W820) - 0., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W828) - 1., HOP4Y(1)}, - {PIP_X(id_W828) - 1., HOP4Y(1), PIP_X(id_W828) - 1., WIRE_Y(0)}}}, - {id_E82_loop4, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, - {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, - {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, - {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, - {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, - {wrap_len + 5., HOP4Y(7), PIP_X(id_W820) + 4., HOP4Y(7)}, - {PIP_X(id_W820) + 4., HOP4Y(7), PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W820) + 3., HOP4Y(5)}, - {PIP_X(id_W820) + 3., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W820) + 2., HOP4Y(3)}, - {PIP_X(id_W820) + 2., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W828) + 1., HOP4Y(1)}, - {PIP_X(id_W828) + 1., HOP4Y(1), PIP_X(id_W828) + 1., WIRE_Y(0)}}}, - {id_E82_loop5, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, - {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, - {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, - {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, - {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, - {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, - {wrap_len + 6., HOP4Y(5), PIP_X(id_W820) + 5., HOP4Y(5)}, - {PIP_X(id_W820) + 5., HOP4Y(5), PIP_X(id_W820) - top_wire_dist + 5., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W820) + 4., HOP4Y(3)}, - {PIP_X(id_W820) + 4., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W828) + 3., HOP4Y(1)}, - {PIP_X(id_W828) + 3., HOP4Y(1), PIP_X(id_W828) + 3., WIRE_Y(0)}}}, - {id_E82_loop6, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, - {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, - {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, - {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, - {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, - {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, - {wrap_len + 7., HOP4Y(3), PIP_X(id_W820) + 6., HOP4Y(3)}, - {PIP_X(id_W820) + 6., HOP4Y(3), PIP_X(id_W820) - top_wire_dist + 6., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W828) + 5., HOP4Y(1)}, - {PIP_X(id_W828) + 5., HOP4Y(1), PIP_X(id_W828) + 5., WIRE_Y(0)}}}, - {id_E82_loop7, - {{PIP_X(id_E820), WIRE_Y(0), PIP_X(id_E820), HOP4Y(16)}, - {PIP_X(id_E820), HOP4Y(16), PIP_X(id_W828), HOP4Y(16)}, - {PIP_X(id_W828) + 0., HOP4Y(16), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W828) + 1., HOP4Y(14)}, - {PIP_X(id_W828) + 1., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W828) + 2., HOP4Y(12)}, - {PIP_X(id_W828) + 2., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W828) + 3., HOP4Y(10)}, - {PIP_X(id_W828) + 3., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W828) + 4., HOP4Y(8)}, - {PIP_X(id_W828) + 4., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E824) + 4., HOP4Y(8), PIP_X(id_E824) + 4., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W828) + 5., HOP4Y(6)}, - {PIP_X(id_W828) + 5., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W828) + 6., HOP4Y(4)}, - {PIP_X(id_W828) + 6., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W828) + 7., HOP4Y(2)}, - {PIP_X(id_W828) + 7., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, - {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, - {wrap_len + 8., HOP4Y(1), PIP_X(id_W828) + 7., HOP4Y(1)}, - {PIP_X(id_W828) + 7., HOP4Y(1), PIP_X(id_W828) + 7., WIRE_Y(0)}}}, - {id_W82_loop0, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, - {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, - {-wrap_len - 0., HOP4Y(14), PIP_X(id_W828) + 0., HOP4Y(14)}, - {PIP_X(id_W828) + 0., HOP4Y(14), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(12)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W828) + 1., HOP4Y(12)}, - {PIP_X(id_W828) + 1., HOP4Y(12), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W828) + 2., HOP4Y(10)}, - {PIP_X(id_W828) + 2., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W828) + 3., HOP4Y(8)}, - {PIP_X(id_W828) + 3., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(6)}, - {PIP_X(id_E824) + 3., HOP4Y(8), PIP_X(id_E824) + 3., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W828) + 4., HOP4Y(6)}, - {PIP_X(id_W828) + 4., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W828) + 5., HOP4Y(4)}, - {PIP_X(id_W828) + 5., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W828) + 6., HOP4Y(2)}, - {PIP_X(id_W828) + 6., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E828) + 7., HOP4Y(0)}, - {PIP_X(id_E828) + 7., HOP4Y(0), PIP_X(id_E828) + 7., WIRE_Y(0)}}}, - {id_W82_loop1, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, - {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, - {-wrap_len - 1., HOP4Y(12), PIP_X(id_W828) - 1., HOP4Y(12)}, - {PIP_X(id_W828) - 1., HOP4Y(12), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(10)}, - {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W828) + 0., HOP4Y(10)}, - {PIP_X(id_W828) + 0., HOP4Y(10), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W828) + 1., HOP4Y(8)}, - {PIP_X(id_W828) + 1., HOP4Y(8), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(6)}, - {PIP_X(id_E824) + 1., HOP4Y(8), PIP_X(id_E824) + 1., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W828) + 2., HOP4Y(6)}, - {PIP_X(id_W828) + 2., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W828) + 3., HOP4Y(4)}, - {PIP_X(id_W828) + 3., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W828) + 4., HOP4Y(2)}, - {PIP_X(id_W828) + 4., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E828) + 5., HOP4Y(0)}, - {PIP_X(id_E828) + 5., HOP4Y(0), PIP_X(id_E828) + 5., WIRE_Y(0)}}}, - {id_W82_loop2, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, - {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, - {-wrap_len - 2., HOP4Y(10), PIP_X(id_W828) - 2., HOP4Y(10)}, - {PIP_X(id_W828) - 2., HOP4Y(10), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(8)}, - {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W828) - 1., HOP4Y(8)}, - {PIP_X(id_W828) - 1., HOP4Y(8), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(6)}, - {PIP_X(id_E824) - 1., HOP4Y(8), PIP_X(id_E824) - 1., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W828) + 0., HOP4Y(6)}, - {PIP_X(id_W828) + 0., HOP4Y(6), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W828) + 1., HOP4Y(4)}, - {PIP_X(id_W828) + 1., HOP4Y(4), PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W828) + 2., HOP4Y(2)}, - {PIP_X(id_W828) + 2., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E828) + 3., HOP4Y(0)}, - {PIP_X(id_E828) + 3., HOP4Y(0), PIP_X(id_E828) + 3., WIRE_Y(0)}}}, - {id_W82_loop3, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, - {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, - {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, - {-wrap_len - 3., HOP4Y(8), PIP_X(id_W828) - 3., HOP4Y(8)}, - {PIP_X(id_W828) - 3., HOP4Y(8), PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(6)}, - {PIP_X(id_E824) - 3., HOP4Y(8), PIP_X(id_E824) - 3., WIRE_Y(0)}, - {PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W828) - 2., HOP4Y(6)}, - {PIP_X(id_W828) - 2., HOP4Y(6), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W828) - 1., HOP4Y(4)}, - {PIP_X(id_W828) - 1., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W828) + 0., HOP4Y(2)}, - {PIP_X(id_W828) + 0., HOP4Y(2), PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E828) + 1., HOP4Y(0)}, - {PIP_X(id_E828) + 1., HOP4Y(0), PIP_X(id_E828) + 1., WIRE_Y(0)}}}, - {id_W82_loop4, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, - {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, - {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, - {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, - {PIP_X(id_W824) - 4., HOP4Y(6), PIP_X(id_W824) - 4., WIRE_Y(0)}, - {-wrap_len - 4., HOP4Y(6), PIP_X(id_W828) - 4., HOP4Y(6)}, - {PIP_X(id_W828) - 4., HOP4Y(6), PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(4)}, - {PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W828) - 3., HOP4Y(4)}, - {PIP_X(id_W828) - 3., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W828) - 2., HOP4Y(2)}, - {PIP_X(id_W828) - 2., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E828) - 1., HOP4Y(0)}, - {PIP_X(id_E828) - 1., HOP4Y(0), PIP_X(id_E828) - 1., WIRE_Y(0)}}}, - {id_W82_loop5, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, - {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, - {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, - {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, - {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, - {-wrap_len - 5., HOP4Y(4), PIP_X(id_W828) - 5., HOP4Y(4)}, - {PIP_X(id_W828) - 5., HOP4Y(4), PIP_X(id_W828) + top_wire_dist - 5., HOP4Y(2)}, - {PIP_X(id_W828) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W828) - 4., HOP4Y(2)}, - {PIP_X(id_W828) - 4., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E828) - 3., HOP4Y(0)}, - {PIP_X(id_E828) - 3., HOP4Y(0), PIP_X(id_E828) - 3., WIRE_Y(0)}}}, - {id_W82_loop6, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, - {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, - {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, - {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, - {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, - {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, - {-wrap_len - 6., HOP4Y(2), PIP_X(id_W828) - 6., HOP4Y(2)}, - {PIP_X(id_W828) - 6., HOP4Y(2), PIP_X(id_W828) + top_wire_dist - 6., HOP4Y(0)}, - {PIP_X(id_W828) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E828) - 5., HOP4Y(0)}, - {PIP_X(id_E828) - 5., HOP4Y(0), PIP_X(id_E828) - 5., WIRE_Y(0)}}}, - {id_W82_loop7, - {{PIP_X(id_W820), WIRE_Y(0), PIP_X(id_W820), HOP4Y(17)}, - {PIP_X(id_W820) - 0., HOP4Y(17), PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W820) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W820) - 1., HOP4Y(15)}, - {PIP_X(id_W820) - 1., HOP4Y(15), PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W820) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W820) - 2., HOP4Y(13)}, - {PIP_X(id_W820) - 2., HOP4Y(13), PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W820) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W820) - 3., HOP4Y(11)}, - {PIP_X(id_W820) - 3., HOP4Y(11), PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W820) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W820) - 4., HOP4Y(9)}, - {PIP_X(id_W820) - 4., HOP4Y(9), PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W824) - 4., HOP4Y(9), PIP_X(id_W824) - 4., WIRE_Y(0)}, - {PIP_X(id_W820) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W820) - 5., HOP4Y(7)}, - {PIP_X(id_W820) - 5., HOP4Y(7), PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W820) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W820) - 6., HOP4Y(5)}, - {PIP_X(id_W820) - 6., HOP4Y(5), PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W820) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W820) - 7., HOP4Y(3)}, - {PIP_X(id_W820) - 7., HOP4Y(3), PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W820) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, - {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, - {-wrap_len - 7., HOP4Y(0), PIP_X(id_E828) - 7., HOP4Y(0)}, - {PIP_X(id_E828) - 7., HOP4Y(0), PIP_X(id_E828) - 7., WIRE_Y(0)}}}, - -#undef HOP4Y -#define HOP4Y(offset) WIRE_Y((float)offset + HOP4Y_START + 18.f + 18.f + 18.f) - {id_E83, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, - {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, - {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, - {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, - {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W838) + 7., HOP4Y(2)}, - {PIP_X(id_W838) + 7., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0), PIP_X(id_E838) + 8., HOP4Y(0)}, - {PIP_X(id_E838) + 8, HOP4Y(0), PIP_X(id_E838) + 8., WIRE_Y(0)}}}, - {id_W83, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, - {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, - {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, - {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, - {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W830) - 7., HOP4Y(3)}, - {PIP_X(id_W830) - 7., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1), PIP_X(id_W838) - 8., HOP4Y(1)}, - {PIP_X(id_W838) - 8, HOP4Y(1), PIP_X(id_W838) - 8., WIRE_Y(0)}}}, - {id_E83_loop0, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), wrap_len + 1., HOP4Y(14)}, - {wrap_len + 1., HOP4Y(14), wrap_len + 1., HOP4Y(15)}, - {wrap_len + 1., HOP4Y(15), PIP_X(id_W830) - 0., HOP4Y(15)}, - {PIP_X(id_W830) - 0., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(13), PIP_X(id_W830) - 1., HOP4Y(13)}, - {PIP_X(id_W830) - 1., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(11), PIP_X(id_W830) - 2., HOP4Y(11)}, - {PIP_X(id_W830) - 2., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(9), PIP_X(id_W830) - 3., HOP4Y(9)}, - {PIP_X(id_W830) - 3., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(7)}, - {PIP_X(id_W834) - 3., HOP4Y(9), PIP_X(id_W834) - 3., WIRE_Y(0)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(7), PIP_X(id_W830) - 4., HOP4Y(7)}, - {PIP_X(id_W830) - 4., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(5), PIP_X(id_W830) - 5., HOP4Y(5)}, - {PIP_X(id_W830) - 5., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(3), PIP_X(id_W830) - 6., HOP4Y(3)}, - {PIP_X(id_W830) - 6., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(1), PIP_X(id_W838) - 7., HOP4Y(1)}, - {PIP_X(id_W838) - 7, HOP4Y(1), PIP_X(id_W838) - 7., WIRE_Y(0)}}}, - {id_E83_loop1, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), wrap_len + 2., HOP4Y(12)}, - {wrap_len + 2., HOP4Y(12), wrap_len + 2., HOP4Y(13)}, - {wrap_len + 2., HOP4Y(13), PIP_X(id_W830) + 1., HOP4Y(13)}, - {PIP_X(id_W830) + 1., HOP4Y(13), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(11), PIP_X(id_W830) - 0., HOP4Y(11)}, - {PIP_X(id_W830) - 0., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(9), PIP_X(id_W830) - 1., HOP4Y(9)}, - {PIP_X(id_W834) - 1., HOP4Y(9), PIP_X(id_W834) - 1., WIRE_Y(0)}, - {PIP_X(id_W830) - 1., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(7)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(7), PIP_X(id_W830) - 2., HOP4Y(7)}, - {PIP_X(id_W830) - 2., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(5), PIP_X(id_W830) - 3., HOP4Y(5)}, - {PIP_X(id_W830) - 3., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(3), PIP_X(id_W830) - 4., HOP4Y(3)}, - {PIP_X(id_W830) - 4., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(1), PIP_X(id_W838) - 5., HOP4Y(1)}, - {PIP_X(id_W838) - 5., HOP4Y(1), PIP_X(id_W838) - 5., WIRE_Y(0)}}}, - {id_E83_loop2, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), wrap_len + 3., HOP4Y(10)}, - {wrap_len + 3., HOP4Y(10), wrap_len + 3., HOP4Y(11)}, - {wrap_len + 3., HOP4Y(11), PIP_X(id_W830) + 2., HOP4Y(11)}, - {PIP_X(id_W830) + 2., HOP4Y(11), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(9), PIP_X(id_W830) + 1., HOP4Y(9)}, - {PIP_X(id_W834) + 1., HOP4Y(9), PIP_X(id_W834) + 1., WIRE_Y(0)}, - {PIP_X(id_W830) + 1., HOP4Y(9), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(7)}, - {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(7), PIP_X(id_W830) + 0., HOP4Y(7)}, - {PIP_X(id_W830) + 0., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(5), PIP_X(id_W830) - 1., HOP4Y(5)}, - {PIP_X(id_W830) - 1., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(3), PIP_X(id_W830) - 2., HOP4Y(3)}, - {PIP_X(id_W830) - 2., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(1), PIP_X(id_W838) - 3., HOP4Y(1)}, - {PIP_X(id_W838) - 3., HOP4Y(1), PIP_X(id_W838) - 3., WIRE_Y(0)}}}, - {id_E83_loop3, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, - {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), wrap_len + 4., HOP4Y(8)}, - {wrap_len + 4., HOP4Y(8), wrap_len + 4., HOP4Y(9)}, - {wrap_len + 4., HOP4Y(9), PIP_X(id_W830) + 3., HOP4Y(9)}, - {PIP_X(id_W834) + 3., HOP4Y(9), PIP_X(id_W834) + 3., WIRE_Y(0)}, - {PIP_X(id_W830) + 3., HOP4Y(9), PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(7)}, - {PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(7), PIP_X(id_W830) + 2., HOP4Y(7)}, - {PIP_X(id_W830) + 2., HOP4Y(7), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(5), PIP_X(id_W830) + 1., HOP4Y(5)}, - {PIP_X(id_W830) + 1., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist + 1., HOP4Y(3), PIP_X(id_W830) - 0., HOP4Y(3)}, - {PIP_X(id_W830) - 0., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(1), PIP_X(id_W838) - 1., HOP4Y(1)}, - {PIP_X(id_W838) - 1., HOP4Y(1), PIP_X(id_W838) - 1., WIRE_Y(0)}}}, - {id_E83_loop4, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, - {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, - {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, - {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), wrap_len + 5., HOP4Y(6)}, - {wrap_len + 5., HOP4Y(6), wrap_len + 5., HOP4Y(7)}, - {wrap_len + 5., HOP4Y(7), PIP_X(id_W830) + 4., HOP4Y(7)}, - {PIP_X(id_W830) + 4., HOP4Y(7), PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(5), PIP_X(id_W830) + 3., HOP4Y(5)}, - {PIP_X(id_W830) + 3., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist + 3., HOP4Y(3), PIP_X(id_W830) + 2., HOP4Y(3)}, - {PIP_X(id_W830) + 2., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist + 2., HOP4Y(1), PIP_X(id_W838) + 1., HOP4Y(1)}, - {PIP_X(id_W838) + 1., HOP4Y(1), PIP_X(id_W838) + 1., WIRE_Y(0)}}}, - {id_E83_loop5, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, - {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, - {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, - {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, - {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), wrap_len + 6., HOP4Y(4)}, - {wrap_len + 6., HOP4Y(4), wrap_len + 6., HOP4Y(5)}, - {wrap_len + 6., HOP4Y(5), PIP_X(id_W830) + 5., HOP4Y(5)}, - {PIP_X(id_W830) + 5., HOP4Y(5), PIP_X(id_W830) - top_wire_dist + 5., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist + 5., HOP4Y(3), PIP_X(id_W830) + 4., HOP4Y(3)}, - {PIP_X(id_W830) + 4., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist + 4., HOP4Y(1), PIP_X(id_W838) + 3., HOP4Y(1)}, - {PIP_X(id_W838) + 3., HOP4Y(1), PIP_X(id_W838) + 3., WIRE_Y(0)}}}, - {id_E83_loop6, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, - {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, - {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, - {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, - {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), wrap_len + 7., HOP4Y(2)}, - {wrap_len + 7., HOP4Y(2), wrap_len + 7., HOP4Y(3)}, - {wrap_len + 7., HOP4Y(3), PIP_X(id_W830) + 6., HOP4Y(3)}, - {PIP_X(id_W830) + 6., HOP4Y(3), PIP_X(id_W830) - top_wire_dist + 6., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist + 6., HOP4Y(1), PIP_X(id_W838) + 5., HOP4Y(1)}, - {PIP_X(id_W838) + 5., HOP4Y(1), PIP_X(id_W838) + 5., WIRE_Y(0)}}}, - {id_E83_loop7, - {{PIP_X(id_E830), WIRE_Y(0), PIP_X(id_E830), HOP4Y(16)}, - {PIP_X(id_E830), HOP4Y(16), PIP_X(id_W838), HOP4Y(16)}, - {PIP_X(id_W838) + 0., HOP4Y(16), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(14), PIP_X(id_W838) + 1., HOP4Y(14)}, - {PIP_X(id_W838) + 1., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(12), PIP_X(id_W838) + 2., HOP4Y(12)}, - {PIP_X(id_W838) + 2., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(10), PIP_X(id_W838) + 3., HOP4Y(10)}, - {PIP_X(id_W838) + 3., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(8), PIP_X(id_W838) + 4., HOP4Y(8)}, - {PIP_X(id_W838) + 4., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6)}, - {PIP_X(id_E834) + 4., HOP4Y(8), PIP_X(id_E834) + 4., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(6), PIP_X(id_W838) + 5., HOP4Y(6)}, - {PIP_X(id_W838) + 5., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(4), PIP_X(id_W838) + 6., HOP4Y(4)}, - {PIP_X(id_W838) + 6., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(2), PIP_X(id_W838) + 7., HOP4Y(2)}, - {PIP_X(id_W838) + 7., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 7., HOP4Y(0), wrap_len + 8., HOP4Y(0)}, - {wrap_len + 8., HOP4Y(0), wrap_len + 8., HOP4Y(1)}, - {wrap_len + 8., HOP4Y(1), PIP_X(id_W838) + 7., HOP4Y(1)}, - {PIP_X(id_W838) + 7., HOP4Y(1), PIP_X(id_W838) + 7., WIRE_Y(0)}}}, - {id_W83_loop0, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(15)}, - {-wrap_len - 0., HOP4Y(15), -wrap_len - 0., HOP4Y(14)}, - {-wrap_len - 0., HOP4Y(14), PIP_X(id_W838) + 0., HOP4Y(14)}, - {PIP_X(id_W838) + 0., HOP4Y(14), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(12)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(12), PIP_X(id_W838) + 1., HOP4Y(12)}, - {PIP_X(id_W838) + 1., HOP4Y(12), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(10), PIP_X(id_W838) + 2., HOP4Y(10)}, - {PIP_X(id_W838) + 2., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(8), PIP_X(id_W838) + 3., HOP4Y(8)}, - {PIP_X(id_W838) + 3., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(6)}, - {PIP_X(id_E834) + 3., HOP4Y(8), PIP_X(id_E834) + 3., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(6), PIP_X(id_W838) + 4., HOP4Y(6)}, - {PIP_X(id_W838) + 4., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(4), PIP_X(id_W838) + 5., HOP4Y(4)}, - {PIP_X(id_W838) + 5., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist + 5., HOP4Y(2), PIP_X(id_W838) + 6., HOP4Y(2)}, - {PIP_X(id_W838) + 6., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 6., HOP4Y(0), PIP_X(id_E838) + 7., HOP4Y(0)}, - {PIP_X(id_E838) + 7., HOP4Y(0), PIP_X(id_E838) + 7., WIRE_Y(0)}}}, - {id_W83_loop1, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(13)}, - {-wrap_len - 1., HOP4Y(13), -wrap_len - 1., HOP4Y(12)}, - {-wrap_len - 1., HOP4Y(12), PIP_X(id_W838) - 1., HOP4Y(12)}, - {PIP_X(id_W838) - 1., HOP4Y(12), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(10)}, - {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(10), PIP_X(id_W838) + 0., HOP4Y(10)}, - {PIP_X(id_W838) + 0., HOP4Y(10), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(8), PIP_X(id_W838) + 1., HOP4Y(8)}, - {PIP_X(id_W838) + 1., HOP4Y(8), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(6)}, - {PIP_X(id_E834) + 1., HOP4Y(8), PIP_X(id_E834) + 1., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(6), PIP_X(id_W838) + 2., HOP4Y(6)}, - {PIP_X(id_W838) + 2., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(4), PIP_X(id_W838) + 3., HOP4Y(4)}, - {PIP_X(id_W838) + 3., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist + 3., HOP4Y(2), PIP_X(id_W838) + 4., HOP4Y(2)}, - {PIP_X(id_W838) + 4., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 4., HOP4Y(0), PIP_X(id_E838) + 5., HOP4Y(0)}, - {PIP_X(id_E838) + 5., HOP4Y(0), PIP_X(id_E838) + 5., WIRE_Y(0)}}}, - {id_W83_loop2, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(11)}, - {-wrap_len - 2., HOP4Y(11), -wrap_len - 2., HOP4Y(10)}, - {-wrap_len - 2., HOP4Y(10), PIP_X(id_W838) - 2., HOP4Y(10)}, - {PIP_X(id_W838) - 2., HOP4Y(10), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(8)}, - {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(8), PIP_X(id_W838) - 1., HOP4Y(8)}, - {PIP_X(id_W838) - 1., HOP4Y(8), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(6)}, - {PIP_X(id_E834) - 1., HOP4Y(8), PIP_X(id_E834) - 1., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(6), PIP_X(id_W838) + 0., HOP4Y(6)}, - {PIP_X(id_W838) + 0., HOP4Y(6), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(4), PIP_X(id_W838) + 1., HOP4Y(4)}, - {PIP_X(id_W838) + 1., HOP4Y(4), PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist + 1., HOP4Y(2), PIP_X(id_W838) + 2., HOP4Y(2)}, - {PIP_X(id_W838) + 2., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 2., HOP4Y(0), PIP_X(id_E838) + 3., HOP4Y(0)}, - {PIP_X(id_E838) + 3., HOP4Y(0), PIP_X(id_E838) + 3., WIRE_Y(0)}}}, - {id_W83_loop3, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, - {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(9)}, - {-wrap_len - 3., HOP4Y(9), -wrap_len - 3., HOP4Y(8)}, - {-wrap_len - 3., HOP4Y(8), PIP_X(id_W838) - 3., HOP4Y(8)}, - {PIP_X(id_W838) - 3., HOP4Y(8), PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(6)}, - {PIP_X(id_E834) - 3., HOP4Y(8), PIP_X(id_E834) - 3., WIRE_Y(0)}, - {PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(6), PIP_X(id_W838) - 2., HOP4Y(6)}, - {PIP_X(id_W838) - 2., HOP4Y(6), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(4), PIP_X(id_W838) - 1., HOP4Y(4)}, - {PIP_X(id_W838) - 1., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist - 1., HOP4Y(2), PIP_X(id_W838) + 0., HOP4Y(2)}, - {PIP_X(id_W838) + 0., HOP4Y(2), PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist + 0., HOP4Y(0), PIP_X(id_E838) + 1., HOP4Y(0)}, - {PIP_X(id_E838) + 1., HOP4Y(0), PIP_X(id_E838) + 1., WIRE_Y(0)}}}, - {id_W83_loop4, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, - {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, - {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(7)}, - {-wrap_len - 4., HOP4Y(7), -wrap_len - 4., HOP4Y(6)}, - {PIP_X(id_W834) - 4., HOP4Y(6), PIP_X(id_W834) - 4., WIRE_Y(0)}, - {-wrap_len - 4., HOP4Y(6), PIP_X(id_W838) - 4., HOP4Y(6)}, - {PIP_X(id_W838) - 4., HOP4Y(6), PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(4)}, - {PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(4), PIP_X(id_W838) - 3., HOP4Y(4)}, - {PIP_X(id_W838) - 3., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist - 3., HOP4Y(2), PIP_X(id_W838) - 2., HOP4Y(2)}, - {PIP_X(id_W838) - 2., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist - 2., HOP4Y(0), PIP_X(id_E838) - 1., HOP4Y(0)}, - {PIP_X(id_E838) - 1., HOP4Y(0), PIP_X(id_E838) - 1., WIRE_Y(0)}}}, - {id_W83_loop5, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, - {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, - {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, - {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(5)}, - {-wrap_len - 5., HOP4Y(5), -wrap_len - 5., HOP4Y(4)}, - {-wrap_len - 5., HOP4Y(4), PIP_X(id_W838) - 5., HOP4Y(4)}, - {PIP_X(id_W838) - 5., HOP4Y(4), PIP_X(id_W838) + top_wire_dist - 5., HOP4Y(2)}, - {PIP_X(id_W838) + top_wire_dist - 5., HOP4Y(2), PIP_X(id_W838) - 4., HOP4Y(2)}, - {PIP_X(id_W838) - 4., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist - 4., HOP4Y(0), PIP_X(id_E838) - 3., HOP4Y(0)}, - {PIP_X(id_E838) - 3., HOP4Y(0), PIP_X(id_E838) - 3., WIRE_Y(0)}}}, - {id_W83_loop6, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, - {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, - {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, - {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, - {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(3)}, - {-wrap_len - 6., HOP4Y(3), -wrap_len - 6., HOP4Y(2)}, - {-wrap_len - 6., HOP4Y(2), PIP_X(id_W838) - 6., HOP4Y(2)}, - {PIP_X(id_W838) - 6., HOP4Y(2), PIP_X(id_W838) + top_wire_dist - 6., HOP4Y(0)}, - {PIP_X(id_W838) + top_wire_dist - 6., HOP4Y(0), PIP_X(id_E838) - 5., HOP4Y(0)}, - {PIP_X(id_E838) - 5., HOP4Y(0), PIP_X(id_E838) - 5., WIRE_Y(0)}}}, - {id_W83_loop7, - {{PIP_X(id_W830), WIRE_Y(0), PIP_X(id_W830), HOP4Y(17)}, - {PIP_X(id_W830) - 0., HOP4Y(17), PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15)}, - {PIP_X(id_W830) - top_wire_dist - 0., HOP4Y(15), PIP_X(id_W830) - 1., HOP4Y(15)}, - {PIP_X(id_W830) - 1., HOP4Y(15), PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13)}, - {PIP_X(id_W830) - top_wire_dist - 1., HOP4Y(13), PIP_X(id_W830) - 2., HOP4Y(13)}, - {PIP_X(id_W830) - 2., HOP4Y(13), PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11)}, - {PIP_X(id_W830) - top_wire_dist - 2., HOP4Y(11), PIP_X(id_W830) - 3., HOP4Y(11)}, - {PIP_X(id_W830) - 3., HOP4Y(11), PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9)}, - {PIP_X(id_W830) - top_wire_dist - 3., HOP4Y(9), PIP_X(id_W830) - 4., HOP4Y(9)}, - {PIP_X(id_W830) - 4., HOP4Y(9), PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7)}, - {PIP_X(id_W834) - 4., HOP4Y(9), PIP_X(id_W834) - 4., WIRE_Y(0)}, - {PIP_X(id_W830) - top_wire_dist - 4., HOP4Y(7), PIP_X(id_W830) - 5., HOP4Y(7)}, - {PIP_X(id_W830) - 5., HOP4Y(7), PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5)}, - {PIP_X(id_W830) - top_wire_dist - 5., HOP4Y(5), PIP_X(id_W830) - 6., HOP4Y(5)}, - {PIP_X(id_W830) - 6., HOP4Y(5), PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3)}, - {PIP_X(id_W830) - top_wire_dist - 6., HOP4Y(3), PIP_X(id_W830) - 7., HOP4Y(3)}, - {PIP_X(id_W830) - 7., HOP4Y(3), PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1)}, - {PIP_X(id_W830) - top_wire_dist - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(1)}, - {-wrap_len - 7., HOP4Y(1), -wrap_len - 7., HOP4Y(0)}, - {-wrap_len - 7., HOP4Y(0), PIP_X(id_E838) - 7., HOP4Y(0)}, - {PIP_X(id_E838) - 7., HOP4Y(0), PIP_X(id_E838) - 7., WIRE_Y(0)}}}, -}; void gfxCreateBelDecals(Arch *arch); void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel); From 604260a0d7344627cea82198512384319c705105 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 3 Feb 2022 11:26:45 +1000 Subject: [PATCH 051/712] gowin: Add a DS location recognition For differential signals it is necessary to set the position of two pins at once: P and N. This commit adds that capability and also adds another style of location setting --- with the pin letter in square brackets used in vendor tools. Signed-off-by: YRabbit --- gowin/arch.cc | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 8c654b2eb9..1c401a43ce 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -505,10 +505,13 @@ static Loc getLoc(std::smatch match, int maxX, int maxY) void Arch::read_cst(std::istream &in) { - std::regex iobre = std::regex("IO_LOC +\"([^\"]+)\" +([^ ;]+) *;.*"); + // If two locations are specified separated by commas (for differential I/O buffers), + // only the first location is actually recognized and used. + // And pin A will be Positive and pin B will be Negative in any case. + std::regex iobre = std::regex("IO_LOC +\"([^\"]+)\" +([^ ,;]+)(, *[^ ;]+)? *;.*"); std::regex portre = std::regex("IO_PORT +\"([^\"]+)\" +([^;]+;).*"); std::regex port_attrre = std::regex("([^ =;]+=[^ =;]+) *([^;]*;)"); - std::regex iobelre = std::regex("IO([TRBL])([0-9]+)([A-Z])"); + std::regex iobelre = std::regex("IO([TRBL])([0-9]+)\\[?([A-Z])\\]?"); std::regex inslocre = std::regex("INS_LOC +\"([^\"]+)\" +R([0-9]+)C([0-9]+)\\[([0-9])\\]\\[([AB])\\] *;.*"); std::smatch match, match_attr, match_pinloc; std::string line, pinline; @@ -551,17 +554,20 @@ void Arch::read_cst(std::istream &in) if (belname != nullptr) { std::string bel = IdString(belname->src_id).str(this); it->second->setAttr(IdString(ID_BEL), bel); - } else if (std::regex_match(pinline, match_pinloc, iobelre)) { - // may be it's IOx#[AB] style? - Loc loc = getLoc(match_pinloc, getGridDimX(), getGridDimY()); - BelId bel = getBelByLocation(loc); - if (bel == BelId()) { - log_error("Pin %s not found\n", pinline.c_str()); - } - std::string belname = getCtx()->nameOfBel(bel); - it->second->setAttr(IdString(ID_BEL), belname); } else { - log_error("Pin %s not found\n", pinname.c_str(this)); + if (std::regex_match(pinline, match_pinloc, iobelre)) { + // may be it's IOx#[AB] style? + Loc loc = getLoc(match_pinloc, getGridDimX(), getGridDimY()); + BelId bel = getBelByLocation(loc); + if (bel == BelId()) { + log_error("Pin %s not found (TRBL style). \n", pinline.c_str()); + } + std::string belname = getCtx()->nameOfBel(bel); + it->second->setAttr(IdString(ID_BEL), belname); + } else { + std::cout << "plain error:[" << pinline << "]" << std::endl; + log_error("Pin %s not found (pin# style)\n", pinname.c_str(this)); + } } } break; case insloc: { // INS_LOC From d446a299871e7cf30631721f20818f7cce9ea3c0 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 3 Feb 2022 14:59:25 +0000 Subject: [PATCH 052/712] nexus: Hotfix to disable unimplemented DCS routethru Signed-off-by: gatecat --- nexus/arch.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 06a901cdf3..d553200fe0 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -185,7 +185,12 @@ Arch::Arch(ArchArgs args) : args(args) for (auto pip : getPipsUphill(w)) disabled_pips.insert(pip); } - NPNR_ASSERT(disabled_pips.size() == 4); + // TODO: find a better solution to disable these + WireId dcs_out = getWireByName( + IdStringList(std::array{x_ids.at(37), y_ids.at(10), id("JDCSOUT_DCS_DCSIP")})); + for (auto dcs_pip : getPipsUphill(dcs_out)) + disabled_pips.insert(dcs_pip); + NPNR_ASSERT(disabled_pips.size() == 6); } } From 84399caebe3c3eb0eba5275b7e2d5479404ba1a7 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 3 Feb 2022 15:28:46 +0000 Subject: [PATCH 053/712] run clangformat Signed-off-by: gatecat --- common/hashlib.h | 4 +--- gowin/gfx.h | 1 - mistral/arch.cc | 15 ++++++++------- mistral/arch.h | 5 +++-- mistral/bitstream.cc | 16 +++++++++------- mistral/io.cc | 2 +- nexus/main.cc | 3 +-- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/common/hashlib.h b/common/hashlib.h index 70de8c919e..2f7357e25c 100644 --- a/common/hashlib.h +++ b/common/hashlib.h @@ -28,9 +28,7 @@ const int hashtable_size_factor = 3; // Cantor pairing function for two non-negative integers // https://en.wikipedia.org/wiki/Pairing_function -inline unsigned int mkhash(unsigned int a, unsigned int b) { - return (a*a + 3*a + 2*a*b + b + b*b) / 2; -} +inline unsigned int mkhash(unsigned int a, unsigned int b) { return (a * a + 3 * a + 2 * a * b + b + b * b) / 2; } // traditionally 5381 is used as starting value for the djb2 hash const unsigned int mkhash_init = 5381; diff --git a/gowin/gfx.h b/gowin/gfx.h index b5ea4678ac..623b9fb20e 100644 --- a/gowin/gfx.h +++ b/gowin/gfx.h @@ -24,7 +24,6 @@ NEXTPNR_NAMESPACE_BEGIN - void gfxCreateBelDecals(Arch *arch); void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel); void gfxSetIOBWireDecals(Arch *arch, BelInfo &bel); diff --git a/mistral/arch.cc b/mistral/arch.cc index f61d07abe0..c50e30f49b 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -43,18 +43,19 @@ void IdString::initialize_arch(const BaseCtx *ctx) #undef X } -CycloneV::rnode_t Arch::find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi, int pi) const +CycloneV::rnode_t Arch::find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi, + int pi) const { auto pn1 = CycloneV::pnode(bt, x, y, port, bi, pi); auto rn1 = cyclonev->pnode_to_rnode(pn1); - if(rn1) + if (rn1) return rn1; - if(bt == CycloneV::GPIO) { + if (bt == CycloneV::GPIO) { auto pn2 = cyclonev->p2p_to(pn1); - if(!pn2) { + if (!pn2) { auto pnv = cyclonev->p2p_from(pn1); - if(!pnv.empty()) + if (!pnv.empty()) pn2 = pnv[0]; } auto pn3 = cyclonev->hmc_get_bypass(pn2); @@ -68,9 +69,9 @@ CycloneV::rnode_t Arch::find_rnode(CycloneV::block_type_t bt, int x, int y, Cycl WireId Arch::get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi) const { auto rn = find_rnode(bt, x, y, port, bi, pi); - if(rn) + if (rn) return WireId(rn); - + log_error("Trying to connect unknown node %s\n", CycloneV::pn2s(CycloneV::pnode(bt, x, y, port, bi, pi)).c_str()); } diff --git a/mistral/arch.h b/mistral/arch.h index e931df2d08..e17be33125 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -461,7 +461,8 @@ struct Arch : BaseArch void add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire); - CycloneV::rnode_t find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi = -1, int pi = -1) const; + CycloneV::rnode_t find_rnode(CycloneV::block_type_t bt, int x, int y, CycloneV::port_type_t port, int bi = -1, + int pi = -1) const; WireId get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const; bool has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const; @@ -561,7 +562,7 @@ struct Arch : BaseArch // ------------------------------------------------- - void build_bitstream(); // bitstream.cc + void build_bitstream(); // bitstream.cc }; NEXTPNR_NAMESPACE_END diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 340f4b9642..e18d141346 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -39,14 +39,14 @@ struct MistralBitgen { auto pn1 = CycloneV::pnode(bt, pos, port, bi, pi); auto rn1 = cv->pnode_to_rnode(pn1); - if(rn1) + if (rn1) return rn1; - if(bt == CycloneV::GPIO) { + if (bt == CycloneV::GPIO) { auto pn2 = cv->p2p_to(pn1); - if(!pn2) { + if (!pn2) { auto pnv = cv->p2p_from(pn1); - if(!pnv.empty()) + if (!pnv.empty()) pn2 = pnv[0]; } auto pn3 = cv->hmc_get_bypass(pn2); @@ -97,9 +97,11 @@ struct MistralBitgen // Output gpios must also bypass things in the associated dqs auto dqs = cv->p2p_to(CycloneV::pnode(CycloneV::GPIO, pos, CycloneV::PNONE, bi, -1)); - if(dqs) { - cv->bmux_m_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::INPUT_REG4_SEL, CycloneV::pn2bi(dqs), CycloneV::SEL_LOCKED_DPA); - cv->bmux_r_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::RB_T9_SEL_EREG_CFF_DELAY, CycloneV::pn2bi(dqs), 0x1f); + if (dqs) { + cv->bmux_m_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::INPUT_REG4_SEL, CycloneV::pn2bi(dqs), + CycloneV::SEL_LOCKED_DPA); + cv->bmux_r_set(CycloneV::DQS16, CycloneV::pn2p(dqs), CycloneV::RB_T9_SEL_EREG_CFF_DELAY, + CycloneV::pn2bi(dqs), 0x1f); } } // There seem to be two mirrored OEIN inversion bits for constant OE for inputs/outputs. This might be to diff --git a/mistral/io.cc b/mistral/io.cc index a0a01af38a..c8d0238dcf 100644 --- a/mistral/io.cc +++ b/mistral/io.cc @@ -30,7 +30,7 @@ void Arch::create_gpio(int x, int y) WireId pad = add_wire(x, y, id(stringf("PAD[%d]", z))); BelId bel = add_bel(x, y, id(stringf("IO[%d]", z)), id_MISTRAL_IO); add_bel_pin(bel, id_PAD, PORT_INOUT, pad); - if(has_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)) { + if (has_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)) { // FIXME: is the port index of zero always correct? add_bel_pin(bel, id_I, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)); add_bel_pin(bel, id_OE, PORT_IN, get_port(CycloneV::GPIO, x, y, z, CycloneV::OEIN, 0)); diff --git a/nexus/main.cc b/nexus/main.cc index b02dfa998c..88dddfe598 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -54,8 +54,7 @@ po::options_description NexusCommandHandler::getArchOptions() specific.add_options()("no-pack-lutff", "disable packing (clustering) LUTs and FFs together"); specific.add_options()("carry-lutff-ratio", po::value(), "ratio of FFs to be added to carry-chain LUT clusters"); - specific.add_options()("estimate-delay-mult", po::value(), - "multiplier for the estimate delay"); + specific.add_options()("estimate-delay-mult", po::value(), "multiplier for the estimate delay"); return specific; } From 1152c9f2f816c3d996b76dcf8c9333fb931ca8a8 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 4 Feb 2022 09:18:08 +1000 Subject: [PATCH 054/712] gowin: Remove leftover debugging Signed-off-by: YRabbit --- gowin/arch.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 886d811d8a..0793832601 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -732,7 +732,6 @@ void Arch::read_cst(std::istream &in) std::string belname = getCtx()->nameOfBel(bel); it->second->setAttr(IdString(ID_BEL), belname); } else { - std::cout << "plain error:[" << pinline << "]" << std::endl; log_error("Pin %s not found (pin# style)\n", pinname.c_str(this)); } } From 0675b98437df77f38a5d3f6278df7463d60cb3a3 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 4 Feb 2022 14:24:22 +1000 Subject: [PATCH 055/712] gowin: Speed up the GUI By mistake, an empty decal gets filled with graphical elements. Signed-off-by: YRabbit --- gowin/gfx.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gowin/gfx.cc b/gowin/gfx.cc index a851f53a69..daae5c71c3 100644 --- a/gowin/gfx.cc +++ b/gowin/gfx.cc @@ -5260,7 +5260,8 @@ void gfxSetWireDefaultDecal(Arch *arch, WireInfo &wire) snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); active_id = arch->id(buf); active.decal = active_id; - inactive_id = IdString(); + snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); + inactive_id = arch->id(buf); inactive.decal = inactive_id; active.x = inactive.x = 0; active.y = inactive.y = 0; @@ -5301,9 +5302,8 @@ void gfxSetWireDefaultDecal(Arch *arch, WireInfo &wire) snprintf(buf, sizeof(buf), "%s_active", wire.name.c_str(arch)); active_id = arch->id(buf); active.decal = active_id; - // snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); - // inactive_id = arch->id(buf); - inactive_id = IdString(); + snprintf(buf, sizeof(buf), "%s_inactive", wire.name.c_str(arch)); + inactive_id = arch->id(buf); inactive.decal = inactive_id; active.x = inactive.x = 0; active.y = inactive.y = 0; From 5ef5c33e9e964a176040c829adc965ec46331d77 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 4 Feb 2022 15:54:41 +0000 Subject: [PATCH 056/712] generic: Add missing Pip vector binding Signed-off-by: gatecat --- generic/arch_pybindings.cc | 1 + generic/arch_pybindings.h | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index 92c782529b..6d3d981c53 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -266,6 +266,7 @@ void arch_wrap_python(py::module &m) WRAP_MAP_UPTR(m, NetMap, "IdNetMap"); WRAP_MAP(m, HierarchyMap, wrap_context, "HierarchyMap"); WRAP_VECTOR(m, const std::vector, conv_to_str); + WRAP_VECTOR(m, const std::vector, conv_to_str); } NEXTPNR_NAMESPACE_END diff --git a/generic/arch_pybindings.h b/generic/arch_pybindings.h index 72d688dc61..f0daccbc4e 100644 --- a/generic/arch_pybindings.h +++ b/generic/arch_pybindings.h @@ -76,6 +76,18 @@ template <> struct string_converter } }; +template <> struct string_converter +{ + PipId from_str(Context *ctx, std::string name) { return ctx->getPipByNameStr(name); } + + std::string to_str(Context *ctx, const PipId &id) + { + if (id == PipId()) + throw bad_wrap(); + return ctx->getPipName(id).str(ctx); + } +}; + template <> struct string_converter { BelPin from_str(Context *ctx, std::string name) From e5bfff6e9fbbbc743c504365ab41e4112aefb6d6 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 4 Feb 2022 20:49:32 +0000 Subject: [PATCH 057/712] viaduct: Allow constraining only cascades without fanout Signed-off-by: gatecat --- generic/viaduct_helpers.cc | 4 +++- generic/viaduct_helpers.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/generic/viaduct_helpers.cc b/generic/viaduct_helpers.cc index 36bdd6bec1..e9f7fa60e6 100644 --- a/generic/viaduct_helpers.cc +++ b/generic/viaduct_helpers.cc @@ -82,7 +82,7 @@ void ViaductHelpers::remove_nextpnr_iobs(const pool &top_ports) } int ViaductHelpers::constrain_cell_pairs(const pool &src_ports, const pool &sink_ports, - int delta_z) + int delta_z, bool allow_fanout) { int constrained = 0; for (auto &cell : ctx->cells) { @@ -96,6 +96,8 @@ int ViaductHelpers::constrain_cell_pairs(const pool &src_ports, co continue; if (!src_ports.count(CellTypePort(ci.type, port.first))) continue; + if (!allow_fanout && port.second.net->users.size() > 1) + continue; for (auto &usr : port.second.net->users) { if (!sink_ports.count(CellTypePort(usr))) continue; diff --git a/generic/viaduct_helpers.h b/generic/viaduct_helpers.h index 8cba84115e..cac2230421 100644 --- a/generic/viaduct_helpers.h +++ b/generic/viaduct_helpers.h @@ -70,7 +70,8 @@ struct ViaductHelpers // expects a set of top-level port types void remove_nextpnr_iobs(const pool &top_ports); // Constrain cells with certain port connection patterns together with a fixed z-offset - int constrain_cell_pairs(const pool &src_ports, const pool &sink_ports, int delta_z); + int constrain_cell_pairs(const pool &src_ports, const pool &sink_ports, int delta_z, + bool allow_fanout = true); // Replace constants with given driving cells void replace_constants(CellTypePort vcc_driver, CellTypePort gnd_driver, const dict &vcc_params = {}, From 230de1e68afb0f7c57f64bd1b35850e529395d92 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 24 Jan 2022 23:54:09 +0800 Subject: [PATCH 058/712] gowin: add an option to manually specify family In the vendor IDE, there's a device family named GW1N-9C (which seems to mean C revision of GW1N-9), in which the model numbers are all the same with GW1N-9. Add an option to nextpnr-gowin to allow manually specified family for this situation. Signed-off-by: Icenowy Zheng --- gowin/main.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/gowin/main.cc b/gowin/main.cc index fb9df48d20..343d922b5c 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -48,6 +48,7 @@ po::options_description GowinCommandHandler::getArchOptions() { po::options_description specific("Architecture specific options"); specific.add_options()("device", po::value(), "device name"); + specific.add_options()("family", po::value(), "family name"); specific.add_options()("cst", po::value(), "physical constraints file"); return specific; } @@ -62,12 +63,16 @@ std::unique_ptr GowinCommandHandler::createContext(dict(); + } else { + char buf[36]; + // 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. + snprintf(buf, 36, "GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str()); + chipArgs.family = buf; + } chipArgs.partnumber = match[0]; return std::unique_ptr(new Context(chipArgs)); } From cd97ef6536eccc73554b5f418945c4e001db9606 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Sun, 6 Feb 2022 11:56:09 +0100 Subject: [PATCH 059/712] add GW1N-9C db --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- gowin/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index 934bbbcbe0..a4233f3d51 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -65,4 +65,4 @@ RUN set -e -x ;\ PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide RUN set -e -x ;\ - pip3 install apycula==0.2a2 + pip3 install apycula==0.2a3 diff --git a/gowin/CMakeLists.txt b/gowin/CMakeLists.txt index 5ec321288c..34af9dc6d5 100644 --- a/gowin/CMakeLists.txt +++ b/gowin/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) project(chipdb-gowin NONE) -set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9 GW1NS-2 GW1NS-4) +set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-2 GW1NS-4) set(GOWIN_DEVICES ${ALL_GOWIN_DEVICES} CACHE STRING "Include support for these Gowin devices (available: ${ALL_GOWIN_DEVICES})") message(STATUS "Enabled Gowin devices: ${GOWIN_DEVICES}") From 5c300933609b5213322e271a4e745e2d4a101b31 Mon Sep 17 00:00:00 2001 From: Dan Callaghan Date: Thu, 10 Feb 2022 15:48:06 +1100 Subject: [PATCH 060/712] nexus: reduce OSCA worst case to 7% MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current version of Crosslink-NX Family Data Sheet lists the high frequency oscillator maximum frequency as 481.5MHz (that is, 7% higher than its nominal 450MHz): https://www.latticesemi.com/-/media/LatticeSemi/Documents/DataSheets/CrossLink/FPGA-DS-02049-1-2-1-CrossLink-NX-Family.ashx?document_id=52780 Older documents listed a wider frequency range but ±7% is the range for production parts. --- nexus/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index 5e9da3042f..81a729b187 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1987,7 +1987,7 @@ struct NexusPacker copy_constraint(ci, id_CLKI, id_CLKO, 1); } else if (ci->type == id_OSC_CORE) { int div = int_or_default(ci->params, ctx->id("HF_CLK_DIV"), 128); - const float tol = 1.15f; // OSCA has +/-15% frequency tolerance, assume the worst case. + const float tol = 1.07f; // OSCA has +/-7% frequency tolerance, assume the worst case. set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1) / tol)); set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10) / tol)); } else if (ci->type == id_PLL_CORE) { From e90f5b72f7047e3813ce0817eeba82393919b13e Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Tue, 15 Feb 2022 14:12:16 +0200 Subject: [PATCH 061/712] gowin: Add GW1NZ-1 --- gowin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gowin/CMakeLists.txt b/gowin/CMakeLists.txt index 34af9dc6d5..ab6f4ee42e 100644 --- a/gowin/CMakeLists.txt +++ b/gowin/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) project(chipdb-gowin NONE) -set(ALL_GOWIN_DEVICES GW1N-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-2 GW1NS-4) +set(ALL_GOWIN_DEVICES GW1N-1 GW1NZ-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-2 GW1NS-4) set(GOWIN_DEVICES ${ALL_GOWIN_DEVICES} CACHE STRING "Include support for these Gowin devices (available: ${ALL_GOWIN_DEVICES})") message(STATUS "Enabled Gowin devices: ${GOWIN_DEVICES}") From 05c41ae2246f7a5da5247906f73d88d8526ebe6d Mon Sep 17 00:00:00 2001 From: Ezra Thomas <46074998+ept221@users.noreply.github.com> Date: Tue, 15 Feb 2022 22:29:39 -0500 Subject: [PATCH 062/712] Fixed formatting typo in archapi.md --- docs/archapi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/archapi.md b/docs/archapi.md index f798254fad..3fc30cdb5b 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -569,7 +569,7 @@ run the router. Graphics Methods ---------------- -###DecalGfxRangeT getDecalGraphics(DecalId decal) const +### DecalGfxRangeT getDecalGraphics(DecalId decal) const Return the graphic elements that make up a decal. From bdea5a1f6f5633ddc84fcbe1f13325b655bae1e9 Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Wed, 16 Feb 2022 10:25:18 +0200 Subject: [PATCH 063/712] gowin: bump apycula version --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index a4233f3d51..d1e7b47bf9 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -65,4 +65,4 @@ RUN set -e -x ;\ PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide RUN set -e -x ;\ - pip3 install apycula==0.2a3 + pip3 install apycula==0.2a4 From f3ee0d51a9546afe77a31d2a1e98a0bbee4659fa Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 16 Feb 2022 13:32:53 +0000 Subject: [PATCH 064/712] clangformat Signed-off-by: gatecat --- gowin/main.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gowin/main.cc b/gowin/main.cc index 343d922b5c..38a67fcd18 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -64,14 +64,14 @@ std::unique_ptr GowinCommandHandler::createContext(dict(); + chipArgs.family = vm["family"].as(); } else { - char buf[36]; - // 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. - snprintf(buf, 36, "GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str()); - chipArgs.family = buf; + char buf[36]; + // 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. + snprintf(buf, 36, "GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str()); + chipArgs.family = buf; } chipArgs.partnumber = match[0]; return std::unique_ptr(new Context(chipArgs)); From 02e6d2dbca0433e6f873c6af635cee701e84f5f5 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 16 Feb 2022 13:39:57 +0000 Subject: [PATCH 065/712] mistral: Fix 'not handled in switch' compiler warning Signed-off-by: gatecat --- mistral/delay.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mistral/delay.cc b/mistral/delay.cc index 98ef1be62a..4d123249e8 100644 --- a/mistral/delay.cc +++ b/mistral/delay.cc @@ -235,8 +235,9 @@ DelayQuad Arch::getPipDelay(PipId pip) const return DelayQuad{123}; case CycloneV::rnode_type_t::TCLK: return DelayQuad{46}; + default: + return DelayQuad{308}; } - return DelayQuad{308}; } delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const From 30fd86ce69fa65e89dec119e23b5bccb54de70a3 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 16 Feb 2022 13:53:47 +0000 Subject: [PATCH 066/712] refactor: New NetInfo and CellInfo constructors --- common/basectx.cc | 7 +--- common/design_utils.cc | 8 +--- common/nextpnr_types.h | 6 +++ ecp5/cells.cc | 18 ++++----- ecp5/globals.cc | 23 +++++------ ecp5/pack.cc | 58 +++++++++------------------- fpga_interchange/pseudo_pip_model.cc | 6 +-- fpga_interchange/site_arch.cc | 4 +- generic/cells.cc | 10 ++--- generic/pack.cc | 6 +-- gowin/cells.cc | 10 ++--- gowin/pack.cc | 6 +-- ice40/bitstream.cc | 4 +- ice40/cells.cc | 20 ++++------ ice40/chains.cc | 32 +++++---------- ice40/pack.cc | 22 ++++------- machxo2/cells.cc | 10 ++--- machxo2/pack.cc | 15 +++---- 18 files changed, 95 insertions(+), 170 deletions(-) diff --git a/common/basectx.cc b/common/basectx.cc index b9036ed6ec..b55cd072ab 100644 --- a/common/basectx.cc +++ b/common/basectx.cc @@ -211,8 +211,7 @@ NetInfo *BaseCtx::createNet(IdString name) { NPNR_ASSERT(!nets.count(name)); NPNR_ASSERT(!net_aliases.count(name)); - std::unique_ptr net{new NetInfo}; - net->name = name; + auto net = std::make_unique(name); net_aliases[name] = name; NetInfo *ptr = net.get(); nets[name] = std::move(net); @@ -252,9 +251,7 @@ void BaseCtx::lockNetRouting(IdString name) CellInfo *BaseCtx::createCell(IdString name, IdString type) { NPNR_ASSERT(!cells.count(name)); - std::unique_ptr cell{new CellInfo}; - cell->name = name; - cell->type = type; + auto cell = std::make_unique(getCtx(), name, type); CellInfo *ptr = cell.get(); cells[name] = std::move(cell); refreshUi(); diff --git a/common/design_utils.cc b/common/design_utils.cc index da5decf9c4..9432b6cd71 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -129,12 +129,8 @@ void connect_ports(Context *ctx, CellInfo *cell1, IdString port1_name, CellInfo PortInfo &port1 = cell1->ports.at(port1_name); if (port1.net == nullptr) { // No net on port1; need to create one - std::unique_ptr p1net(new NetInfo()); - p1net->name = ctx->id(cell1->name.str(ctx) + "$conn$" + port1_name.str(ctx)); - connect_port(ctx, p1net.get(), cell1, port1_name); - IdString p1name = p1net->name; - NPNR_ASSERT(!ctx->cells.count(p1name)); - ctx->nets[p1name] = std::move(p1net); + NetInfo *p1net = ctx->createNet(ctx->id(cell1->name.str(ctx) + "$conn$" + port1_name.str(ctx))); + connect_port(ctx, p1net, cell1, port1_name); } connect_port(ctx, port1.net, cell2, port2_name); } diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 6b594d643d..6debd2b8df 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -124,6 +124,7 @@ struct ClockConstraint; struct NetInfo : ArchNetInfo { + explicit NetInfo(IdString name) : name(name){}; IdString name, hierpath; int32_t udata = 0; @@ -155,8 +156,13 @@ struct PortInfo PortType type; }; +struct Context; + struct CellInfo : ArchCellInfo { + CellInfo(Context *ctx, IdString name, IdString type) : ctx(ctx), name(name), type(type){}; + Context *ctx = nullptr; + IdString name, type, hierpath; int32_t udata; diff --git a/ecp5/cells.cc b/ecp5/cells.cc index b0ae2065f8..e5ab4d4b7d 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -34,13 +34,9 @@ void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; - std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); - if (name.empty()) { - new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)); - } else { - new_cell->name = ctx->id(name); - } - new_cell->type = type; + IdString name_id = + name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name); + std::unique_ptr new_cell = std::make_unique(ctx, name_id, type); auto copy_bel_ports = [&]() { // First find a Bel of the target type @@ -465,11 +461,11 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectorports.count(nxio->name)) { IdString tn_netname = nxio->name; NPNR_ASSERT(!ctx->nets.count(tn_netname)); - std::unique_ptr toplevel_net{new NetInfo}; + ctx->net_aliases.erase(tn_netname); + NetInfo *toplevel_net = ctx->createNet(tn_netname); toplevel_net->name = tn_netname; - connect_port(ctx, toplevel_net.get(), trio, ctx->id("B")); - ctx->ports[nxio->name].net = toplevel_net.get(); - ctx->nets[tn_netname] = std::move(toplevel_net); + connect_port(ctx, toplevel_net, trio, ctx->id("B")); + ctx->ports[nxio->name].net = toplevel_net; } CellInfo *tbuf = net_driven_by( diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 8ee49c0293..cc2208e00a 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -456,11 +456,10 @@ class Ecp5GlobalRouter dccptr = net->driver.cell; } else { auto dcc = create_ecp5_cell(ctx, id_DCCA, "$gbuf$" + net->name.str(ctx)); - std::unique_ptr glbnet = std::unique_ptr(new NetInfo); - glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); - glbnet->driver.cell = dcc.get(); - glbnet->driver.port = id_CLKO; - dcc->ports[id_CLKO].net = glbnet.get(); + glbptr = ctx->createNet(ctx->id("$glbnet$" + net->name.str(ctx))); + glbptr->driver.cell = dcc.get(); + glbptr->driver.port = id_CLKO; + dcc->ports[id_CLKO].net = glbptr; std::vector keep_users; for (auto user : net->users) { if (dcs_cell != nullptr && user.cell != dcs_cell) { @@ -473,8 +472,8 @@ class Ecp5GlobalRouter } else if (is_logic_port(user)) { keep_users.push_back(user); } else { - glbnet->users.push_back(user); - user.cell->ports.at(user.port).net = glbnet.get(); + glbptr->users.push_back(user); + user.cell->ports.at(user.port).net = glbptr; } } net->users = keep_users; @@ -485,13 +484,11 @@ class Ecp5GlobalRouter clki_pr.cell = dcc.get(); net->users.push_back(clki_pr); if (net->clkconstr) { - glbnet->clkconstr = std::unique_ptr(new ClockConstraint()); - glbnet->clkconstr->low = net->clkconstr->low; - glbnet->clkconstr->high = net->clkconstr->high; - glbnet->clkconstr->period = net->clkconstr->period; + glbptr->clkconstr = std::unique_ptr(new ClockConstraint()); + glbptr->clkconstr->low = net->clkconstr->low; + glbptr->clkconstr->high = net->clkconstr->high; + glbptr->clkconstr->period = net->clkconstr->period; } - glbptr = glbnet.get(); - ctx->nets[glbnet->name] = std::move(glbnet); dccptr = dcc.get(); ctx->cells[dcc->name] = std::move(dcc); } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 85d92336c2..79644d1331 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -761,17 +761,14 @@ class Ecp5Packer carry->users.end()); connect_port(ctx, carry, feedin.get(), id_A0); - std::unique_ptr new_carry(new NetInfo()); - new_carry->name = ctx->id(feedin->name.str(ctx) + "$COUT"); - connect_port(ctx, new_carry.get(), feedin.get(), ctx->id("COUT")); + NetInfo *new_carry = ctx->createNet(ctx->id(feedin->name.str(ctx) + "$COUT")); + connect_port(ctx, new_carry, feedin.get(), ctx->id("COUT")); chain_in.cell->ports[chain_in.port].net = nullptr; - connect_port(ctx, new_carry.get(), chain_in.cell, chain_in.port); + connect_port(ctx, new_carry, chain_in.cell, chain_in.port); CellInfo *feedin_ptr = feedin.get(); IdString feedin_name = feedin->name; ctx->cells[feedin_name] = std::move(feedin); - IdString new_carry_name = new_carry->name; - ctx->nets[new_carry_name] = std::move(new_carry); return feedin_ptr; } @@ -789,11 +786,10 @@ class Ecp5Packer carry->driver.cell = nullptr; connect_port(ctx, carry, feedout.get(), ctx->id("S0")); - std::unique_ptr new_cin(new NetInfo()); - new_cin->name = ctx->id(feedout->name.str(ctx) + "$CIN"); + NetInfo *new_cin = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$CIN")); new_cin->driver = carry_drv; - carry_drv.cell->ports.at(carry_drv.port).net = new_cin.get(); - connect_port(ctx, new_cin.get(), feedout.get(), ctx->id("CIN")); + carry_drv.cell->ports.at(carry_drv.port).net = new_cin; + connect_port(ctx, new_cin, feedout.get(), ctx->id("CIN")); if (chain_next) { // Loop back into LUT4_1 for feedthrough @@ -805,24 +801,17 @@ class Ecp5Packer }), carry->users.end()); - std::unique_ptr new_cout(new NetInfo()); - new_cout->name = ctx->id(feedout->name.str(ctx) + "$COUT"); - connect_port(ctx, new_cout.get(), feedout.get(), ctx->id("COUT")); + NetInfo *new_cout = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$COUT")); + connect_port(ctx, new_cout, feedout.get(), ctx->id("COUT")); chain_next->cell->ports[chain_next->port].net = nullptr; - connect_port(ctx, new_cout.get(), chain_next->cell, chain_next->port); - - IdString new_cout_name = new_cout->name; - ctx->nets[new_cout_name] = std::move(new_cout); + connect_port(ctx, new_cout, chain_next->cell, chain_next->port); } CellInfo *feedout_ptr = feedout.get(); IdString feedout_name = feedout->name; ctx->cells[feedout_name] = std::move(feedout); - IdString new_cin_name = new_cin->name; - ctx->nets[new_cin_name] = std::move(new_cin); - return feedout_ptr; } @@ -1384,16 +1373,14 @@ class Ecp5Packer std::unique_ptr gnd_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_GND"); gnd_cell->params[ctx->id("INIT")] = Property(0, 16); - std::unique_ptr gnd_net = std::unique_ptr(new NetInfo); - gnd_net->name = ctx->id("$PACKER_GND_NET"); + auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); gnd_net->driver.port = ctx->id("Z"); gnd_cell->ports.at(ctx->id("Z")).net = gnd_net.get(); std::unique_ptr vcc_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_VCC"); vcc_cell->params[ctx->id("INIT")] = Property(65535, 16); - std::unique_ptr vcc_net = std::unique_ptr(new NetInfo); - vcc_net->name = ctx->id("$PACKER_VCC_NET"); + auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); vcc_net->driver.port = ctx->id("Z"); vcc_cell->ports.at(ctx->id("Z")).net = vcc_net.get(); @@ -1967,12 +1954,9 @@ class Ecp5Packer IdString eckname = ctx->id(ecknet->name.str(ctx) + "$eclk" + std::to_string(bank) + "_" + std::to_string(free_eclk)); - std::unique_ptr promoted_ecknet(new NetInfo); - promoted_ecknet->name = eckname; + NetInfo *promoted_ecknet = ctx->createNet(eckname); promoted_ecknet->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1; // Prevents router etc touching this special net - eclk.buf = promoted_ecknet.get(); - NPNR_ASSERT(!ctx->nets.count(eckname)); - ctx->nets[eckname] = std::move(promoted_ecknet); + eclk.buf = promoted_ecknet; // Insert TRELLIS_ECLKBUF to isolate edge clock from general routing std::unique_ptr eclkbuf = @@ -2052,17 +2036,13 @@ class Ecp5Packer ci->ports[port].name = port; ci->ports[port].type = PORT_IN; } - - std::unique_ptr zero_cell{new CellInfo}; - std::unique_ptr zero_net{new NetInfo}; IdString name = ctx->id(ci->name.str(ctx) + "$zero$" + port.str(ctx)); - zero_cell->type = ctx->id("GND"); - zero_cell->name = name; - zero_net->name = name; - zero_cell->ports[ctx->id("GND")].type = PORT_OUT; - connect_port(ctx, zero_net.get(), zero_cell.get(), ctx->id("GND")); - connect_port(ctx, zero_net.get(), ci, port); - ctx->nets[name] = std::move(zero_net); + + auto zero_cell = std::make_unique(ctx, name, ctx->id("GND")); + NetInfo *zero_net = ctx->createNet(name); + zero_cell->addOutput(ctx->id("GND")); + connect_port(ctx, zero_net, zero_cell.get(), ctx->id("GND")); + connect_port(ctx, zero_net, ci, port); new_cells.push_back(std::move(zero_cell)); } diff --git a/fpga_interchange/pseudo_pip_model.cc b/fpga_interchange/pseudo_pip_model.cc index 39718c651b..7f2427c4f4 100644 --- a/fpga_interchange/pseudo_pip_model.cc +++ b/fpga_interchange/pseudo_pip_model.cc @@ -355,12 +355,11 @@ void PseudoPipModel::update_site(const Context *ctx, size_t site) NPNR_ASSERT(bel_data.lut_element != -1); - lut_thru_cells.emplace_back(); + lut_thru_cells.emplace_back(nullptr, IdString(), IdString(ctx->wire_lut->cell)); CellInfo &cell = lut_thru_cells.back(); cell.bel = bel; - cell.type = IdString(ctx->wire_lut->cell); NPNR_ASSERT(ctx->wire_lut->input_pins.size() == 1); cell.lut_cell.pins.push_back(IdString(ctx->wire_lut->input_pins[0])); @@ -384,7 +383,7 @@ void PseudoPipModel::update_site(const Context *ctx, size_t site) continue; } - lut_cells.emplace_back(); + lut_cells.emplace_back(nullptr, IdString(), ctx->wire_lut ? IdString(ctx->wire_lut->cell) : IdString()); CellInfo &cell = lut_cells.back(); cell.bel.tile = tile; @@ -393,7 +392,6 @@ void PseudoPipModel::update_site(const Context *ctx, size_t site) if (ctx->wire_lut == nullptr) continue; - cell.type = IdString(ctx->wire_lut->cell); NPNR_ASSERT(ctx->wire_lut->input_pins.size() == 1); cell.lut_cell.pins.push_back(IdString(ctx->wire_lut->input_pins[0])); diff --git a/fpga_interchange/site_arch.cc b/fpga_interchange/site_arch.cc index ac64446521..f78e8af44f 100644 --- a/fpga_interchange/site_arch.cc +++ b/fpga_interchange/site_arch.cc @@ -103,7 +103,8 @@ void SiteArch::archcheck() } } -SiteArch::SiteArch(const SiteInformation *site_info) : ctx(site_info->ctx), site_info(site_info) +SiteArch::SiteArch(const SiteInformation *site_info) + : ctx(site_info->ctx), site_info(site_info), blocking_net(site_info->ctx->id("$nextpnr_blocked_net")) { // Build list of input and output site ports // @@ -275,7 +276,6 @@ SiteArch::SiteArch(const SiteInformation *site_info) : ctx(site_info->ctx), site } } - blocking_net.name = ctx->id("$nextpnr_blocked_net"); blocking_site_net.net = &blocking_net; } diff --git a/generic/cells.cc b/generic/cells.cc index 44b99050f2..967afb23ca 100644 --- a/generic/cells.cc +++ b/generic/cells.cc @@ -34,13 +34,9 @@ void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir std::unique_ptr create_generic_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; - std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); - if (name.empty()) { - new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)); - } else { - new_cell->name = ctx->id(name); - } - new_cell->type = type; + IdString name_id = + name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name); + auto new_cell = std::make_unique(ctx, name_id, type); if (type == ctx->id("GENERIC_SLICE")) { new_cell->params[ctx->id("K")] = ctx->args.K; new_cell->params[ctx->id("INIT")] = 0; diff --git a/generic/pack.cc b/generic/pack.cc index 291a528dae..8bdbbed07c 100644 --- a/generic/pack.cc +++ b/generic/pack.cc @@ -140,8 +140,7 @@ static void pack_constants(Context *ctx) std::unique_ptr gnd_cell = create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), "$PACKER_GND"); gnd_cell->params[ctx->id("INIT")] = Property(0, 1 << ctx->args.K); - std::unique_ptr gnd_net = std::unique_ptr(new NetInfo); - gnd_net->name = ctx->id("$PACKER_GND_NET"); + std::unique_ptr gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); gnd_net->driver.port = ctx->id("F"); gnd_cell->ports.at(ctx->id("F")).net = gnd_net.get(); @@ -149,8 +148,7 @@ static void pack_constants(Context *ctx) std::unique_ptr vcc_cell = create_generic_cell(ctx, ctx->id("GENERIC_SLICE"), "$PACKER_VCC"); // Fill with 1s vcc_cell->params[ctx->id("INIT")] = Property(Property::S1).extract(0, (1 << ctx->args.K), Property::S1); - std::unique_ptr vcc_net = std::unique_ptr(new NetInfo); - vcc_net->name = ctx->id("$PACKER_VCC_NET"); + std::unique_ptr vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); vcc_net->driver.port = ctx->id("F"); vcc_cell->ports.at(ctx->id("F")).net = vcc_net.get(); diff --git a/gowin/cells.cc b/gowin/cells.cc index dce3f45654..67a81f39f9 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -35,13 +35,9 @@ void add_port(const Context *ctx, CellInfo *cell, IdString id, PortType dir) std::unique_ptr create_generic_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; - std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); - if (name.empty()) { - new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)); - } else { - new_cell->name = ctx->id(name); - } - new_cell->type = type; + IdString name_id = + name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name); + auto new_cell = std::make_unique(ctx, name_id, type); if (type == id_SLICE) { new_cell->params[id_INIT] = 0; new_cell->params[id_FF_USED] = 0; diff --git a/gowin/pack.cc b/gowin/pack.cc index 553eeb4e53..3d33f2d8a4 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -612,8 +612,7 @@ static void pack_constants(Context *ctx) std::unique_ptr gnd_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_GND"); gnd_cell->params[ctx->id("INIT")] = Property(0, 1 << 4); - std::unique_ptr gnd_net = std::unique_ptr(new NetInfo); - gnd_net->name = ctx->id("$PACKER_GND_NET"); + auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); gnd_net->driver.port = ctx->id("F"); gnd_cell->ports.at(ctx->id("F")).net = gnd_net.get(); @@ -621,8 +620,7 @@ static void pack_constants(Context *ctx) std::unique_ptr vcc_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_VCC"); // Fill with 1s vcc_cell->params[ctx->id("INIT")] = Property(Property::S1).extract(0, (1 << 4), Property::S1); - std::unique_ptr vcc_net = std::unique_ptr(new NetInfo); - vcc_net->name = ctx->id("$PACKER_VCC_NET"); + auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); vcc_net->driver.port = ctx->id("F"); vcc_cell->ports.at(ctx->id("F")).net = vcc_net.get(); diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 3c7e74151c..8caa9dc698 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -979,9 +979,7 @@ void read_config(Context *ctx, std::istream &in, chipconfig_t &config) IdString netName = ctx->id(name); if (ctx->nets.find(netName) == ctx->nets.end()) { - std::unique_ptr created_net = std::unique_ptr(new NetInfo); - created_net->name = netName; - ctx->nets[netName] = std::move(created_net); + ctx->createNet(netName); } WireId wire; diff --git a/ice40/cells.cc b/ice40/cells.cc index a7e5b0670c..9a75118f53 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -36,13 +36,10 @@ void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; - std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); - if (name.empty()) { - new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)); - } else { - new_cell->name = ctx->id(name); - } - new_cell->type = type; + IdString name_id = + name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name); + auto new_cell = std::make_unique(ctx, name_id, type); + if (type == ctx->id("ICESTORM_LC")) { new_cell->params[ctx->id("LUT_INIT")] = Property(0, 16); new_cell->params[ctx->id("NEG_CLK")] = Property::State::S0; @@ -459,11 +456,10 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to if (ctx->ports.count(nxio->name)) { IdString tn_netname = nxio->name; NPNR_ASSERT(!ctx->nets.count(tn_netname)); - std::unique_ptr toplevel_net{new NetInfo}; - toplevel_net->name = tn_netname; - connect_port(ctx, toplevel_net.get(), sbio, ctx->id("PACKAGE_PIN")); - ctx->ports[nxio->name].net = toplevel_net.get(); - ctx->nets[tn_netname] = std::move(toplevel_net); + ctx->net_aliases.erase(tn_netname); + NetInfo *toplevel_net = ctx->createNet(tn_netname); + connect_port(ctx, toplevel_net, sbio, ctx->id("PACKAGE_PIN")); + ctx->ports[nxio->name].net = toplevel_net; } CellInfo *tbuf = net_driven_by( diff --git a/ice40/chains.cc b/ice40/chains.cc index d0d8b043fc..fa4ce413cf 100644 --- a/ice40/chains.cc +++ b/ice40/chains.cc @@ -114,8 +114,7 @@ class ChainConstrainer lc->params[ctx->id("LUT_INIT")] = Property(65280, 16); // 0xff00: O = I3 lc->params[ctx->id("CARRY_ENABLE")] = Property::State::S1; lc->ports.at(id_O).net = cout_port.net; - std::unique_ptr co_i3_net(new NetInfo()); - co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3"); + NetInfo *co_i3_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$I3")); co_i3_net->driver = cout_port.net->driver; PortRef i3_r; i3_r.port = id_I3; @@ -125,17 +124,12 @@ class ChainConstrainer o_r.port = id_O; o_r.cell = lc.get(); cout_port.net->driver = o_r; - lc->ports.at(id_I3).net = co_i3_net.get(); - cout_port.net = co_i3_net.get(); - - IdString co_i3_name = co_i3_net->name; - NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end()); - ctx->nets[co_i3_name] = std::move(co_i3_net); + lc->ports.at(id_I3).net = co_i3_net; + cout_port.net = co_i3_net; // If COUT also connects to a CIN; preserve the carry chain if (cin_cell) { - std::unique_ptr co_cin_net(new NetInfo()); - co_cin_net->name = ctx->id(lc->name.str(ctx) + "$COUT"); + NetInfo *co_cin_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$COUT")); // Connect I1 to 1 to preserve carry chain NetInfo *vcc = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get(); @@ -150,7 +144,7 @@ class ChainConstrainer co_r.port = id_COUT; co_r.cell = lc.get(); co_cin_net->driver = co_r; - lc->ports.at(id_COUT).net = co_cin_net.get(); + lc->ports.at(id_COUT).net = co_cin_net; // Find the user corresponding to the next CIN int replaced_ports = 0; @@ -166,14 +160,11 @@ class ChainConstrainer if (fnd_user != usr.end()) { co_cin_net->users.push_back(*fnd_user); usr.erase(fnd_user); - cin_cell->ports.at(port).net = co_cin_net.get(); + cin_cell->ports.at(port).net = co_cin_net; ++replaced_ports; } } NPNR_ASSERT(replaced_ports > 0); - IdString co_cin_name = co_cin_net->name; - NPNR_ASSERT(ctx->nets.find(co_cin_name) == ctx->nets.end()); - ctx->nets[co_cin_name] = std::move(co_cin_net); } IdString name = lc->name; @@ -201,24 +192,19 @@ class ChainConstrainer i1_ref.port = ctx->id("I1"); lc->ports.at(ctx->id("I1")).net->users.push_back(i1_ref); - std::unique_ptr out_net(new NetInfo()); - out_net->name = ctx->id(lc->name.str(ctx) + "$O"); + NetInfo *out_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$O")); PortRef drv_ref; drv_ref.port = ctx->id("COUT"); drv_ref.cell = lc.get(); out_net->driver = drv_ref; - lc->ports.at(ctx->id("COUT")).net = out_net.get(); + lc->ports.at(ctx->id("COUT")).net = out_net; PortRef usr_ref; usr_ref.port = cin_port.name; usr_ref.cell = cin_cell; out_net->users.push_back(usr_ref); - cin_cell->ports.at(cin_port.name).net = out_net.get(); - - IdString out_net_name = out_net->name; - NPNR_ASSERT(ctx->nets.find(out_net_name) == ctx->nets.end()); - ctx->nets[out_net_name] = std::move(out_net); + cin_cell->ports.at(cin_port.name).net = out_net; IdString name = lc->name; ctx->assignCellInfo(lc.get()); diff --git a/ice40/pack.cc b/ice40/pack.cc index 921e73e08e..fcbdf2bdad 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -354,8 +354,7 @@ static void pack_constants(Context *ctx) std::unique_ptr gnd_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_GND"); gnd_cell->params[ctx->id("LUT_INIT")] = Property(0, 16); - std::unique_ptr gnd_net = std::unique_ptr(new NetInfo); - gnd_net->name = ctx->id("$PACKER_GND_NET"); + auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); gnd_net->driver.port = ctx->id("O"); gnd_cell->ports.at(ctx->id("O")).net = gnd_net.get(); @@ -367,8 +366,7 @@ static void pack_constants(Context *ctx) std::unique_ptr vcc_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_VCC"); vcc_cell->params[ctx->id("LUT_INIT")] = Property(1, 16); - std::unique_ptr vcc_net = std::unique_ptr(new NetInfo); - vcc_net->name = ctx->id("$PACKER_VCC_NET"); + auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); vcc_net->driver.port = ctx->id("O"); vcc_cell->ports.at(ctx->id("O")).net = vcc_net.get(); @@ -606,15 +604,14 @@ static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen pr.cell = gb.get(); pr.port = ctx->id("GLOBAL_BUFFER_OUTPUT"); - std::unique_ptr glbnet = std::unique_ptr(new NetInfo()); - glbnet->name = ctx->id(glb_name); + NetInfo *glbnet = ctx->createNet(ctx->id(glb_name)); glbnet->driver = pr; - gb->ports[ctx->id("GLOBAL_BUFFER_OUTPUT")].net = glbnet.get(); + gb->ports[ctx->id("GLOBAL_BUFFER_OUTPUT")].net = glbnet; std::vector keep_users; for (auto user : net->users) { if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) || (is_cen && is_enable_port(ctx, user)) || (is_logic && is_logic_port(ctx, user))) { - user.cell->ports[user.port].net = glbnet.get(); + user.cell->ports[user.port].net = glbnet; glbnet->users.push_back(user); } else { keep_users.push_back(user); @@ -629,7 +626,6 @@ static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen glbnet->clkconstr->period = net->clkconstr->period; } - ctx->nets[glbnet->name] = std::move(glbnet); ctx->cells[gb->name] = std::move(gb); } @@ -1026,11 +1022,10 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString pt->params[ctx->id("LUT_INIT")] = Property(65280, 16); // output is always I3 // Create LUT output net. - std::unique_ptr out_net = std::unique_ptr(new NetInfo); - out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net"); + NetInfo *out_net = ctx->createNet(ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net")); out_net->driver.cell = pt.get(); out_net->driver.port = ctx->id("O"); - pt->ports.at(ctx->id("O")).net = out_net.get(); + pt->ports.at(ctx->id("O")).net = out_net; // New users of the original cell's port std::vector new_users; @@ -1040,7 +1035,7 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString continue; } // Rewrite pointer into net in user. - user.cell->ports[user.port].net = out_net.get(); + user.cell->ports[user.port].net = out_net; // Add user to net. PortRef pr; pr.cell = user.cell; @@ -1058,7 +1053,6 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString // Replace users of the original net. port.net->users = new_users; - ctx->nets[out_net->name] = std::move(out_net); return pt; } diff --git a/machxo2/cells.cc b/machxo2/cells.cc index 7334234d62..534d8e3cc9 100644 --- a/machxo2/cells.cc +++ b/machxo2/cells.cc @@ -41,13 +41,9 @@ void add_port(const Context *ctx, CellInfo *cell, IdString id, PortType dir) std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; - std::unique_ptr new_cell = std::unique_ptr(new CellInfo()); - if (name.empty()) { - new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)); - } else { - new_cell->name = ctx->id(name); - } - new_cell->type = type; + IdString name_id = + name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name); + auto new_cell = std::make_unique(ctx, name_id, type); if (type == id_FACADE_SLICE) { new_cell->params[id_MODE] = std::string("LOGIC"); diff --git a/machxo2/pack.cc b/machxo2/pack.cc index c53229ba01..0c2c945922 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -183,17 +183,16 @@ static void pack_constants(Context *ctx) const_cell->params[id_LUT0_INITVAL] = Property(0, 16); const_cell->params[id_LUT1_INITVAL] = Property(0xFFFF, 16); - std::unique_ptr gnd_net = std::unique_ptr(new NetInfo); - gnd_net->name = ctx->id("$PACKER_GND_NET"); + NetInfo *gnd_net = ctx->createNet(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = const_cell.get(); gnd_net->driver.port = id_F0; - const_cell->ports.at(id_F0).net = gnd_net.get(); + const_cell->ports.at(id_F0).net = gnd_net; - std::unique_ptr vcc_net = std::unique_ptr(new NetInfo); + NetInfo *vcc_net = ctx->createNet(ctx->id("$PACKER_VCC_NET")); vcc_net->name = ctx->id("$PACKER_VCC_NET"); vcc_net->driver.cell = const_cell.get(); vcc_net->driver.port = id_F1; - const_cell->ports.at(id_F1).net = vcc_net.get(); + const_cell->ports.at(id_F1).net = vcc_net; std::vector dead_nets; @@ -201,20 +200,18 @@ static void pack_constants(Context *ctx) NetInfo *ni = net.second.get(); if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { IdString drv_cell = ni->driver.cell->name; - set_net_constant(ctx, ni, gnd_net.get(), false); + set_net_constant(ctx, ni, gnd_net, false); dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) { IdString drv_cell = ni->driver.cell->name; - set_net_constant(ctx, ni, vcc_net.get(), true); + set_net_constant(ctx, ni, vcc_net, true); dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); } } ctx->cells[const_cell->name] = std::move(const_cell); - ctx->nets[gnd_net->name] = std::move(gnd_net); - ctx->nets[vcc_net->name] = std::move(vcc_net); for (auto dn : dead_nets) { ctx->nets.erase(dn); From 9ef0bc3d3ad667d937ed803eba7b216a604d5624 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 16 Feb 2022 16:45:45 +0000 Subject: [PATCH 067/712] refactor: Use cell member functions to add ports Signed-off-by: gatecat --- ecp5/cells.cc | 164 +++++++++++------------ generic/cells.cc | 23 ++-- gowin/cells.cc | 34 ++--- ice40/cells.cc | 331 +++++++++++++++++++++++------------------------ machxo2/cells.cc | 131 +++++++++---------- 5 files changed, 322 insertions(+), 361 deletions(-) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index e5ab4d4b7d..18d9107f51 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -25,12 +25,6 @@ NEXTPNR_NAMESPACE_BEGIN -void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) -{ - IdString id = ctx->id(name); - cell->ports[id] = PortInfo{id, nullptr, dir}; -} - std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; @@ -49,7 +43,7 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str } NPNR_ASSERT(tgt != BelId()); for (auto port : ctx->getBelPins(tgt)) { - add_port(ctx, new_cell.get(), port.str(ctx), ctx->getBelPinType(tgt, port)); + new_cell->ports[port] = PortInfo{port, nullptr, ctx->getBelPinType(tgt, port)}; } }; @@ -70,104 +64,104 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->params[ctx->id("CCU2_INJECT1_1")] = std::string("NO"); new_cell->params[ctx->id("WREMUX")] = std::string("WRE"); - add_port(ctx, new_cell.get(), "A0", PORT_IN); - add_port(ctx, new_cell.get(), "B0", PORT_IN); - add_port(ctx, new_cell.get(), "C0", PORT_IN); - add_port(ctx, new_cell.get(), "D0", PORT_IN); - - add_port(ctx, new_cell.get(), "A1", PORT_IN); - add_port(ctx, new_cell.get(), "B1", PORT_IN); - add_port(ctx, new_cell.get(), "C1", PORT_IN); - add_port(ctx, new_cell.get(), "D1", PORT_IN); - - add_port(ctx, new_cell.get(), "M0", PORT_IN); - add_port(ctx, new_cell.get(), "M1", PORT_IN); - - add_port(ctx, new_cell.get(), "FCI", PORT_IN); - add_port(ctx, new_cell.get(), "FXA", PORT_IN); - add_port(ctx, new_cell.get(), "FXB", PORT_IN); - - add_port(ctx, new_cell.get(), "CLK", PORT_IN); - add_port(ctx, new_cell.get(), "LSR", PORT_IN); - add_port(ctx, new_cell.get(), "CE", PORT_IN); - - add_port(ctx, new_cell.get(), "DI0", PORT_IN); - add_port(ctx, new_cell.get(), "DI1", PORT_IN); - - add_port(ctx, new_cell.get(), "WD0", PORT_IN); - add_port(ctx, new_cell.get(), "WD1", PORT_IN); - add_port(ctx, new_cell.get(), "WAD0", PORT_IN); - add_port(ctx, new_cell.get(), "WAD1", PORT_IN); - add_port(ctx, new_cell.get(), "WAD2", PORT_IN); - add_port(ctx, new_cell.get(), "WAD3", PORT_IN); - add_port(ctx, new_cell.get(), "WRE", PORT_IN); - add_port(ctx, new_cell.get(), "WCK", PORT_IN); - - add_port(ctx, new_cell.get(), "F0", PORT_OUT); - add_port(ctx, new_cell.get(), "Q0", PORT_OUT); - add_port(ctx, new_cell.get(), "F1", PORT_OUT); - add_port(ctx, new_cell.get(), "Q1", PORT_OUT); - - add_port(ctx, new_cell.get(), "FCO", PORT_OUT); - add_port(ctx, new_cell.get(), "OFX0", PORT_OUT); - add_port(ctx, new_cell.get(), "OFX1", PORT_OUT); - - add_port(ctx, new_cell.get(), "WDO0", PORT_OUT); - add_port(ctx, new_cell.get(), "WDO1", PORT_OUT); - add_port(ctx, new_cell.get(), "WDO2", PORT_OUT); - add_port(ctx, new_cell.get(), "WDO3", PORT_OUT); - add_port(ctx, new_cell.get(), "WADO0", PORT_OUT); - add_port(ctx, new_cell.get(), "WADO1", PORT_OUT); - add_port(ctx, new_cell.get(), "WADO2", PORT_OUT); - add_port(ctx, new_cell.get(), "WADO3", PORT_OUT); + new_cell->addInput(ctx->id("A0")); + new_cell->addInput(ctx->id("B0")); + new_cell->addInput(ctx->id("C0")); + new_cell->addInput(ctx->id("D0")); + + new_cell->addInput(ctx->id("A1")); + new_cell->addInput(ctx->id("B1")); + new_cell->addInput(ctx->id("C1")); + new_cell->addInput(ctx->id("D1")); + + new_cell->addInput(ctx->id("M0")); + new_cell->addInput(ctx->id("M1")); + + new_cell->addInput(ctx->id("FCI")); + new_cell->addInput(ctx->id("FXA")); + new_cell->addInput(ctx->id("FXB")); + + new_cell->addInput(ctx->id("CLK")); + new_cell->addInput(ctx->id("LSR")); + new_cell->addInput(ctx->id("CE")); + + new_cell->addInput(ctx->id("DI0")); + new_cell->addInput(ctx->id("DI1")); + + new_cell->addInput(ctx->id("WD0")); + new_cell->addInput(ctx->id("WD1")); + new_cell->addInput(ctx->id("WAD0")); + new_cell->addInput(ctx->id("WAD1")); + new_cell->addInput(ctx->id("WAD2")); + new_cell->addInput(ctx->id("WAD3")); + new_cell->addInput(ctx->id("WRE")); + new_cell->addInput(ctx->id("WCK")); + + new_cell->addOutput(ctx->id("F0")); + new_cell->addOutput(ctx->id("Q0")); + new_cell->addOutput(ctx->id("F1")); + new_cell->addOutput(ctx->id("Q1")); + + new_cell->addOutput(ctx->id("FCO")); + new_cell->addOutput(ctx->id("OFX0")); + new_cell->addOutput(ctx->id("OFX1")); + + new_cell->addOutput(ctx->id("WDO0")); + new_cell->addOutput(ctx->id("WDO1")); + new_cell->addOutput(ctx->id("WDO2")); + new_cell->addOutput(ctx->id("WDO3")); + new_cell->addOutput(ctx->id("WADO0")); + new_cell->addOutput(ctx->id("WADO1")); + new_cell->addOutput(ctx->id("WADO2")); + new_cell->addOutput(ctx->id("WADO3")); } else if (type == ctx->id("TRELLIS_IO")) { new_cell->params[ctx->id("DIR")] = std::string("INPUT"); new_cell->attrs[ctx->id("IO_TYPE")] = std::string("LVCMOS33"); new_cell->params[ctx->id("DATAMUX_ODDR")] = std::string("PADDO"); new_cell->params[ctx->id("DATAMUX_MDDR")] = std::string("PADDO"); - add_port(ctx, new_cell.get(), "B", PORT_INOUT); - add_port(ctx, new_cell.get(), "I", PORT_IN); - add_port(ctx, new_cell.get(), "T", PORT_IN); - add_port(ctx, new_cell.get(), "O", PORT_OUT); + new_cell->addInout(ctx->id("B")); + new_cell->addInput(ctx->id("I")); + new_cell->addInput(ctx->id("T")); + new_cell->addOutput(ctx->id("O")); - add_port(ctx, new_cell.get(), "IOLDO", PORT_IN); - add_port(ctx, new_cell.get(), "IOLTO", PORT_IN); + new_cell->addInput(ctx->id("IOLDO")); + new_cell->addInput(ctx->id("IOLTO")); } else if (type == ctx->id("LUT4")) { new_cell->params[ctx->id("INIT")] = Property(0, 16); - add_port(ctx, new_cell.get(), "A", PORT_IN); - add_port(ctx, new_cell.get(), "B", PORT_IN); - add_port(ctx, new_cell.get(), "C", PORT_IN); - add_port(ctx, new_cell.get(), "D", PORT_IN); - add_port(ctx, new_cell.get(), "Z", PORT_OUT); + new_cell->addInput(ctx->id("A")); + new_cell->addInput(ctx->id("B")); + new_cell->addInput(ctx->id("C")); + new_cell->addInput(ctx->id("D")); + new_cell->addOutput(ctx->id("Z")); } else if (type == ctx->id("CCU2C")) { new_cell->params[ctx->id("INIT0")] = Property(0, 16); new_cell->params[ctx->id("INIT1")] = Property(0, 16); new_cell->params[ctx->id("INJECT1_0")] = std::string("YES"); new_cell->params[ctx->id("INJECT1_1")] = std::string("YES"); - add_port(ctx, new_cell.get(), "CIN", PORT_IN); + new_cell->addInput(ctx->id("CIN")); - add_port(ctx, new_cell.get(), "A0", PORT_IN); - add_port(ctx, new_cell.get(), "B0", PORT_IN); - add_port(ctx, new_cell.get(), "C0", PORT_IN); - add_port(ctx, new_cell.get(), "D0", PORT_IN); + new_cell->addInput(ctx->id("A0")); + new_cell->addInput(ctx->id("B0")); + new_cell->addInput(ctx->id("C0")); + new_cell->addInput(ctx->id("D0")); - add_port(ctx, new_cell.get(), "A1", PORT_IN); - add_port(ctx, new_cell.get(), "B1", PORT_IN); - add_port(ctx, new_cell.get(), "C1", PORT_IN); - add_port(ctx, new_cell.get(), "D1", PORT_IN); + new_cell->addInput(ctx->id("A1")); + new_cell->addInput(ctx->id("B1")); + new_cell->addInput(ctx->id("C1")); + new_cell->addInput(ctx->id("D1")); - add_port(ctx, new_cell.get(), "S0", PORT_OUT); - add_port(ctx, new_cell.get(), "S1", PORT_OUT); - add_port(ctx, new_cell.get(), "COUT", PORT_OUT); + new_cell->addOutput(ctx->id("S0")); + new_cell->addOutput(ctx->id("S1")); + new_cell->addOutput(ctx->id("COUT")); } else if (type == ctx->id("DCCA")) { - add_port(ctx, new_cell.get(), "CLKI", PORT_IN); - add_port(ctx, new_cell.get(), "CLKO", PORT_OUT); - add_port(ctx, new_cell.get(), "CE", PORT_IN); + new_cell->addInput(ctx->id("CLKI")); + new_cell->addOutput(ctx->id("CLKO")); + new_cell->addInput(ctx->id("CE")); } else if (type == id_IOLOGIC || type == id_SIOLOGIC) { new_cell->params[ctx->id("MODE")] = std::string("NONE"); new_cell->params[ctx->id("GSR")] = std::string("DISABLED"); @@ -198,8 +192,8 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str // Just copy ports from the Bel copy_bel_ports(); } else if (type == id_TRELLIS_ECLKBUF) { - add_port(ctx, new_cell.get(), "ECLKI", PORT_IN); - add_port(ctx, new_cell.get(), "ECLKO", PORT_OUT); + new_cell->addInput(ctx->id("ECLKI")); + new_cell->addOutput(ctx->id("ECLKO")); } else { log_error("unable to create ECP5 cell of type %s", type.c_str(ctx)); } diff --git a/generic/cells.cc b/generic/cells.cc index 967afb23ca..c14ddf7306 100644 --- a/generic/cells.cc +++ b/generic/cells.cc @@ -24,13 +24,6 @@ NEXTPNR_NAMESPACE_BEGIN -void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) -{ - IdString id = ctx->id(name); - NPNR_ASSERT(cell->ports.count(id) == 0); - cell->ports[id] = PortInfo{id, nullptr, dir}; -} - std::unique_ptr create_generic_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; @@ -43,21 +36,21 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->params[ctx->id("FF_USED")] = 0; for (int i = 0; i < ctx->args.K; i++) - add_port(ctx, new_cell.get(), "I[" + std::to_string(i) + "]", PORT_IN); + new_cell->addInput(ctx->id("I[" + std::to_string(i) + "]")); - add_port(ctx, new_cell.get(), "CLK", PORT_IN); + new_cell->addInput(ctx->id("CLK")); - add_port(ctx, new_cell.get(), "F", PORT_OUT); - add_port(ctx, new_cell.get(), "Q", PORT_OUT); + new_cell->addOutput(ctx->id("F")); + new_cell->addOutput(ctx->id("Q")); } else if (type == ctx->id("GENERIC_IOB")) { new_cell->params[ctx->id("INPUT_USED")] = 0; new_cell->params[ctx->id("OUTPUT_USED")] = 0; new_cell->params[ctx->id("ENABLE_USED")] = 0; - add_port(ctx, new_cell.get(), "PAD", PORT_INOUT); - add_port(ctx, new_cell.get(), "I", PORT_IN); - add_port(ctx, new_cell.get(), "EN", PORT_IN); - add_port(ctx, new_cell.get(), "O", PORT_OUT); + new_cell->addInout(ctx->id("PAD")); + new_cell->addInput(ctx->id("I")); + new_cell->addInput(ctx->id("EN")); + new_cell->addOutput(ctx->id("O")); } else { log_error("unable to create generic cell of type %s", type.c_str(ctx)); } diff --git a/gowin/cells.cc b/gowin/cells.cc index 67a81f39f9..aef34f535a 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -26,12 +26,6 @@ NEXTPNR_NAMESPACE_BEGIN -void add_port(const Context *ctx, CellInfo *cell, IdString id, PortType dir) -{ - NPNR_ASSERT(cell->ports.count(id) == 0); - cell->ports[id] = PortInfo{id, nullptr, dir}; -} - std::unique_ptr create_generic_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; @@ -45,30 +39,30 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: IdString names[4] = {id_A, id_B, id_C, id_D}; for (int i = 0; i < 4; i++) { - add_port(ctx, new_cell.get(), names[i], PORT_IN); + new_cell->addInput(names[i]); } - add_port(ctx, new_cell.get(), id_CLK, PORT_IN); + new_cell->addInput(id_CLK); - add_port(ctx, new_cell.get(), id_F, PORT_OUT); - add_port(ctx, new_cell.get(), id_Q, PORT_OUT); - add_port(ctx, new_cell.get(), id_CE, PORT_IN); - add_port(ctx, new_cell.get(), id_LSR, PORT_IN); + new_cell->addOutput(id_F); + new_cell->addOutput(id_Q); + new_cell->addInput(id_CE); + new_cell->addInput(id_LSR); } else if (type == id_GW_MUX2_LUT5 || type == id_GW_MUX2_LUT6 || type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT8) { - add_port(ctx, new_cell.get(), id_I0, PORT_IN); - add_port(ctx, new_cell.get(), id_I1, PORT_IN); - add_port(ctx, new_cell.get(), id_SEL, PORT_IN); - add_port(ctx, new_cell.get(), id_OF, PORT_OUT); + new_cell->addInput(id_I0); + new_cell->addInput(id_I1); + new_cell->addInput(id_SEL); + new_cell->addOutput(id_OF); } else if (type == id_IOB || type == id_IOBS) { new_cell->params[id_INPUT_USED] = 0; new_cell->params[id_OUTPUT_USED] = 0; new_cell->params[id_ENABLE_USED] = 0; - add_port(ctx, new_cell.get(), id_PAD, PORT_INOUT); - add_port(ctx, new_cell.get(), id_I, PORT_IN); - add_port(ctx, new_cell.get(), id_OEN, PORT_IN); - add_port(ctx, new_cell.get(), id_O, PORT_OUT); + new_cell->addInout(id_PAD); + new_cell->addInput(id_I); + new_cell->addInput(id_OEN); + new_cell->addOutput(id_O); } else { log_error("unable to create generic cell of type %s\n", type.c_str(ctx)); } diff --git a/ice40/cells.cc b/ice40/cells.cc index 9a75118f53..b97131a62e 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -26,13 +26,6 @@ NEXTPNR_NAMESPACE_BEGIN -void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) -{ - IdString id = ctx->id(name); - NPNR_ASSERT(cell->ports.count(id) == 0); - cell->ports[id] = PortInfo{id, nullptr, dir}; -} - std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; @@ -50,97 +43,97 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri new_cell->params[ctx->id("CIN_CONST")] = Property::State::S0; new_cell->params[ctx->id("CIN_SET")] = Property::State::S0; - add_port(ctx, new_cell.get(), "I0", PORT_IN); - add_port(ctx, new_cell.get(), "I1", PORT_IN); - add_port(ctx, new_cell.get(), "I2", PORT_IN); - add_port(ctx, new_cell.get(), "I3", PORT_IN); - add_port(ctx, new_cell.get(), "CIN", PORT_IN); + new_cell->addInput(ctx->id("I0")); + new_cell->addInput(ctx->id("I1")); + new_cell->addInput(ctx->id("I2")); + new_cell->addInput(ctx->id("I3")); + new_cell->addInput(ctx->id("CIN")); - add_port(ctx, new_cell.get(), "CLK", PORT_IN); - add_port(ctx, new_cell.get(), "CEN", PORT_IN); - add_port(ctx, new_cell.get(), "SR", PORT_IN); + new_cell->addInput(ctx->id("CLK")); + new_cell->addInput(ctx->id("CEN")); + new_cell->addInput(ctx->id("SR")); - add_port(ctx, new_cell.get(), "LO", PORT_OUT); - add_port(ctx, new_cell.get(), "O", PORT_OUT); - add_port(ctx, new_cell.get(), "COUT", PORT_OUT); + new_cell->addOutput(ctx->id("LO")); + new_cell->addOutput(ctx->id("O")); + new_cell->addOutput(ctx->id("COUT")); } else if (type == ctx->id("SB_IO")) { new_cell->params[ctx->id("PIN_TYPE")] = Property(0, 6); new_cell->params[ctx->id("PULLUP")] = Property::State::S0; new_cell->params[ctx->id("NEG_TRIGGER")] = Property::State::S0; new_cell->params[ctx->id("IO_STANDARD")] = Property("SB_LVCMOS"); - add_port(ctx, new_cell.get(), "PACKAGE_PIN", PORT_INOUT); + new_cell->addInout(ctx->id("PACKAGE_PIN")); - add_port(ctx, new_cell.get(), "LATCH_INPUT_VALUE", PORT_IN); - add_port(ctx, new_cell.get(), "CLOCK_ENABLE", PORT_IN); - add_port(ctx, new_cell.get(), "INPUT_CLK", PORT_IN); - add_port(ctx, new_cell.get(), "OUTPUT_CLK", PORT_IN); + new_cell->addInput(ctx->id("LATCH_INPUT_VALUE")); + new_cell->addInput(ctx->id("CLOCK_ENABLE")); + new_cell->addInput(ctx->id("INPUT_CLK")); + new_cell->addInput(ctx->id("OUTPUT_CLK")); - add_port(ctx, new_cell.get(), "OUTPUT_ENABLE", PORT_IN); - add_port(ctx, new_cell.get(), "D_OUT_0", PORT_IN); - add_port(ctx, new_cell.get(), "D_OUT_1", PORT_IN); + new_cell->addInput(ctx->id("OUTPUT_ENABLE")); + new_cell->addInput(ctx->id("D_OUT_0")); + new_cell->addInput(ctx->id("D_OUT_1")); - add_port(ctx, new_cell.get(), "D_IN_0", PORT_OUT); - add_port(ctx, new_cell.get(), "D_IN_1", PORT_OUT); + new_cell->addOutput(ctx->id("D_IN_0")); + new_cell->addOutput(ctx->id("D_IN_1")); } else if (type == ctx->id("ICESTORM_RAM")) { new_cell->params[ctx->id("NEG_CLK_W")] = Property::State::S0; new_cell->params[ctx->id("NEG_CLK_R")] = Property::State::S0; new_cell->params[ctx->id("WRITE_MODE")] = Property::State::S0; new_cell->params[ctx->id("READ_MODE")] = Property::State::S0; - add_port(ctx, new_cell.get(), "RCLK", PORT_IN); - add_port(ctx, new_cell.get(), "RCLKE", PORT_IN); - add_port(ctx, new_cell.get(), "RE", PORT_IN); + new_cell->addInput(ctx->id("RCLK")); + new_cell->addInput(ctx->id("RCLKE")); + new_cell->addInput(ctx->id("RE")); - add_port(ctx, new_cell.get(), "WCLK", PORT_IN); - add_port(ctx, new_cell.get(), "WCLKE", PORT_IN); - add_port(ctx, new_cell.get(), "WE", PORT_IN); + new_cell->addInput(ctx->id("WCLK")); + new_cell->addInput(ctx->id("WCLKE")); + new_cell->addInput(ctx->id("WE")); for (int i = 0; i < 16; i++) { - add_port(ctx, new_cell.get(), "WDATA_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "MASK_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "RDATA_" + std::to_string(i), PORT_OUT); + new_cell->addInput(ctx->id("WDATA_" + std::to_string(i))); + new_cell->addInput(ctx->id("MASK_" + std::to_string(i))); + new_cell->addOutput(ctx->id("RDATA_" + std::to_string(i))); } for (int i = 0; i < 11; i++) { - add_port(ctx, new_cell.get(), "RADDR_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "WADDR_" + std::to_string(i), PORT_IN); + new_cell->addInput(ctx->id("RADDR_" + std::to_string(i))); + new_cell->addInput(ctx->id("WADDR_" + std::to_string(i))); } } else if (type == ctx->id("ICESTORM_LFOSC")) { - add_port(ctx, new_cell.get(), "CLKLFEN", PORT_IN); - add_port(ctx, new_cell.get(), "CLKLFPU", PORT_IN); - add_port(ctx, new_cell.get(), "CLKLF", PORT_OUT); - add_port(ctx, new_cell.get(), "CLKLF_FABRIC", PORT_OUT); + new_cell->addInput(ctx->id("CLKLFEN")); + new_cell->addInput(ctx->id("CLKLFPU")); + new_cell->addOutput(ctx->id("CLKLF")); + new_cell->addOutput(ctx->id("CLKLF_FABRIC")); } else if (type == ctx->id("ICESTORM_HFOSC")) { new_cell->params[ctx->id("CLKHF_DIV")] = Property("0b00"); new_cell->params[ctx->id("TRIM_EN")] = Property("0b0"); - add_port(ctx, new_cell.get(), "CLKHFEN", PORT_IN); - add_port(ctx, new_cell.get(), "CLKHFPU", PORT_IN); - add_port(ctx, new_cell.get(), "CLKHF", PORT_OUT); - add_port(ctx, new_cell.get(), "CLKHF_FABRIC", PORT_OUT); + new_cell->addInput(ctx->id("CLKHFEN")); + new_cell->addInput(ctx->id("CLKHFPU")); + new_cell->addOutput(ctx->id("CLKHF")); + new_cell->addOutput(ctx->id("CLKHF_FABRIC")); for (int i = 0; i < 10; i++) - add_port(ctx, new_cell.get(), "TRIM" + std::to_string(i), PORT_IN); + new_cell->addInput(ctx->id("TRIM" + std::to_string(i))); } else if (type == ctx->id("SB_GB")) { - add_port(ctx, new_cell.get(), "USER_SIGNAL_TO_GLOBAL_BUFFER", PORT_IN); - add_port(ctx, new_cell.get(), "GLOBAL_BUFFER_OUTPUT", PORT_OUT); + new_cell->addInput(ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER")); + new_cell->addOutput(ctx->id("GLOBAL_BUFFER_OUTPUT")); } else if (type == ctx->id("ICESTORM_SPRAM")) { - add_port(ctx, new_cell.get(), "WREN", PORT_IN); - add_port(ctx, new_cell.get(), "CHIPSELECT", PORT_IN); - add_port(ctx, new_cell.get(), "CLOCK", PORT_IN); - add_port(ctx, new_cell.get(), "STANDBY", PORT_IN); - add_port(ctx, new_cell.get(), "SLEEP", PORT_IN); - add_port(ctx, new_cell.get(), "POWEROFF", PORT_IN); + new_cell->addInput(ctx->id("WREN")); + new_cell->addInput(ctx->id("CHIPSELECT")); + new_cell->addInput(ctx->id("CLOCK")); + new_cell->addInput(ctx->id("STANDBY")); + new_cell->addInput(ctx->id("SLEEP")); + new_cell->addInput(ctx->id("POWEROFF")); for (int i = 0; i < 16; i++) { - add_port(ctx, new_cell.get(), "DATAIN_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "DATAOUT_" + std::to_string(i), PORT_OUT); + new_cell->addInput(ctx->id("DATAIN_" + std::to_string(i))); + new_cell->addOutput(ctx->id("DATAOUT_" + std::to_string(i))); } for (int i = 0; i < 14; i++) { - add_port(ctx, new_cell.get(), "ADDRESS_" + std::to_string(i), PORT_IN); + new_cell->addInput(ctx->id("ADDRESS_" + std::to_string(i))); } for (int i = 0; i < 4; i++) { - add_port(ctx, new_cell.get(), "MASKWREN_" + std::to_string(i), PORT_IN); + new_cell->addInput(ctx->id("MASKWREN_" + std::to_string(i))); } } else if (type == ctx->id("ICESTORM_DSP")) { new_cell->params[ctx->id("NEG_TRIGGER")] = Property::State::S0; @@ -168,44 +161,44 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri new_cell->params[ctx->id("A_SIGNED")] = Property::State::S0; new_cell->params[ctx->id("B_SIGNED")] = Property::State::S0; - add_port(ctx, new_cell.get(), "CLK", PORT_IN); - add_port(ctx, new_cell.get(), "CE", PORT_IN); + new_cell->addInput(ctx->id("CLK")); + new_cell->addInput(ctx->id("CE")); for (int i = 0; i < 16; i++) { - add_port(ctx, new_cell.get(), "C_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "A_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "B_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "D_" + std::to_string(i), PORT_IN); + new_cell->addInput(ctx->id("C_" + std::to_string(i))); + new_cell->addInput(ctx->id("A_" + std::to_string(i))); + new_cell->addInput(ctx->id("B_" + std::to_string(i))); + new_cell->addInput(ctx->id("D_" + std::to_string(i))); } - add_port(ctx, new_cell.get(), "AHOLD", PORT_IN); - add_port(ctx, new_cell.get(), "BHOLD", PORT_IN); - add_port(ctx, new_cell.get(), "CHOLD", PORT_IN); - add_port(ctx, new_cell.get(), "DHOLD", PORT_IN); + new_cell->addInput(ctx->id("AHOLD")); + new_cell->addInput(ctx->id("BHOLD")); + new_cell->addInput(ctx->id("CHOLD")); + new_cell->addInput(ctx->id("DHOLD")); - add_port(ctx, new_cell.get(), "IRSTTOP", PORT_IN); - add_port(ctx, new_cell.get(), "IRSTBOT", PORT_IN); - add_port(ctx, new_cell.get(), "ORSTTOP", PORT_IN); - add_port(ctx, new_cell.get(), "ORSTBOT", PORT_IN); + new_cell->addInput(ctx->id("IRSTTOP")); + new_cell->addInput(ctx->id("IRSTBOT")); + new_cell->addInput(ctx->id("ORSTTOP")); + new_cell->addInput(ctx->id("ORSTBOT")); - add_port(ctx, new_cell.get(), "OLOADTOP", PORT_IN); - add_port(ctx, new_cell.get(), "OLOADBOT", PORT_IN); + new_cell->addInput(ctx->id("OLOADTOP")); + new_cell->addInput(ctx->id("OLOADBOT")); - add_port(ctx, new_cell.get(), "ADDSUBTOP", PORT_IN); - add_port(ctx, new_cell.get(), "ADDSUBBOT", PORT_IN); + new_cell->addInput(ctx->id("ADDSUBTOP")); + new_cell->addInput(ctx->id("ADDSUBBOT")); - add_port(ctx, new_cell.get(), "OHOLDTOP", PORT_IN); - add_port(ctx, new_cell.get(), "OHOLDBOT", PORT_IN); + new_cell->addInput(ctx->id("OHOLDTOP")); + new_cell->addInput(ctx->id("OHOLDBOT")); - add_port(ctx, new_cell.get(), "CI", PORT_IN); - add_port(ctx, new_cell.get(), "ACCUMCI", PORT_IN); - add_port(ctx, new_cell.get(), "SIGNEXTIN", PORT_IN); + new_cell->addInput(ctx->id("CI")); + new_cell->addInput(ctx->id("ACCUMCI")); + new_cell->addInput(ctx->id("SIGNEXTIN")); for (int i = 0; i < 32; i++) { - add_port(ctx, new_cell.get(), "O_" + std::to_string(i), PORT_OUT); + new_cell->addOutput(ctx->id("O_" + std::to_string(i))); } - add_port(ctx, new_cell.get(), "CO", PORT_OUT); - add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT); - add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT); + new_cell->addOutput(ctx->id("CO")); + new_cell->addOutput(ctx->id("ACCUMCO")); + new_cell->addOutput(ctx->id("SIGNEXTOUT")); } else if (type == ctx->id("ICESTORM_PLL")) { new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = Property::State::S0; @@ -227,114 +220,114 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = Property::State::S0; new_cell->params[ctx->id("TEST_MODE")] = Property::State::S0; - add_port(ctx, new_cell.get(), "BYPASS", PORT_IN); + new_cell->addInput(ctx->id("BYPASS")); for (int i = 0; i < 8; i++) - add_port(ctx, new_cell.get(), "DYNAMICDELAY_" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN); - add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN); - add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN); - add_port(ctx, new_cell.get(), "RESETB", PORT_IN); - - add_port(ctx, new_cell.get(), "SCLK", PORT_IN); - add_port(ctx, new_cell.get(), "SDI", PORT_IN); - add_port(ctx, new_cell.get(), "SDO", PORT_OUT); - - add_port(ctx, new_cell.get(), "LOCK", PORT_OUT); - add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT); - add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT); - add_port(ctx, new_cell.get(), "PLLOUT_A_GLOBAL", PORT_OUT); - add_port(ctx, new_cell.get(), "PLLOUT_B_GLOBAL", PORT_OUT); + new_cell->addInput(ctx->id("DYNAMICDELAY_" + std::to_string(i))); + new_cell->addInput(ctx->id("EXTFEEDBACK")); + new_cell->addInput(ctx->id("LATCHINPUTVALUE")); + new_cell->addInput(ctx->id("REFERENCECLK")); + new_cell->addInput(ctx->id("RESETB")); + + new_cell->addInput(ctx->id("SCLK")); + new_cell->addInput(ctx->id("SDI")); + new_cell->addOutput(ctx->id("SDO")); + + new_cell->addOutput(ctx->id("LOCK")); + new_cell->addOutput(ctx->id("PLLOUT_A")); + new_cell->addOutput(ctx->id("PLLOUT_B")); + new_cell->addOutput(ctx->id("PLLOUT_A_GLOBAL")); + new_cell->addOutput(ctx->id("PLLOUT_B_GLOBAL")); } else if (type == ctx->id("SB_RGBA_DRV")) { new_cell->params[ctx->id("CURRENT_MODE")] = std::string("0b0"); new_cell->params[ctx->id("RGB0_CURRENT")] = std::string("0b000000"); new_cell->params[ctx->id("RGB1_CURRENT")] = std::string("0b000000"); new_cell->params[ctx->id("RGB2_CURRENT")] = std::string("0b000000"); - add_port(ctx, new_cell.get(), "CURREN", PORT_IN); - add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN); - add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN); - add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN); - add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN); - add_port(ctx, new_cell.get(), "RGB0", PORT_OUT); - add_port(ctx, new_cell.get(), "RGB1", PORT_OUT); - add_port(ctx, new_cell.get(), "RGB2", PORT_OUT); + new_cell->addInput(ctx->id("CURREN")); + new_cell->addInput(ctx->id("RGBLEDEN")); + new_cell->addInput(ctx->id("RGB0PWM")); + new_cell->addInput(ctx->id("RGB1PWM")); + new_cell->addInput(ctx->id("RGB2PWM")); + new_cell->addOutput(ctx->id("RGB0")); + new_cell->addOutput(ctx->id("RGB1")); + new_cell->addOutput(ctx->id("RGB2")); } else if (type == ctx->id("SB_LED_DRV_CUR")) { - add_port(ctx, new_cell.get(), "EN", PORT_IN); - add_port(ctx, new_cell.get(), "LEDPU", PORT_OUT); + new_cell->addInput(ctx->id("EN")); + new_cell->addOutput(ctx->id("LEDPU")); } else if (type == ctx->id("SB_RGB_DRV")) { new_cell->params[ctx->id("RGB0_CURRENT")] = std::string("0b000000"); new_cell->params[ctx->id("RGB1_CURRENT")] = std::string("0b000000"); new_cell->params[ctx->id("RGB2_CURRENT")] = std::string("0b000000"); - add_port(ctx, new_cell.get(), "RGBPU", PORT_IN); - add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN); - add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN); - add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN); - add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN); - add_port(ctx, new_cell.get(), "RGB0", PORT_OUT); - add_port(ctx, new_cell.get(), "RGB1", PORT_OUT); - add_port(ctx, new_cell.get(), "RGB2", PORT_OUT); + new_cell->addInput(ctx->id("RGBPU")); + new_cell->addInput(ctx->id("RGBLEDEN")); + new_cell->addInput(ctx->id("RGB0PWM")); + new_cell->addInput(ctx->id("RGB1PWM")); + new_cell->addInput(ctx->id("RGB2PWM")); + new_cell->addOutput(ctx->id("RGB0")); + new_cell->addOutput(ctx->id("RGB1")); + new_cell->addOutput(ctx->id("RGB2")); } else if (type == ctx->id("SB_LEDDA_IP")) { - add_port(ctx, new_cell.get(), "LEDDCS", PORT_IN); - add_port(ctx, new_cell.get(), "LEDDCLK", PORT_IN); + new_cell->addInput(ctx->id("LEDDCS")); + new_cell->addInput(ctx->id("LEDDCLK")); for (int i = 0; i < 8; i++) - add_port(ctx, new_cell.get(), "LEDDDAT" + std::to_string(i), PORT_IN); + new_cell->addInput(ctx->id("LEDDDAT" + std::to_string(i))); for (int i = 0; i < 3; i++) - add_port(ctx, new_cell.get(), "LEDDADDR" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "LEDDDEN", PORT_IN); - add_port(ctx, new_cell.get(), "LEDDEXE", PORT_IN); - add_port(ctx, new_cell.get(), "LEDDRST", PORT_IN); // doesn't actually exist, for icecube code compatibility - // only - add_port(ctx, new_cell.get(), "PWMOUT0", PORT_OUT); - add_port(ctx, new_cell.get(), "PWMOUT1", PORT_OUT); - add_port(ctx, new_cell.get(), "PWMOUT2", PORT_OUT); - add_port(ctx, new_cell.get(), "LEDDON", PORT_OUT); + new_cell->addInput(ctx->id("LEDDADDR" + std::to_string(i))); + new_cell->addInput(ctx->id("LEDDDEN")); + new_cell->addInput(ctx->id("LEDDEXE")); + new_cell->addInput(ctx->id("LEDDRST")); // doesn't actually exist, for icecube code compatibility + // only + new_cell->addOutput(ctx->id("PWMOUT0")); + new_cell->addOutput(ctx->id("PWMOUT1")); + new_cell->addOutput(ctx->id("PWMOUT2")); + new_cell->addOutput(ctx->id("LEDDON")); } else if (type == ctx->id("SB_I2C")) { new_cell->params[ctx->id("I2C_SLAVE_INIT_ADDR")] = std::string("0b1111100001"); new_cell->params[ctx->id("BUS_ADDR74")] = std::string("0b0001"); for (int i = 0; i < 8; i++) { - add_port(ctx, new_cell.get(), "SBADRI" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "SBDATI" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "SBDATO" + std::to_string(i), PORT_OUT); + new_cell->addInput(ctx->id("SBADRI" + std::to_string(i))); + new_cell->addInput(ctx->id("SBDATI" + std::to_string(i))); + new_cell->addOutput(ctx->id("SBDATO" + std::to_string(i))); } - add_port(ctx, new_cell.get(), "SBCLKI", PORT_IN); - add_port(ctx, new_cell.get(), "SBRWI", PORT_IN); - add_port(ctx, new_cell.get(), "SBSTBI", PORT_IN); - add_port(ctx, new_cell.get(), "SCLI", PORT_IN); - add_port(ctx, new_cell.get(), "SDAI", PORT_IN); - add_port(ctx, new_cell.get(), "SBACKO", PORT_OUT); - add_port(ctx, new_cell.get(), "I2CIRQ", PORT_OUT); - add_port(ctx, new_cell.get(), "I2CWKUP", PORT_OUT); - add_port(ctx, new_cell.get(), "SCLO", PORT_OUT); - add_port(ctx, new_cell.get(), "SCLOE", PORT_OUT); - add_port(ctx, new_cell.get(), "SDAO", PORT_OUT); - add_port(ctx, new_cell.get(), "SDAOE", PORT_OUT); + new_cell->addInput(ctx->id("SBCLKI")); + new_cell->addInput(ctx->id("SBRWI")); + new_cell->addInput(ctx->id("SBSTBI")); + new_cell->addInput(ctx->id("SCLI")); + new_cell->addInput(ctx->id("SDAI")); + new_cell->addOutput(ctx->id("SBACKO")); + new_cell->addOutput(ctx->id("I2CIRQ")); + new_cell->addOutput(ctx->id("I2CWKUP")); + new_cell->addOutput(ctx->id("SCLO")); + new_cell->addOutput(ctx->id("SCLOE")); + new_cell->addOutput(ctx->id("SDAO")); + new_cell->addOutput(ctx->id("SDAOE")); } else if (type == ctx->id("SB_SPI")) { new_cell->params[ctx->id("BUS_ADDR74")] = std::string("0b0000"); for (int i = 0; i < 8; i++) { - add_port(ctx, new_cell.get(), "SBADRI" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "SBDATI" + std::to_string(i), PORT_IN); - add_port(ctx, new_cell.get(), "SBDATO" + std::to_string(i), PORT_OUT); + new_cell->addInput(ctx->id("SBADRI" + std::to_string(i))); + new_cell->addInput(ctx->id("SBDATI" + std::to_string(i))); + new_cell->addOutput(ctx->id("SBDATO" + std::to_string(i))); } - add_port(ctx, new_cell.get(), "SBCLKI", PORT_IN); - add_port(ctx, new_cell.get(), "SBRWI", PORT_IN); - add_port(ctx, new_cell.get(), "SBSTBI", PORT_IN); - add_port(ctx, new_cell.get(), "MI", PORT_IN); - add_port(ctx, new_cell.get(), "SI", PORT_IN); - add_port(ctx, new_cell.get(), "SCKI", PORT_IN); - add_port(ctx, new_cell.get(), "SCSNI", PORT_IN); - add_port(ctx, new_cell.get(), "SBACKO", PORT_OUT); - add_port(ctx, new_cell.get(), "SPIIRQ", PORT_OUT); - add_port(ctx, new_cell.get(), "SPIWKUP", PORT_OUT); - add_port(ctx, new_cell.get(), "SO", PORT_OUT); - add_port(ctx, new_cell.get(), "SOE", PORT_OUT); - add_port(ctx, new_cell.get(), "MO", PORT_OUT); - add_port(ctx, new_cell.get(), "MOE", PORT_OUT); - add_port(ctx, new_cell.get(), "SCKO", PORT_OUT); - add_port(ctx, new_cell.get(), "SCKOE", PORT_OUT); + new_cell->addInput(ctx->id("SBCLKI")); + new_cell->addInput(ctx->id("SBRWI")); + new_cell->addInput(ctx->id("SBSTBI")); + new_cell->addInput(ctx->id("MI")); + new_cell->addInput(ctx->id("SI")); + new_cell->addInput(ctx->id("SCKI")); + new_cell->addInput(ctx->id("SCSNI")); + new_cell->addOutput(ctx->id("SBACKO")); + new_cell->addOutput(ctx->id("SPIIRQ")); + new_cell->addOutput(ctx->id("SPIWKUP")); + new_cell->addOutput(ctx->id("SO")); + new_cell->addOutput(ctx->id("SOE")); + new_cell->addOutput(ctx->id("MO")); + new_cell->addOutput(ctx->id("MOE")); + new_cell->addOutput(ctx->id("SCKO")); + new_cell->addOutput(ctx->id("SCKOE")); for (int i = 0; i < 4; i++) { - add_port(ctx, new_cell.get(), "MCSNO" + std::to_string(i), PORT_OUT); - add_port(ctx, new_cell.get(), "MCSNOE" + std::to_string(i), PORT_OUT); + new_cell->addOutput(ctx->id("MCSNO" + std::to_string(i))); + new_cell->addOutput(ctx->id("MCSNOE" + std::to_string(i))); } } else { log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); diff --git a/machxo2/cells.cc b/machxo2/cells.cc index 534d8e3cc9..bbe3f2d636 100644 --- a/machxo2/cells.cc +++ b/machxo2/cells.cc @@ -25,19 +25,6 @@ NEXTPNR_NAMESPACE_BEGIN -void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir) -{ - IdString id = ctx->id(name); - NPNR_ASSERT(cell->ports.count(id) == 0); - cell->ports[id] = PortInfo{id, nullptr, dir}; -} - -void add_port(const Context *ctx, CellInfo *cell, IdString id, PortType dir) -{ - NPNR_ASSERT(cell->ports.count(id) == 0); - cell->ports[id] = PortInfo{id, nullptr, dir}; -} - std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std::string name) { static int auto_idx = 0; @@ -64,72 +51,72 @@ std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std:: new_cell->params[id_CCU2_INJECT1_1] = std::string("YES"); new_cell->params[id_WREMUX] = std::string("INV"); - add_port(ctx, new_cell.get(), id_A0, PORT_IN); - add_port(ctx, new_cell.get(), id_B0, PORT_IN); - add_port(ctx, new_cell.get(), id_C0, PORT_IN); - add_port(ctx, new_cell.get(), id_D0, PORT_IN); - - add_port(ctx, new_cell.get(), id_A1, PORT_IN); - add_port(ctx, new_cell.get(), id_B1, PORT_IN); - add_port(ctx, new_cell.get(), id_C1, PORT_IN); - add_port(ctx, new_cell.get(), id_D1, PORT_IN); - - add_port(ctx, new_cell.get(), id_M0, PORT_IN); - add_port(ctx, new_cell.get(), id_M1, PORT_IN); - - add_port(ctx, new_cell.get(), id_FCI, PORT_IN); - add_port(ctx, new_cell.get(), id_FXA, PORT_IN); - add_port(ctx, new_cell.get(), id_FXB, PORT_IN); - - add_port(ctx, new_cell.get(), id_CLK, PORT_IN); - add_port(ctx, new_cell.get(), id_LSR, PORT_IN); - add_port(ctx, new_cell.get(), id_CE, PORT_IN); - - add_port(ctx, new_cell.get(), id_DI0, PORT_IN); - add_port(ctx, new_cell.get(), id_DI1, PORT_IN); - - add_port(ctx, new_cell.get(), id_WD0, PORT_IN); - add_port(ctx, new_cell.get(), id_WD1, PORT_IN); - add_port(ctx, new_cell.get(), id_WAD0, PORT_IN); - add_port(ctx, new_cell.get(), id_WAD1, PORT_IN); - add_port(ctx, new_cell.get(), id_WAD2, PORT_IN); - add_port(ctx, new_cell.get(), id_WAD3, PORT_IN); - add_port(ctx, new_cell.get(), id_WRE, PORT_IN); - add_port(ctx, new_cell.get(), id_WCK, PORT_IN); - - add_port(ctx, new_cell.get(), id_F0, PORT_OUT); - add_port(ctx, new_cell.get(), id_Q0, PORT_OUT); - add_port(ctx, new_cell.get(), id_F1, PORT_OUT); - add_port(ctx, new_cell.get(), id_Q1, PORT_OUT); - - add_port(ctx, new_cell.get(), id_FCO, PORT_OUT); - add_port(ctx, new_cell.get(), id_OFX0, PORT_OUT); - add_port(ctx, new_cell.get(), id_OFX1, PORT_OUT); - - add_port(ctx, new_cell.get(), id_WDO0, PORT_OUT); - add_port(ctx, new_cell.get(), id_WDO1, PORT_OUT); - add_port(ctx, new_cell.get(), id_WDO2, PORT_OUT); - add_port(ctx, new_cell.get(), id_WDO3, PORT_OUT); - add_port(ctx, new_cell.get(), id_WADO0, PORT_OUT); - add_port(ctx, new_cell.get(), id_WADO1, PORT_OUT); - add_port(ctx, new_cell.get(), id_WADO2, PORT_OUT); - add_port(ctx, new_cell.get(), id_WADO3, PORT_OUT); + new_cell->addInput(id_A0); + new_cell->addInput(id_B0); + new_cell->addInput(id_C0); + new_cell->addInput(id_D0); + + new_cell->addInput(id_A1); + new_cell->addInput(id_B1); + new_cell->addInput(id_C1); + new_cell->addInput(id_D1); + + new_cell->addInput(id_M0); + new_cell->addInput(id_M1); + + new_cell->addInput(id_FCI); + new_cell->addInput(id_FXA); + new_cell->addInput(id_FXB); + + new_cell->addInput(id_CLK); + new_cell->addInput(id_LSR); + new_cell->addInput(id_CE); + + new_cell->addInput(id_DI0); + new_cell->addInput(id_DI1); + + new_cell->addInput(id_WD0); + new_cell->addInput(id_WD1); + new_cell->addInput(id_WAD0); + new_cell->addInput(id_WAD1); + new_cell->addInput(id_WAD2); + new_cell->addInput(id_WAD3); + new_cell->addInput(id_WRE); + new_cell->addInput(id_WCK); + + new_cell->addOutput(id_F0); + new_cell->addOutput(id_Q0); + new_cell->addOutput(id_F1); + new_cell->addOutput(id_Q1); + + new_cell->addOutput(id_FCO); + new_cell->addOutput(id_OFX0); + new_cell->addOutput(id_OFX1); + + new_cell->addOutput(id_WDO0); + new_cell->addOutput(id_WDO1); + new_cell->addOutput(id_WDO2); + new_cell->addOutput(id_WDO3); + new_cell->addOutput(id_WADO0); + new_cell->addOutput(id_WADO1); + new_cell->addOutput(id_WADO2); + new_cell->addOutput(id_WADO3); } else if (type == id_FACADE_IO) { new_cell->params[id_DIR] = std::string("INPUT"); new_cell->attrs[ctx->id("IO_TYPE")] = std::string("LVCMOS33"); - add_port(ctx, new_cell.get(), "PAD", PORT_INOUT); - add_port(ctx, new_cell.get(), "I", PORT_IN); - add_port(ctx, new_cell.get(), "EN", PORT_IN); - add_port(ctx, new_cell.get(), "O", PORT_OUT); + new_cell->addInout(ctx->id("PAD")); + new_cell->addInput(ctx->id("I")); + new_cell->addInput(ctx->id("EN")); + new_cell->addOutput(ctx->id("O")); } else if (type == id_LUT4) { new_cell->params[id_INIT] = Property(0, 16); - add_port(ctx, new_cell.get(), id_A, PORT_IN); - add_port(ctx, new_cell.get(), id_B, PORT_IN); - add_port(ctx, new_cell.get(), id_C, PORT_IN); - add_port(ctx, new_cell.get(), id_D, PORT_IN); - add_port(ctx, new_cell.get(), id_Z, PORT_OUT); + new_cell->addInput(id_A); + new_cell->addInput(id_B); + new_cell->addInput(id_C); + new_cell->addInput(id_D); + new_cell->addOutput(id_Z); } else { log_error("unable to create MachXO2 cell of type %s", type.c_str(ctx)); } From 76683a1e3c123d28deff750c38467c6377936879 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 16 Feb 2022 17:09:54 +0000 Subject: [PATCH 068/712] refactor: Use constids instead of id("..") Signed-off-by: gatecat --- ecp5/arch.cc | 37 +- ecp5/arch_place.cc | 2 +- ecp5/bitstream.cc | 495 +++++++++++------------- ecp5/cells.cc | 426 ++++++++++----------- ecp5/cells.h | 33 +- ecp5/constids.inc | 510 +++++++++++++++++++++++++ ecp5/dcu_bitstream.h | 588 +++++++++++++--------------- ecp5/globals.cc | 14 +- ecp5/lpf.cc | 2 +- ecp5/main.cc | 2 +- ecp5/pack.cc | 883 +++++++++++++++++++++---------------------- gowin/arch.cc | 24 +- gowin/arch.h | 2 +- gowin/constids.inc | 9 + gowin/pack.cc | 60 +-- ice40/arch.cc | 90 ++--- ice40/arch_place.cc | 2 +- ice40/bitstream.cc | 90 ++--- ice40/cells.cc | 588 ++++++++++++++-------------- ice40/cells.h | 80 ++-- ice40/chains.cc | 55 ++- ice40/constids.inc | 166 +++++++- ice40/main.cc | 8 +- ice40/pack.cc | 363 +++++++++--------- ice40/pcf.cc | 16 +- machxo2/arch.cc | 22 +- machxo2/arch.h | 2 +- machxo2/bitstream.cc | 40 +- machxo2/cells.cc | 34 +- machxo2/cells.h | 6 +- machxo2/constids.inc | 19 + machxo2/pack.cc | 22 +- mistral/arch.cc | 8 +- mistral/constids.inc | 8 + mistral/globals.cc | 6 +- mistral/main.cc | 2 +- nexus/arch.cc | 40 +- nexus/constids.inc | 21 + nexus/main.cc | 6 +- nexus/pack.cc | 40 +- nexus/post_place.cc | 2 +- 41 files changed, 2686 insertions(+), 2137 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 2e453f2a90..3d20badb22 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -202,25 +202,25 @@ std::string Arch::get_full_chip_name() const IdString Arch::archArgsToId(ArchArgs args) const { if (args.type == ArchArgs::LFE5U_12F) - return id("lfe5u_12f"); + return id_lfe5u_12f; if (args.type == ArchArgs::LFE5U_25F) - return id("lfe5u_25f"); + return id_lfe5u_25f; if (args.type == ArchArgs::LFE5U_45F) - return id("lfe5u_45f"); + return id_lfe5u_45f; if (args.type == ArchArgs::LFE5U_85F) - return id("lfe5u_85f"); + return id_lfe5u_85f; if (args.type == ArchArgs::LFE5UM_25F) - return id("lfe5um_25f"); + return id_lfe5um_25f; if (args.type == ArchArgs::LFE5UM_45F) - return id("lfe5um_45f"); + return id_lfe5um_45f; if (args.type == ArchArgs::LFE5UM_85F) - return id("lfe5um_85f"); + return id_lfe5um_85f; if (args.type == ArchArgs::LFE5UM5G_25F) - return id("lfe5um5g_25f"); + return id_lfe5um5g_25f; if (args.type == ArchArgs::LFE5UM5G_45F) - return id("lfe5um5g_45f"); + return id_lfe5um5g_45f; if (args.type == ArchArgs::LFE5UM5G_85F) - return id("lfe5um5g_85f"); + return id_lfe5um5g_85f; return IdString(); } @@ -586,7 +586,7 @@ delay_t Arch::getRipupDelayPenalty() const { return 400; } bool Arch::place() { - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + std::string placer = str_or_default(settings, id_placer, defaultPlacer); if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); @@ -612,7 +612,7 @@ bool Arch::place() for (auto &cell : cells) cell.second->belStrength = STRENGTH_LOCKED; - getCtx()->settings[getCtx()->id("place")] = 1; + getCtx()->settings[id_place] = 1; archInfoToAttributes(); return true; @@ -620,7 +620,7 @@ bool Arch::place() bool Arch::route() { - std::string router = str_or_default(settings, id("router"), defaultRouter); + std::string router = str_or_default(settings, id_router, defaultRouter); disable_router_lutperm = getCtx()->setting("arch.disable_router_lutperm", false); @@ -639,7 +639,7 @@ bool Arch::route() log_error("ECP5 architecture does not support router '%s'\n", router.c_str()); } - getCtx()->settings[getCtx()->id("route")] = 1; + getCtx()->settings[id_route] = 1; archInfoToAttributes(); return result; } @@ -1033,12 +1033,12 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port get_setuphold_from_tmg_db(id_SDPRAME, id_WCK, port, info.setup, info.hold); } else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 0 && port == id_M0) || (sd1 == 0 && port == id_M1)) { - info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE; + info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE; info.clock_port = id_CLK; get_setuphold_from_tmg_db(id_SLOGICB, id_CLK, port, info.setup, info.hold); } else { - info.edge = cell->sliceInfo.clkmux == id("INV") ? FALLING_EDGE : RISING_EDGE; + info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE; info.clock_port = id_CLK; bool is_path = get_delay_from_tmg_db(id_SLOGICB, id_CLK, port, info.clockToQ); NPNR_ASSERT(is_path); @@ -1070,8 +1070,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } else { info.clock_port = half_clock; } - info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id("CLKBMUX") : id("CLKAMUX"), "CLK") == - "INV") + info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id_CLKBMUX : id_CLKAMUX, "CLK") == "INV") ? FALLING_EDGE : RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { @@ -1298,7 +1297,7 @@ std::vector> Arch::getWireAttrs(WireId wire) co std::vector> ret; auto &wi = loc_info(wire)->wire_data[wire.index]; - ret.push_back(std::make_pair(id("TILE_WIRE_ID"), stringf("%d", wi.tile_wire))); + ret.push_back(std::make_pair(id_TILE_WIRE_ID, stringf("%d", wi.tile_wire))); return ret; } diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index 3cc6d252f1..a1f8aa1f04 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -150,7 +150,7 @@ void Arch::permute_luts() for (auto &cell : cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_TRELLIS_SLICE && str_or_default(ci->params, id("MODE"), "LOGIC") == "LOGIC") { + if (ci->type == id_TRELLIS_SLICE && str_or_default(ci->params, id_MODE, "LOGIC") == "LOGIC") { proc_lut(ci, 0); proc_lut(ci, 1); } diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index de6c711ece..11a855ec48 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -723,10 +723,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->bel != BelId() && ci->type == ctx->id("TRELLIS_IO")) { + if (ci->bel != BelId() && ci->type == id_TRELLIS_IO) { int bank = ctx->get_pio_bel_bank(ci->bel); - std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT"); - std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); + std::string dir = str_or_default(ci->params, id_DIR, "INPUT"); + std::string iotype = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); if (dir != "INPUT" || is_referenced(ioType_from_str(iotype))) { IOVoltage vcc = get_vccio(ioType_from_str(iotype)); @@ -835,71 +835,65 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex log_warning("found unplaced cell '%s' during bitstream gen\n", ci->name.c_str(ctx)); } BelId bel = ci->bel; - if (ci->type == ctx->id("TRELLIS_SLICE")) { + if (ci->type == id_TRELLIS_SLICE) { pool used_phys_pins; std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get(); - int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); - int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); + int lut0_init = int_or_default(ci->params, id_LUT0_INITVAL); + int lut1_init = int_or_default(ci->params, id_LUT1_INITVAL); cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16)); cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16)); - cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC")); - cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); - cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, ctx->id("REG0_SD"), "0")); - cc.tiles[tname].add_enum(slice + ".REG1.SD", intstr_or_default(ci->params, ctx->id("REG1_SD"), "0")); - cc.tiles[tname].add_enum(slice + ".REG0.REGSET", - str_or_default(ci->params, ctx->id("REG0_REGSET"), "RESET")); - cc.tiles[tname].add_enum(slice + ".REG1.REGSET", - str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET")); - cc.tiles[tname].add_enum(slice + ".REG0.LSRMODE", - str_or_default(ci->params, ctx->id("REG0_LSRMODE"), "LSR")); - cc.tiles[tname].add_enum(slice + ".REG1.LSRMODE", - str_or_default(ci->params, ctx->id("REG1_LSRMODE"), "LSR")); - cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1")); + cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, id_MODE, "LOGIC")); + cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED")); + cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, id_REG0_SD, "0")); + cc.tiles[tname].add_enum(slice + ".REG1.SD", intstr_or_default(ci->params, id_REG1_SD, "0")); + cc.tiles[tname].add_enum(slice + ".REG0.REGSET", str_or_default(ci->params, id_REG0_REGSET, "RESET")); + cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, id_REG1_REGSET, "RESET")); + cc.tiles[tname].add_enum(slice + ".REG0.LSRMODE", str_or_default(ci->params, id_REG0_LSRMODE, "LSR")); + cc.tiles[tname].add_enum(slice + ".REG1.LSRMODE", str_or_default(ci->params, id_REG1_LSRMODE, "LSR")); + cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, id_CEMUX, "1")); if (ci->sliceInfo.using_dff) { NetInfo *lsrnet = nullptr; - if (ci->ports.find(ctx->id("LSR")) != ci->ports.end() && ci->ports.at(ctx->id("LSR")).net != nullptr) - lsrnet = ci->ports.at(ctx->id("LSR")).net; + if (ci->ports.find(id_LSR) != ci->ports.end() && ci->ports.at(id_LSR).net != nullptr) + lsrnet = ci->ports.at(id_LSR).net; if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR0")) == lsrnet) { - cc.tiles[tname].add_enum("LSR0.SRMODE", - str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); - cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR")); } if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR1")) == lsrnet) { - cc.tiles[tname].add_enum("LSR1.SRMODE", - str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); - cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR")); } NetInfo *clknet = nullptr; - if (ci->ports.find(ctx->id("CLK")) != ci->ports.end() && ci->ports.at(ctx->id("CLK")).net != nullptr) - clknet = ci->ports.at(ctx->id("CLK")).net; + if (ci->ports.find(id_CLK) != ci->ports.end() && ci->ports.at(id_CLK).net != nullptr) + clknet = ci->ports.at(id_CLK).net; if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK0")) == clknet) { - cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, ctx->id("CLKMUX"), "CLK")); + cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK")); } if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK1")) == clknet) { - cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, ctx->id("CLKMUX"), "CLK")); + cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK")); } } - if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "CCU2") { + if (str_or_default(ci->params, id_MODE, "LOGIC") == "CCU2") { cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", - str_or_default(ci->params, ctx->id("CCU2_INJECT1_0"), "YES")); + str_or_default(ci->params, id_CCU2_INJECT1_0, "YES")); cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", - str_or_default(ci->params, ctx->id("CCU2_INJECT1_1"), "YES")); + str_or_default(ci->params, id_CCU2_INJECT1_1, "YES")); } else { // Don't interfere with cascade mux wiring cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", "_NONE_"); cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", "_NONE_"); } - if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "DPRAM" && slice == "SLICEA") { - cc.tiles[tname].add_enum(slice + ".WREMUX", str_or_default(ci->params, ctx->id("WREMUX"), "WRE")); + if (str_or_default(ci->params, id_MODE, "LOGIC") == "DPRAM" && slice == "SLICEA") { + cc.tiles[tname].add_enum(slice + ".WREMUX", str_or_default(ci->params, id_WREMUX, "WRE")); - std::string wckmux = str_or_default(ci->params, ctx->id("WCKMUX"), "WCK"); + std::string wckmux = str_or_default(ci->params, id_WCKMUX, "WCK"); wckmux = (wckmux == "WCK") ? "CLK" : wckmux; cc.tiles[tname].add_enum("CLK1.CLKMUX", wckmux); } @@ -912,10 +906,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } // TODO: CLKMUX - } else if (ci->type == ctx->id("TRELLIS_IO")) { + } else if (ci->type == id_TRELLIS_IO) { std::string pio = ctx->loc_info(bel)->bel_data[bel.index].name.get(); - std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); - std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT"); + std::string iotype = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); + std::string dir = str_or_default(ci->params, id_DIR, "INPUT"); std::string pio_tile = get_pio_tile(ctx, bel); std::string pic_tile = get_pic_tile(ctx, bel); cc.tiles[pio_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); @@ -950,10 +944,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } else if (is_referenced(ioType_from_str(iotype))) { cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", "NONE"); } - if (dir != "INPUT" && - (ci->ports.find(ctx->id("T")) == ci->ports.end() || ci->ports.at(ctx->id("T")).net == nullptr) && - (ci->ports.find(ctx->id("IOLTO")) == ci->ports.end() || - ci->ports.at(ctx->id("IOLTO")).net == nullptr)) { + if (dir != "INPUT" && (ci->ports.find(id_T) == ci->ports.end() || ci->ports.at(id_T).net == nullptr) && + (ci->ports.find(id_IOLTO) == ci->ports.end() || ci->ports.at(id_IOLTO).net == nullptr)) { // Tie tristate low if unconnected for outputs or bidir WireId jpt_wire = ctx->get_wire_by_loc_basename(bel.location, fmt_str("JPADDT" << pio.back())); PipId jpt_pip = *ctx->getPipsUphill(jpt_wire).begin(); @@ -964,31 +956,29 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } if ((dir == "INPUT" || dir == "BIDIR") && !is_differential(ioType_from_str(iotype)) && !is_referenced(ioType_from_str(iotype))) { - cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", - str_or_default(ci->attrs, ctx->id("HYSTERESIS"), "ON")); + cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", str_or_default(ci->attrs, id_HYSTERESIS, "ON")); } - if (ci->attrs.count(ctx->id("SLEWRATE")) && !is_referenced(ioType_from_str(iotype))) - cc.tiles[pio_tile].add_enum(pio + ".SLEWRATE", str_or_default(ci->attrs, ctx->id("SLEWRATE"), "SLOW")); - if (ci->attrs.count(ctx->id("PULLMODE"))) - cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", str_or_default(ci->attrs, ctx->id("PULLMODE"), "NONE")); - if (ci->attrs.count(ctx->id("DIFFRESISTOR"))) - cc.tiles[pio_tile].add_enum(pio + ".DIFFRESISTOR", - str_or_default(ci->attrs, ctx->id("DIFFRESISTOR"), "OFF")); - if (ci->attrs.count(ctx->id("CLAMP"))) - cc.tiles[pio_tile].add_enum(pio + ".CLAMP", str_or_default(ci->attrs, ctx->id("CLAMP"), "OFF")); - - if (ci->attrs.count(ctx->id("DRIVE"))) { + if (ci->attrs.count(id_SLEWRATE) && !is_referenced(ioType_from_str(iotype))) + cc.tiles[pio_tile].add_enum(pio + ".SLEWRATE", str_or_default(ci->attrs, id_SLEWRATE, "SLOW")); + if (ci->attrs.count(id_PULLMODE)) + cc.tiles[pio_tile].add_enum(pio + ".PULLMODE", str_or_default(ci->attrs, id_PULLMODE, "NONE")); + if (ci->attrs.count(id_DIFFRESISTOR)) + cc.tiles[pio_tile].add_enum(pio + ".DIFFRESISTOR", str_or_default(ci->attrs, id_DIFFRESISTOR, "OFF")); + if (ci->attrs.count(id_CLAMP)) + cc.tiles[pio_tile].add_enum(pio + ".CLAMP", str_or_default(ci->attrs, id_CLAMP, "OFF")); + + if (ci->attrs.count(id_DRIVE)) { static bool drive_3v3_warning_done = false; if (iotype == "LVCMOS33") { - cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "8")); + cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, id_DRIVE, "8")); } else if (iotype == "LVCMOS33D") { if (bel.location.y == 0) { // Pseudo differential top IO NPNR_ASSERT(dir == "OUTPUT"); NPNR_ASSERT(pio == "PIOA"); std::string cpio_tile = get_comp_pio_tile(ctx, bel); - cc.tiles[pio_tile].add_enum("PIOA.DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "12")); - cc.tiles[cpio_tile].add_enum("PIOB.DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "12")); + cc.tiles[pio_tile].add_enum("PIOA.DRIVE", str_or_default(ci->attrs, id_DRIVE, "12")); + cc.tiles[cpio_tile].add_enum("PIOB.DRIVE", str_or_default(ci->attrs, id_DRIVE, "12")); } else { std::string other; if (pio == "PIOA") @@ -997,9 +987,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex other = "PIOD"; else log_error("cannot set DRIVE on differential IO at location %s\n", pio.c_str()); - cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, ctx->id("DRIVE"), "12")); - cc.tiles[pio_tile].add_enum(other + ".DRIVE", - str_or_default(ci->attrs, ctx->id("DRIVE"), "12")); + cc.tiles[pio_tile].add_enum(pio + ".DRIVE", str_or_default(ci->attrs, id_DRIVE, "12")); + cc.tiles[pio_tile].add_enum(other + ".DRIVE", str_or_default(ci->attrs, id_DRIVE, "12")); } } else { if (!drive_3v3_warning_done) @@ -1007,28 +996,28 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex drive_3v3_warning_done = true; } } - if (ci->attrs.count(ctx->id("TERMINATION"))) { + if (ci->attrs.count(id_TERMINATION)) { auto vccio = get_vccio(ioType_from_str(iotype)); switch (vccio) { case IOVoltage::VCC_1V8: cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V8", - str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF")); + str_or_default(ci->attrs, id_TERMINATION, "OFF")); break; case IOVoltage::VCC_1V5: cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V5", - str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF")); + str_or_default(ci->attrs, id_TERMINATION, "OFF")); break; case IOVoltage::VCC_1V35: cc.tiles[pio_tile].add_enum(pio + ".TERMINATION_1V35", - str_or_default(ci->attrs, ctx->id("TERMINATION"), "OFF")); + str_or_default(ci->attrs, id_TERMINATION, "OFF")); break; default: log_error("TERMINATION is not supported with Vcc = %s (on PIO %s)\n", iovoltage_to_str(vccio).c_str(), ci->name.c_str(ctx)); } } - if (ci->attrs.count(ctx->id("OPENDRAIN"))) { - cc.tiles[pio_tile].add_enum(pio + ".OPENDRAIN", str_or_default(ci->attrs, ctx->id("OPENDRAIN"), "OFF")); + if (ci->attrs.count(id_OPENDRAIN)) { + cc.tiles[pio_tile].add_enum(pio + ".OPENDRAIN", str_or_default(ci->attrs, id_OPENDRAIN, "OFF")); if (is_differential(ioType_from_str(iotype))) { std::string other; if (pio == "PIOA") @@ -1037,25 +1026,24 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex other = "PIOD"; else log_error("cannot set OPENDRAIN on differential IO at location %s\n", pio.c_str()); - cc.tiles[pio_tile].add_enum(other + ".OPENDRAIN", - str_or_default(ci->attrs, ctx->id("OPENDRAIN"), "OFF")); + cc.tiles[pio_tile].add_enum(other + ".OPENDRAIN", str_or_default(ci->attrs, id_OPENDRAIN, "OFF")); } } - std::string datamux_oddr = str_or_default(ci->params, ctx->id("DATAMUX_ODDR"), "PADDO"); + std::string datamux_oddr = str_or_default(ci->params, id_DATAMUX_ODDR, "PADDO"); if (datamux_oddr != "PADDO") cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_ODDR", datamux_oddr); - std::string datamux_oreg = str_or_default(ci->params, ctx->id("DATAMUX_OREG"), "PADDO"); + std::string datamux_oreg = str_or_default(ci->params, id_DATAMUX_OREG, "PADDO"); if (datamux_oreg != "PADDO") cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_OREG", datamux_oreg); - std::string datamux_mddr = str_or_default(ci->params, ctx->id("DATAMUX_MDDR"), "PADDO"); + std::string datamux_mddr = str_or_default(ci->params, id_DATAMUX_MDDR, "PADDO"); if (datamux_mddr != "PADDO") cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr); - std::string trimux_tsreg = str_or_default(ci->params, ctx->id("TRIMUX_TSREG"), "PADDT"); + std::string trimux_tsreg = str_or_default(ci->params, id_TRIMUX_TSREG, "PADDT"); if (trimux_tsreg != "PADDT") cc.tiles[pic_tile].add_enum(pio + ".TRIMUX_TSREG", trimux_tsreg); - } else if (ci->type == ctx->id("DCCA")) { - const NetInfo *cen = get_net_or_empty(ci, ctx->id("CE")); + } else if (ci->type == id_DCCA) { + const NetInfo *cen = get_net_or_empty(ci, id_CE); if (cen != nullptr) { std::string belname = ctx->loc_info(bel)->bel_data[bel.index].name.get(); Loc loc = ctx->getBelLocation(bel); @@ -1084,12 +1072,12 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum(std::string("DCC_") + belname[0] + belname.substr(4) + ".MODE", "DCCA"); cc.tilegroups.push_back(tg); } - } else if (ci->type == ctx->id("DCSC")) { + } else if (ci->type == id_DCSC) { std::set dcs_tiles{"EBR_CMUX_LL", "EBR_CMUX_UL", "EBR_CMUX_LL_25K", "DSP_CMUX_UL"}; std::string tile = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, dcs_tiles); std::string dcs = ctx->loc_info(bel)->bel_data[bel.index].name.get(); - cc.tiles[tile].add_enum(dcs + ".DCSMODE", str_or_default(ci->attrs, ctx->id("DCSMODE"), "POS")); - } else if (ci->type == ctx->id("DP16KD")) { + cc.tiles[tile].add_enum(dcs + ".DCSMODE", str_or_default(ci->attrs, id_DCSMODE, "POS")); + } else if (ci->type == id_DP16KD) { TileGroup tg; Loc loc = ctx->getBelLocation(ci->bel); tg.tiles = get_bram_tiles(ctx, ci->bel); @@ -1098,32 +1086,27 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex if (ci->ramInfo.is_pdp) { tg.config.add_enum(ebr + ".MODE", "PDPW16KD"); tg.config.add_enum(ebr + ".PDPW16KD.DATA_WIDTH_R", - intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "36")); + intstr_or_default(ci->params, id_DATA_WIDTH_B, "36")); } else { tg.config.add_enum(ebr + ".MODE", "DP16KD"); - tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A", - intstr_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18")); - tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B", - intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "18")); - tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A", - str_or_default(ci->params, ctx->id("WRITEMODE_A"), "NORMAL")); - tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B", - str_or_default(ci->params, ctx->id("WRITEMODE_B"), "NORMAL")); + tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A", intstr_or_default(ci->params, id_DATA_WIDTH_A, "18")); + tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B", intstr_or_default(ci->params, id_DATA_WIDTH_B, "18")); + tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A", str_or_default(ci->params, id_WRITEMODE_A, "NORMAL")); + tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B", str_or_default(ci->params, id_WRITEMODE_B, "NORMAL")); } - auto csd_a = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_A"), "0b000"), 3), - csd_b = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_B"), "0b000"), 3); + auto csd_a = str_to_bitvector(str_or_default(ci->params, id_CSDECODE_A, "0b000"), 3), + csd_b = str_to_bitvector(str_or_default(ci->params, id_CSDECODE_B, "0b000"), 3); - tg.config.add_enum(ebr + ".REGMODE_A", str_or_default(ci->params, ctx->id("REGMODE_A"), "NOREG")); - tg.config.add_enum(ebr + ".REGMODE_B", str_or_default(ci->params, ctx->id("REGMODE_B"), "NOREG")); + tg.config.add_enum(ebr + ".REGMODE_A", str_or_default(ci->params, id_REGMODE_A, "NOREG")); + tg.config.add_enum(ebr + ".REGMODE_B", str_or_default(ci->params, id_REGMODE_B, "NOREG")); - tg.config.add_enum(ebr + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC")); + tg.config.add_enum(ebr + ".RESETMODE", str_or_default(ci->params, id_RESETMODE, "SYNC")); tg.config.add_enum(ebr + ".ASYNC_RESET_RELEASE", - str_or_default(ci->params, ctx->id("ASYNC_RESET_RELEASE"), "SYNC")); - tg.config.add_enum(ebr + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED")); + str_or_default(ci->params, id_ASYNC_RESET_RELEASE, "SYNC")); + tg.config.add_enum(ebr + ".GSR", str_or_default(ci->params, id_GSR, "DISABLED")); - tg.config.add_word(ebr + ".WID", - int_to_bitvector(bit_reverse(int_or_default(ci->attrs, ctx->id("WID"), 0), 9), 9)); + tg.config.add_word(ebr + ".WID", int_to_bitvector(bit_reverse(int_or_default(ci->attrs, id_WID, 0), 9), 9)); // Tie signals as appropriate for (auto port : ci->ports) { @@ -1169,19 +1152,19 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } } - tg.config.add_enum(ebr + ".CLKAMUX", str_or_default(ci->params, ctx->id("CLKAMUX"), "CLKA")); - tg.config.add_enum(ebr + ".CLKBMUX", str_or_default(ci->params, ctx->id("CLKBMUX"), "CLKB")); + tg.config.add_enum(ebr + ".CLKAMUX", str_or_default(ci->params, id_CLKAMUX, "CLKA")); + tg.config.add_enum(ebr + ".CLKBMUX", str_or_default(ci->params, id_CLKBMUX, "CLKB")); - tg.config.add_enum(ebr + ".RSTAMUX", str_or_default(ci->params, ctx->id("RSTAMUX"), "RSTA")); - tg.config.add_enum(ebr + ".RSTBMUX", str_or_default(ci->params, ctx->id("RSTBMUX"), "RSTB")); + tg.config.add_enum(ebr + ".RSTAMUX", str_or_default(ci->params, id_RSTAMUX, "RSTA")); + tg.config.add_enum(ebr + ".RSTBMUX", str_or_default(ci->params, id_RSTBMUX, "RSTB")); if (!ci->ramInfo.is_pdp) { - tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, ctx->id("WEAMUX"), "WEA")); - tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, ctx->id("WEBMUX"), "WEB")); + tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, id_WEAMUX, "WEA")); + tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, id_WEBMUX, "WEB")); } - tg.config.add_enum(ebr + ".CEAMUX", str_or_default(ci->params, ctx->id("CEAMUX"), "CEA")); - tg.config.add_enum(ebr + ".CEBMUX", str_or_default(ci->params, ctx->id("CEBMUX"), "CEB")); - tg.config.add_enum(ebr + ".OCEAMUX", str_or_default(ci->params, ctx->id("OCEAMUX"), "OCEA")); - tg.config.add_enum(ebr + ".OCEBMUX", str_or_default(ci->params, ctx->id("OCEBMUX"), "OCEB")); + tg.config.add_enum(ebr + ".CEAMUX", str_or_default(ci->params, id_CEAMUX, "CEA")); + tg.config.add_enum(ebr + ".CEBMUX", str_or_default(ci->params, id_CEBMUX, "CEB")); + tg.config.add_enum(ebr + ".OCEAMUX", str_or_default(ci->params, id_OCEAMUX, "OCEA")); + tg.config.add_enum(ebr + ".OCEBMUX", str_or_default(ci->params, id_OCEBMUX, "OCEB")); std::reverse(csd_a.begin(), csd_a.end()); std::reverse(csd_b.begin(), csd_b.end()); @@ -1205,7 +1188,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } } } - int wid = int_or_default(ci->attrs, ctx->id("WID"), 0); + int wid = int_or_default(ci->attrs, id_WID, 0); NPNR_ASSERT(!cc.bram_data.count(wid)); cc.bram_data[wid] = init_data; cc.tilegroups.push_back(tg); @@ -1214,33 +1197,30 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex Loc loc = ctx->getBelLocation(ci->bel); tg.tiles = get_dsp_tiles(ctx, ci->bel); std::string dsp = "MULT18_" + std::to_string(loc.z); - tg.config.add_enum(dsp + ".REG_INPUTA_CLK", str_or_default(ci->params, ctx->id("REG_INPUTA_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_INPUTA_CE", str_or_default(ci->params, ctx->id("REG_INPUTA_CE"), "CE0")); - tg.config.add_enum(dsp + ".REG_INPUTA_RST", str_or_default(ci->params, ctx->id("REG_INPUTA_RST"), "RST0")); - tg.config.add_enum(dsp + ".REG_INPUTB_CLK", str_or_default(ci->params, ctx->id("REG_INPUTB_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_INPUTB_CE", str_or_default(ci->params, ctx->id("REG_INPUTB_CE"), "CE0")); - tg.config.add_enum(dsp + ".REG_INPUTB_RST", str_or_default(ci->params, ctx->id("REG_INPUTB_RST"), "RST0")); - tg.config.add_enum(dsp + ".REG_INPUTC_CLK", str_or_default(ci->params, ctx->id("REG_INPUTC_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_PIPELINE_CLK", - str_or_default(ci->params, ctx->id("REG_PIPELINE_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_PIPELINE_CE", str_or_default(ci->params, ctx->id("REG_PIPELINE_CE"), "CE0")); - tg.config.add_enum(dsp + ".REG_PIPELINE_RST", - str_or_default(ci->params, ctx->id("REG_PIPELINE_RST"), "RST0")); - tg.config.add_enum(dsp + ".REG_OUTPUT_CLK", str_or_default(ci->params, ctx->id("REG_OUTPUT_CLK"), "NONE")); + tg.config.add_enum(dsp + ".REG_INPUTA_CLK", str_or_default(ci->params, id_REG_INPUTA_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_INPUTA_CE", str_or_default(ci->params, id_REG_INPUTA_CE, "CE0")); + tg.config.add_enum(dsp + ".REG_INPUTA_RST", str_or_default(ci->params, id_REG_INPUTA_RST, "RST0")); + tg.config.add_enum(dsp + ".REG_INPUTB_CLK", str_or_default(ci->params, id_REG_INPUTB_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_INPUTB_CE", str_or_default(ci->params, id_REG_INPUTB_CE, "CE0")); + tg.config.add_enum(dsp + ".REG_INPUTB_RST", str_or_default(ci->params, id_REG_INPUTB_RST, "RST0")); + tg.config.add_enum(dsp + ".REG_INPUTC_CLK", str_or_default(ci->params, id_REG_INPUTC_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_PIPELINE_CLK", str_or_default(ci->params, id_REG_PIPELINE_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_PIPELINE_CE", str_or_default(ci->params, id_REG_PIPELINE_CE, "CE0")); + tg.config.add_enum(dsp + ".REG_PIPELINE_RST", str_or_default(ci->params, id_REG_PIPELINE_RST, "RST0")); + tg.config.add_enum(dsp + ".REG_OUTPUT_CLK", str_or_default(ci->params, id_REG_OUTPUT_CLK, "NONE")); if (dsp == "MULT18_0" || dsp == "MULT18_4") - tg.config.add_enum(dsp + ".REG_OUTPUT_RST", - str_or_default(ci->params, ctx->id("REG_OUTPUT_RST"), "RST0")); + tg.config.add_enum(dsp + ".REG_OUTPUT_RST", str_or_default(ci->params, id_REG_OUTPUT_RST, "RST0")); - tg.config.add_enum(dsp + ".CLK0_DIV", str_or_default(ci->params, ctx->id("CLK0_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".CLK1_DIV", str_or_default(ci->params, ctx->id("CLK1_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".CLK2_DIV", str_or_default(ci->params, ctx->id("CLK2_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".CLK3_DIV", str_or_default(ci->params, ctx->id("CLK3_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); - tg.config.add_enum(dsp + ".SOURCEB_MODE", str_or_default(ci->params, ctx->id("SOURCEB_MODE"), "B_SHIFT")); - tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC")); + tg.config.add_enum(dsp + ".CLK0_DIV", str_or_default(ci->params, id_CLK0_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".CLK1_DIV", str_or_default(ci->params, id_CLK1_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".CLK2_DIV", str_or_default(ci->params, id_CLK2_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".CLK3_DIV", str_or_default(ci->params, id_CLK3_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED")); + tg.config.add_enum(dsp + ".SOURCEB_MODE", str_or_default(ci->params, id_SOURCEB_MODE, "B_SHIFT")); + tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, id_RESETMODE, "SYNC")); tg.config.add_enum(dsp + ".MODE", "MULT18X18D"); - if (str_or_default(ci->params, ctx->id("REG_OUTPUT_CLK"), "NONE") == "NONE" && ci->cluster == ClusterId()) + if (str_or_default(ci->params, id_REG_OUTPUT_CLK, "NONE") == "NONE" && ci->cluster == ClusterId()) tg.config.add_enum(dsp + ".CIBOUT_BYP", "ON"); if (loc.z < 4) @@ -1264,67 +1244,53 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex Loc loc = ctx->getBelLocation(ci->bel); tg.tiles = get_dsp_tiles(ctx, ci->bel); std::string dsp = "ALU54_" + std::to_string(loc.z); - tg.config.add_enum(dsp + ".REG_INPUTC0_CLK", - str_or_default(ci->params, ctx->id("REG_INPUTC0_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_INPUTC1_CLK", - str_or_default(ci->params, ctx->id("REG_INPUTC1_CLK"), "NONE")); + tg.config.add_enum(dsp + ".REG_INPUTC0_CLK", str_or_default(ci->params, id_REG_INPUTC0_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_INPUTC1_CLK", str_or_default(ci->params, id_REG_INPUTC1_CLK, "NONE")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_0_CLK", - str_or_default(ci->params, ctx->id("REG_OPCODEOP0_0_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_OPCODEOP0_0_CE", - str_or_default(ci->params, ctx->id("REG_OPCODEOP0_0_CE"), "CE0")); + str_or_default(ci->params, id_REG_OPCODEOP0_0_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_OPCODEOP0_0_CE", str_or_default(ci->params, id_REG_OPCODEOP0_0_CE, "CE0")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_0_RST", - str_or_default(ci->params, ctx->id("REG_OPCODEOP0_0_RST"), "RST0")); + str_or_default(ci->params, id_REG_OPCODEOP0_0_RST, "RST0")); tg.config.add_enum(dsp + ".REG_OPCODEOP1_0_CLK", - str_or_default(ci->params, ctx->id("REG_OPCODEOP1_0_CLK"), "NONE")); + str_or_default(ci->params, id_REG_OPCODEOP1_0_CLK, "NONE")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_CLK", - str_or_default(ci->params, ctx->id("REG_OPCODEOP0_1_CLK"), "NONE")); + str_or_default(ci->params, id_REG_OPCODEOP0_1_CLK, "NONE")); tg.config.add_enum(dsp + ".REG_OPCODEOP1_1_CLK", - str_or_default(ci->params, ctx->id("REG_OPCODEOP1_1_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_CE", - str_or_default(ci->params, ctx->id("REG_OPCODEOP0_1_CE"), "CE0")); + str_or_default(ci->params, id_REG_OPCODEOP1_1_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_CE", str_or_default(ci->params, id_REG_OPCODEOP0_1_CE, "CE0")); tg.config.add_enum(dsp + ".REG_OPCODEOP0_1_RST", - str_or_default(ci->params, ctx->id("REG_OPCODEOP0_1_RST"), "RST0")); - tg.config.add_enum(dsp + ".REG_OPCODEIN_0_CLK", - str_or_default(ci->params, ctx->id("REG_OPCODEIN_0_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_OPCODEIN_0_CE", - str_or_default(ci->params, ctx->id("REG_OPCODEIN_0_CE"), "CE0")); - tg.config.add_enum(dsp + ".REG_OPCODEIN_0_RST", - str_or_default(ci->params, ctx->id("REG_OPCODEIN_0_RST"), "RST0")); - tg.config.add_enum(dsp + ".REG_OPCODEIN_1_CLK", - str_or_default(ci->params, ctx->id("REG_OPCODEIN_1_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_OPCODEIN_1_CE", - str_or_default(ci->params, ctx->id("REG_OPCODEIN_1_CE"), "CE0")); - tg.config.add_enum(dsp + ".REG_OPCODEIN_1_RST", - str_or_default(ci->params, ctx->id("REG_OPCODEIN_1_RST"), "RST0")); - tg.config.add_enum(dsp + ".REG_OUTPUT0_CLK", - str_or_default(ci->params, ctx->id("REG_OUTPUT0_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_OUTPUT1_CLK", - str_or_default(ci->params, ctx->id("REG_OUTPUT1_CLK"), "NONE")); - tg.config.add_enum(dsp + ".REG_FLAG_CLK", str_or_default(ci->params, ctx->id("REG_FLAG_CLK"), "NONE")); - tg.config.add_enum(dsp + ".MCPAT_SOURCE", str_or_default(ci->params, ctx->id("MCPAT_SOURCE"), "STATIC")); - tg.config.add_enum(dsp + ".MASKPAT_SOURCE", - str_or_default(ci->params, ctx->id("MASKPAT_SOURCE"), "STATIC")); - tg.config.add_word(dsp + ".MASK01", - parse_init_str(str_or_default(ci->params, ctx->id("MASK01"), "0x00000000000000"), 56, - ci->name.c_str(ctx))); - tg.config.add_enum(dsp + ".CLK0_DIV", str_or_default(ci->params, ctx->id("CLK0_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".CLK1_DIV", str_or_default(ci->params, ctx->id("CLK1_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".CLK2_DIV", str_or_default(ci->params, ctx->id("CLK2_DIV"), "ENABLED")); - tg.config.add_enum(dsp + ".CLK3_DIV", str_or_default(ci->params, ctx->id("CLK3_DIV"), "ENABLED")); - tg.config.add_word(dsp + ".MCPAT", - parse_init_str(str_or_default(ci->params, ctx->id("MCPAT"), "0x00000000000000"), 56, - ci->name.c_str(ctx))); + str_or_default(ci->params, id_REG_OPCODEOP0_1_RST, "RST0")); + tg.config.add_enum(dsp + ".REG_OPCODEIN_0_CLK", str_or_default(ci->params, id_REG_OPCODEIN_0_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_OPCODEIN_0_CE", str_or_default(ci->params, id_REG_OPCODEIN_0_CE, "CE0")); + tg.config.add_enum(dsp + ".REG_OPCODEIN_0_RST", str_or_default(ci->params, id_REG_OPCODEIN_0_RST, "RST0")); + tg.config.add_enum(dsp + ".REG_OPCODEIN_1_CLK", str_or_default(ci->params, id_REG_OPCODEIN_1_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_OPCODEIN_1_CE", str_or_default(ci->params, id_REG_OPCODEIN_1_CE, "CE0")); + tg.config.add_enum(dsp + ".REG_OPCODEIN_1_RST", str_or_default(ci->params, id_REG_OPCODEIN_1_RST, "RST0")); + tg.config.add_enum(dsp + ".REG_OUTPUT0_CLK", str_or_default(ci->params, id_REG_OUTPUT0_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_OUTPUT1_CLK", str_or_default(ci->params, id_REG_OUTPUT1_CLK, "NONE")); + tg.config.add_enum(dsp + ".REG_FLAG_CLK", str_or_default(ci->params, id_REG_FLAG_CLK, "NONE")); + tg.config.add_enum(dsp + ".MCPAT_SOURCE", str_or_default(ci->params, id_MCPAT_SOURCE, "STATIC")); + tg.config.add_enum(dsp + ".MASKPAT_SOURCE", str_or_default(ci->params, id_MASKPAT_SOURCE, "STATIC")); + tg.config.add_word( + dsp + ".MASK01", + parse_init_str(str_or_default(ci->params, id_MASK01, "0x00000000000000"), 56, ci->name.c_str(ctx))); + tg.config.add_enum(dsp + ".CLK0_DIV", str_or_default(ci->params, id_CLK0_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".CLK1_DIV", str_or_default(ci->params, id_CLK1_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".CLK2_DIV", str_or_default(ci->params, id_CLK2_DIV, "ENABLED")); + tg.config.add_enum(dsp + ".CLK3_DIV", str_or_default(ci->params, id_CLK3_DIV, "ENABLED")); + tg.config.add_word(dsp + ".MCPAT", parse_init_str(str_or_default(ci->params, id_MCPAT, "0x00000000000000"), + 56, ci->name.c_str(ctx))); tg.config.add_word(dsp + ".MASKPAT", - parse_init_str(str_or_default(ci->params, ctx->id("MASKPAT"), "0x00000000000000"), 56, - ci->name.c_str(ctx))); - tg.config.add_word(dsp + ".RNDPAT", - parse_init_str(str_or_default(ci->params, ctx->id("RNDPAT"), "0x00000000000000"), 56, + parse_init_str(str_or_default(ci->params, id_MASKPAT, "0x00000000000000"), 56, ci->name.c_str(ctx))); - tg.config.add_enum(dsp + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); - tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC")); + tg.config.add_word( + dsp + ".RNDPAT", + parse_init_str(str_or_default(ci->params, id_RNDPAT, "0x00000000000000"), 56, ci->name.c_str(ctx))); + tg.config.add_enum(dsp + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED")); + tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, id_RESETMODE, "SYNC")); tg.config.add_enum(dsp + ".FORCE_ZERO_BARREL_SHIFT", - str_or_default(ci->params, ctx->id("FORCE_ZERO_BARREL_SHIFT"), "DISABLED")); - tg.config.add_enum(dsp + ".LEGACY", str_or_default(ci->params, ctx->id("LEGACY"), "DISABLED")); + str_or_default(ci->params, id_FORCE_ZERO_BARREL_SHIFT, "DISABLED")); + tg.config.add_enum(dsp + ".LEGACY", str_or_default(ci->params, id_LEGACY, "DISABLED")); tg.config.add_enum(dsp + ".MODE", "ALU54B"); @@ -1332,14 +1298,14 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum("DSP_LEFT.CIBOUT", "ON"); else tg.config.add_enum("DSP_RIGHT.CIBOUT", "ON"); - if (str_or_default(ci->params, ctx->id("REG_FLAG_CLK"), "NONE") == "NONE") { + if (str_or_default(ci->params, id_REG_FLAG_CLK, "NONE") == "NONE") { if (dsp == "ALU54_7") { tg.config.add_enum("MULT18_5.CIBOUT_BYP", "ON"); } else if (dsp == "ALU54_3") { tg.config.add_enum("MULT18_5.CIBOUT_BYP", "ON"); } } - if (str_or_default(ci->params, ctx->id("REG_OUTPUT0_CLK"), "NONE") == "NONE") { + if (str_or_default(ci->params, id_REG_OUTPUT0_CLK, "NONE") == "NONE") { if (dsp == "ALU54_7") { tg.config.add_enum("MULT18_4.CIBOUT_BYP", "ON"); } else if (dsp == "ALU54_3") { @@ -1354,14 +1320,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum("MODE", "EHXPLLL"); - tg.config.add_word("CLKI_DIV", int_to_bitvector(int_or_default(ci->params, ctx->id("CLKI_DIV"), 1) - 1, 7)); - tg.config.add_word("CLKFB_DIV", - int_to_bitvector(int_or_default(ci->params, ctx->id("CLKFB_DIV"), 1) - 1, 7)); + tg.config.add_word("CLKI_DIV", int_to_bitvector(int_or_default(ci->params, id_CLKI_DIV, 1) - 1, 7)); + tg.config.add_word("CLKFB_DIV", int_to_bitvector(int_or_default(ci->params, id_CLKFB_DIV, 1) - 1, 7)); - tg.config.add_enum("CLKOP_ENABLE", str_or_default(ci->params, ctx->id("CLKOP_ENABLE"), "ENABLED")); - tg.config.add_enum("CLKOS_ENABLE", str_or_default(ci->params, ctx->id("CLKOS_ENABLE"), "ENABLED")); - tg.config.add_enum("CLKOS2_ENABLE", str_or_default(ci->params, ctx->id("CLKOS2_ENABLE"), "ENABLED")); - tg.config.add_enum("CLKOS3_ENABLE", str_or_default(ci->params, ctx->id("CLKOS3_ENABLE"), "ENABLED")); + tg.config.add_enum("CLKOP_ENABLE", str_or_default(ci->params, id_CLKOP_ENABLE, "ENABLED")); + tg.config.add_enum("CLKOS_ENABLE", str_or_default(ci->params, id_CLKOS_ENABLE, "ENABLED")); + tg.config.add_enum("CLKOS2_ENABLE", str_or_default(ci->params, id_CLKOS2_ENABLE, "ENABLED")); + tg.config.add_enum("CLKOS3_ENABLE", str_or_default(ci->params, id_CLKOS3_ENABLE, "ENABLED")); for (std::string out : {"CLKOP", "CLKOS", "CLKOS2", "CLKOS3"}) { tg.config.add_word(out + "_DIV", @@ -1372,73 +1337,59 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex int_to_bitvector(int_or_default(ci->params, ctx->id(out + "_FPHASE"), 0), 3)); } - tg.config.add_enum("FEEDBK_PATH", str_or_default(ci->params, ctx->id("FEEDBK_PATH"), "CLKOP")); - tg.config.add_enum("CLKOP_TRIM_POL", str_or_default(ci->params, ctx->id("CLKOP_TRIM_POL"), "RISING")); + tg.config.add_enum("FEEDBK_PATH", str_or_default(ci->params, id_FEEDBK_PATH, "CLKOP")); + tg.config.add_enum("CLKOP_TRIM_POL", str_or_default(ci->params, id_CLKOP_TRIM_POL, "RISING")); - tg.config.add_enum("CLKOP_TRIM_DELAY", intstr_or_default(ci->params, ctx->id("CLKOP_TRIM_DELAY"), "0")); + tg.config.add_enum("CLKOP_TRIM_DELAY", intstr_or_default(ci->params, id_CLKOP_TRIM_DELAY, "0")); - tg.config.add_enum("CLKOS_TRIM_POL", str_or_default(ci->params, ctx->id("CLKOS_TRIM_POL"), "RISING")); + tg.config.add_enum("CLKOS_TRIM_POL", str_or_default(ci->params, id_CLKOS_TRIM_POL, "RISING")); - tg.config.add_enum("CLKOS_TRIM_DELAY", intstr_or_default(ci->params, ctx->id("CLKOS_TRIM_DELAY"), "0")); + tg.config.add_enum("CLKOS_TRIM_DELAY", intstr_or_default(ci->params, id_CLKOS_TRIM_DELAY, "0")); - tg.config.add_enum("OUTDIVIDER_MUXA", str_or_default(ci->params, ctx->id("OUTDIVIDER_MUXA"), + tg.config.add_enum("OUTDIVIDER_MUXA", str_or_default(ci->params, id_OUTDIVIDER_MUXA, get_net_or_empty(ci, id_CLKOP) ? "DIVA" : "REFCLK")); - tg.config.add_enum("OUTDIVIDER_MUXB", str_or_default(ci->params, ctx->id("OUTDIVIDER_MUXB"), + tg.config.add_enum("OUTDIVIDER_MUXB", str_or_default(ci->params, id_OUTDIVIDER_MUXB, get_net_or_empty(ci, id_CLKOP) ? "DIVB" : "REFCLK")); - tg.config.add_enum("OUTDIVIDER_MUXC", str_or_default(ci->params, ctx->id("OUTDIVIDER_MUXC"), + tg.config.add_enum("OUTDIVIDER_MUXC", str_or_default(ci->params, id_OUTDIVIDER_MUXC, get_net_or_empty(ci, id_CLKOP) ? "DIVC" : "REFCLK")); - tg.config.add_enum("OUTDIVIDER_MUXD", str_or_default(ci->params, ctx->id("OUTDIVIDER_MUXD"), + tg.config.add_enum("OUTDIVIDER_MUXD", str_or_default(ci->params, id_OUTDIVIDER_MUXD, get_net_or_empty(ci, id_CLKOP) ? "DIVD" : "REFCLK")); - tg.config.add_word("PLL_LOCK_MODE", - int_to_bitvector(int_or_default(ci->params, ctx->id("PLL_LOCK_MODE"), 0), 3)); - - tg.config.add_enum("STDBY_ENABLE", str_or_default(ci->params, ctx->id("STDBY_ENABLE"), "DISABLED")); - tg.config.add_enum("REFIN_RESET", str_or_default(ci->params, ctx->id("REFIN_RESET"), "DISABLED")); - tg.config.add_enum("SYNC_ENABLE", str_or_default(ci->params, ctx->id("SYNC_ENABLE"), "DISABLED")); - tg.config.add_enum("INT_LOCK_STICKY", str_or_default(ci->params, ctx->id("INT_LOCK_STICKY"), "ENABLED")); - tg.config.add_enum("DPHASE_SOURCE", str_or_default(ci->params, ctx->id("DPHASE_SOURCE"), "DISABLED")); - tg.config.add_enum("PLLRST_ENA", str_or_default(ci->params, ctx->id("PLLRST_ENA"), "DISABLED")); - tg.config.add_enum("INTFB_WAKE", str_or_default(ci->params, ctx->id("INTFB_WAKE"), "DISABLED")); - - tg.config.add_word("KVCO", int_to_bitvector(int_or_default(ci->attrs, ctx->id("KVCO"), 0), 3)); - tg.config.add_word("LPF_CAPACITOR", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("LPF_CAPACITOR"), 0), 2)); - tg.config.add_word("LPF_RESISTOR", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("LPF_RESISTOR"), 0), 7)); - tg.config.add_word("ICP_CURRENT", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("ICP_CURRENT"), 0), 5)); + tg.config.add_word("PLL_LOCK_MODE", int_to_bitvector(int_or_default(ci->params, id_PLL_LOCK_MODE, 0), 3)); + + tg.config.add_enum("STDBY_ENABLE", str_or_default(ci->params, id_STDBY_ENABLE, "DISABLED")); + tg.config.add_enum("REFIN_RESET", str_or_default(ci->params, id_REFIN_RESET, "DISABLED")); + tg.config.add_enum("SYNC_ENABLE", str_or_default(ci->params, id_SYNC_ENABLE, "DISABLED")); + tg.config.add_enum("INT_LOCK_STICKY", str_or_default(ci->params, id_INT_LOCK_STICKY, "ENABLED")); + tg.config.add_enum("DPHASE_SOURCE", str_or_default(ci->params, id_DPHASE_SOURCE, "DISABLED")); + tg.config.add_enum("PLLRST_ENA", str_or_default(ci->params, id_PLLRST_ENA, "DISABLED")); + tg.config.add_enum("INTFB_WAKE", str_or_default(ci->params, id_INTFB_WAKE, "DISABLED")); + + tg.config.add_word("KVCO", int_to_bitvector(int_or_default(ci->attrs, id_KVCO, 0), 3)); + tg.config.add_word("LPF_CAPACITOR", int_to_bitvector(int_or_default(ci->attrs, id_LPF_CAPACITOR, 0), 2)); + tg.config.add_word("LPF_RESISTOR", int_to_bitvector(int_or_default(ci->attrs, id_LPF_RESISTOR, 0), 7)); + tg.config.add_word("ICP_CURRENT", int_to_bitvector(int_or_default(ci->attrs, id_ICP_CURRENT, 0), 5)); tg.config.add_word("FREQ_LOCK_ACCURACY", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("FREQ_LOCK_ACCURACY"), 0), 2)); + int_to_bitvector(int_or_default(ci->attrs, id_FREQ_LOCK_ACCURACY, 0), 2)); - tg.config.add_word("MFG_GMC_GAIN", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_GMC_GAIN"), 0), 3)); - tg.config.add_word("MFG_GMC_TEST", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_GMC_TEST"), 14), 4)); - tg.config.add_word("MFG1_TEST", int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG1_TEST"), 0), 3)); - tg.config.add_word("MFG2_TEST", int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG2_TEST"), 0), 3)); + tg.config.add_word("MFG_GMC_GAIN", int_to_bitvector(int_or_default(ci->attrs, id_MFG_GMC_GAIN, 0), 3)); + tg.config.add_word("MFG_GMC_TEST", int_to_bitvector(int_or_default(ci->attrs, id_MFG_GMC_TEST, 14), 4)); + tg.config.add_word("MFG1_TEST", int_to_bitvector(int_or_default(ci->attrs, id_MFG1_TEST, 0), 3)); + tg.config.add_word("MFG2_TEST", int_to_bitvector(int_or_default(ci->attrs, id_MFG2_TEST, 0), 3)); tg.config.add_word("MFG_FORCE_VFILTER", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_FORCE_VFILTER"), 0), 1)); - tg.config.add_word("MFG_ICP_TEST", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ICP_TEST"), 0), 1)); - tg.config.add_word("MFG_EN_UP", int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_EN_UP"), 0), 1)); - tg.config.add_word("MFG_FLOAT_ICP", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_FLOAT_ICP"), 0), 1)); - tg.config.add_word("MFG_GMC_PRESET", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_GMC_PRESET"), 0), 1)); - tg.config.add_word("MFG_LF_PRESET", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_LF_PRESET"), 0), 1)); - tg.config.add_word("MFG_GMC_RESET", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_GMC_RESET"), 0), 1)); - tg.config.add_word("MFG_LF_RESET", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_LF_RESET"), 0), 1)); - tg.config.add_word("MFG_LF_RESGRND", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_LF_RESGRND"), 0), 1)); - tg.config.add_word("MFG_GMCREF_SEL", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_GMCREF_SEL"), 0), 2)); + int_to_bitvector(int_or_default(ci->attrs, id_MFG_FORCE_VFILTER, 0), 1)); + tg.config.add_word("MFG_ICP_TEST", int_to_bitvector(int_or_default(ci->attrs, id_MFG_ICP_TEST, 0), 1)); + tg.config.add_word("MFG_EN_UP", int_to_bitvector(int_or_default(ci->attrs, id_MFG_EN_UP, 0), 1)); + tg.config.add_word("MFG_FLOAT_ICP", int_to_bitvector(int_or_default(ci->attrs, id_MFG_FLOAT_ICP, 0), 1)); + tg.config.add_word("MFG_GMC_PRESET", int_to_bitvector(int_or_default(ci->attrs, id_MFG_GMC_PRESET, 0), 1)); + tg.config.add_word("MFG_LF_PRESET", int_to_bitvector(int_or_default(ci->attrs, id_MFG_LF_PRESET, 0), 1)); + tg.config.add_word("MFG_GMC_RESET", int_to_bitvector(int_or_default(ci->attrs, id_MFG_GMC_RESET, 0), 1)); + tg.config.add_word("MFG_LF_RESET", int_to_bitvector(int_or_default(ci->attrs, id_MFG_LF_RESET, 0), 1)); + tg.config.add_word("MFG_LF_RESGRND", int_to_bitvector(int_or_default(ci->attrs, id_MFG_LF_RESGRND, 0), 1)); + tg.config.add_word("MFG_GMCREF_SEL", int_to_bitvector(int_or_default(ci->attrs, id_MFG_GMCREF_SEL, 0), 2)); tg.config.add_word("MFG_ENABLE_FILTEROPAMP", - int_to_bitvector(int_or_default(ci->attrs, ctx->id("MFG_ENABLE_FILTEROPAMP"), 0), 1)); + int_to_bitvector(int_or_default(ci->attrs, id_MFG_ENABLE_FILTEROPAMP, 0), 1)); cc.tilegroups.push_back(tg); } else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) { @@ -1466,23 +1417,22 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } else if (ci->type == id_EXTREFB) { TileGroup tg; tg.tiles = get_dcu_tiles(ctx, ci->bel); - tg.config.add_word( - "EXTREF.REFCK_DCBIAS_EN", - parse_config_str(get_or_default(ci->params, ctx->id("REFCK_DCBIAS_EN"), Property(0)), 1)); + tg.config.add_word("EXTREF.REFCK_DCBIAS_EN", + parse_config_str(get_or_default(ci->params, id_REFCK_DCBIAS_EN, Property(0)), 1)); tg.config.add_word("EXTREF.REFCK_RTERM", - parse_config_str(get_or_default(ci->params, ctx->id("REFCK_RTERM"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_REFCK_RTERM, Property(0)), 1)); tg.config.add_word("EXTREF.REFCK_PWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("REFCK_PWDNB"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_REFCK_PWDNB, Property(0)), 1)); cc.tilegroups.push_back(tg); } else if (ci->type == id_PCSCLKDIV) { Loc loc = ctx->getBelLocation(ci->bel); std::string tname = ctx->get_tile_by_type_loc(loc.y + 1, loc.x, "BMID_0H"); cc.tiles[tname].add_enum("PCSCLKDIV" + std::to_string(loc.z), - str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); + str_or_default(ci->params, id_GSR, "ENABLED")); } else if (ci->type == id_DTR) { cc.tiles[ctx->get_tile_by_type("DTR")].add_enum("DTR.MODE", "DTR"); } else if (ci->type == id_OSCG) { - int div = int_or_default(ci->params, ctx->id("DIV"), 128); + int div = int_or_default(ci->params, id_DIV, 128); if (div == 128) div = 127; cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum("OSC.DIV", std::to_string(div)); @@ -1494,22 +1444,22 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex log_warning("USRMCLK will not function correctly when MASTER_SPI_PORT is set to ENABLE.\n"); cc.tiles[ctx->get_tile_by_type("EFB3_PICB1")].add_enum("CCLK.MODE", "USRMCLK"); } else if (ci->type == id_GSR) { - cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum( - "GSR.GSRMODE", str_or_default(ci->params, ctx->id("MODE"), "ACTIVE_LOW")); - cc.tiles[ctx->get_tile_by_type("VIQ_BUF")].add_enum( - "GSR.SYNCMODE", str_or_default(ci->params, ctx->id("SYNCMODE"), "ASYNC")); + cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum("GSR.GSRMODE", + str_or_default(ci->params, id_MODE, "ACTIVE_LOW")); + cc.tiles[ctx->get_tile_by_type("VIQ_BUF")].add_enum("GSR.SYNCMODE", + str_or_default(ci->params, id_SYNCMODE, "ASYNC")); } else if (ci->type == id_JTAGG) { - cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum( - "JTAG.ER1", str_or_default(ci->params, ctx->id("ER1"), "ENABLED")); - cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum( - "JTAG.ER2", str_or_default(ci->params, ctx->id("ER2"), "ENABLED")); + cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum("JTAG.ER1", + str_or_default(ci->params, id_ER1, "ENABLED")); + cc.tiles[ctx->get_tile_by_type("EFB0_PICB0")].add_enum("JTAG.ER2", + str_or_default(ci->params, id_ER2, "ENABLED")); } else if (ci->type == id_CLKDIVF) { Loc loc = ctx->getBelLocation(ci->bel); bool r = loc.x > 5; std::string clkdiv = std::string("CLKDIV_") + (r ? "R" : "L") + std::to_string(loc.z); std::string tile = ctx->get_tile_by_type(std::string("ECLK_") + (r ? "R" : "L")); - cc.tiles[tile].add_enum(clkdiv + ".DIV", str_or_default(ci->params, ctx->id("DIV"), "2.0")); - cc.tiles[tile].add_enum(clkdiv + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED")); + cc.tiles[tile].add_enum(clkdiv + ".DIV", str_or_default(ci->params, id_DIV, "2.0")); + cc.tiles[tile].add_enum(clkdiv + ".GSR", str_or_default(ci->params, id_GSR, "DISABLED")); } else if (ci->type == id_TRELLIS_ECLKBUF) { } else if (ci->type == id_DQSBUFM) { Loc loc = ctx->getBelLocation(ci->bel); @@ -1521,13 +1471,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.tiles.push_back(ctx->get_tile_by_type_loc(loc.y, loc.x, pic + "0_DQS2")); tg.tiles.push_back(ctx->get_tile_by_type_loc(loc.y + 1, loc.x, pic + "1_DQS3")); tg.config.add_enum("DQS.MODE", "DQSBUFM"); - tg.config.add_enum("DQS.DQS_LI_DEL_ADJ", str_or_default(ci->params, ctx->id("DQS_LI_DEL_ADJ"), "PLUS")); - tg.config.add_enum("DQS.DQS_LO_DEL_ADJ", str_or_default(ci->params, ctx->id("DQS_LO_DEL_ADJ"), "PLUS")); - int li_del_value = int_or_default(ci->params, ctx->id("DQS_LI_DEL_VAL"), 0); - if (str_or_default(ci->params, ctx->id("DQS_LI_DEL_ADJ"), "PLUS") == "MINUS") + tg.config.add_enum("DQS.DQS_LI_DEL_ADJ", str_or_default(ci->params, id_DQS_LI_DEL_ADJ, "PLUS")); + tg.config.add_enum("DQS.DQS_LO_DEL_ADJ", str_or_default(ci->params, id_DQS_LO_DEL_ADJ, "PLUS")); + int li_del_value = int_or_default(ci->params, id_DQS_LI_DEL_VAL, 0); + if (str_or_default(ci->params, id_DQS_LI_DEL_ADJ, "PLUS") == "MINUS") li_del_value = (256 - li_del_value) & 0xFF; - int lo_del_value = int_or_default(ci->params, ctx->id("DQS_LO_DEL_VAL"), 0); - if (str_or_default(ci->params, ctx->id("DQS_LO_DEL_ADJ"), "PLUS") == "MINUS") + int lo_del_value = int_or_default(ci->params, id_DQS_LO_DEL_VAL, 0); + if (str_or_default(ci->params, id_DQS_LO_DEL_ADJ, "PLUS") == "MINUS") lo_del_value = (256 - lo_del_value) & 0xFF; tg.config.add_word("DQS.DQS_LI_DEL_VAL", int_to_bitvector(li_del_value, 8)); tg.config.add_word("DQS.DQS_LO_DEL_VAL", int_to_bitvector(lo_del_value, 8)); @@ -1539,7 +1489,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex ? "YES" : "NO"); tg.config.add_enum("DQS.DDRDEL", get_net_or_empty(ci, id_DDRDEL) != nullptr ? "DDRDEL" : "0"); - tg.config.add_enum("DQS.GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED")); + tg.config.add_enum("DQS.GSR", str_or_default(ci->params, id_GSR, "DISABLED")); cc.tilegroups.push_back(tg); } else if (ci->type == id_ECLKSYNCB) { Loc loc = ctx->getBelLocation(ci->bel); @@ -1565,9 +1515,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tiletype += "A"; std::string tile = ctx->get_tile_by_type(tiletype); cc.tiles[tile].add_enum("DDRDLL.MODE", "DDRDLLA"); - cc.tiles[tile].add_enum("DDRDLL.GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED")); - cc.tiles[tile].add_enum("DDRDLL.FORCE_MAX_DELAY", - str_or_default(ci->params, ctx->id("FORCE_MAX_DELAY"), "NO")); + cc.tiles[tile].add_enum("DDRDLL.GSR", str_or_default(ci->params, id_GSR, "DISABLED")); + cc.tiles[tile].add_enum("DDRDLL.FORCE_MAX_DELAY", str_or_default(ci->params, id_FORCE_MAX_DELAY, "NO")); } else { NPNR_ASSERT_FALSE("unsupported cell type"); } diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 18d9107f51..6d88af7529 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -47,129 +47,129 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str } }; - if (type == ctx->id("TRELLIS_SLICE")) { - new_cell->params[ctx->id("MODE")] = std::string("LOGIC"); - new_cell->params[ctx->id("GSR")] = std::string("DISABLED"); - new_cell->params[ctx->id("SRMODE")] = std::string("LSR_OVER_CE"); - new_cell->params[ctx->id("CEMUX")] = std::string("1"); - new_cell->params[ctx->id("CLKMUX")] = std::string("CLK"); - new_cell->params[ctx->id("LSRMUX")] = std::string("LSR"); - new_cell->params[ctx->id("LUT0_INITVAL")] = Property(0, 16); - new_cell->params[ctx->id("LUT1_INITVAL")] = Property(0, 16); - new_cell->params[ctx->id("REG0_SD")] = std::string("0"); - new_cell->params[ctx->id("REG1_SD")] = std::string("0"); - new_cell->params[ctx->id("REG0_REGSET")] = std::string("RESET"); - new_cell->params[ctx->id("REG1_REGSET")] = std::string("RESET"); - new_cell->params[ctx->id("CCU2_INJECT1_0")] = std::string("NO"); - new_cell->params[ctx->id("CCU2_INJECT1_1")] = std::string("NO"); - new_cell->params[ctx->id("WREMUX")] = std::string("WRE"); - - new_cell->addInput(ctx->id("A0")); - new_cell->addInput(ctx->id("B0")); - new_cell->addInput(ctx->id("C0")); - new_cell->addInput(ctx->id("D0")); - - new_cell->addInput(ctx->id("A1")); - new_cell->addInput(ctx->id("B1")); - new_cell->addInput(ctx->id("C1")); - new_cell->addInput(ctx->id("D1")); - - new_cell->addInput(ctx->id("M0")); - new_cell->addInput(ctx->id("M1")); - - new_cell->addInput(ctx->id("FCI")); - new_cell->addInput(ctx->id("FXA")); - new_cell->addInput(ctx->id("FXB")); - - new_cell->addInput(ctx->id("CLK")); - new_cell->addInput(ctx->id("LSR")); - new_cell->addInput(ctx->id("CE")); - - new_cell->addInput(ctx->id("DI0")); - new_cell->addInput(ctx->id("DI1")); - - new_cell->addInput(ctx->id("WD0")); - new_cell->addInput(ctx->id("WD1")); - new_cell->addInput(ctx->id("WAD0")); - new_cell->addInput(ctx->id("WAD1")); - new_cell->addInput(ctx->id("WAD2")); - new_cell->addInput(ctx->id("WAD3")); - new_cell->addInput(ctx->id("WRE")); - new_cell->addInput(ctx->id("WCK")); - - new_cell->addOutput(ctx->id("F0")); - new_cell->addOutput(ctx->id("Q0")); - new_cell->addOutput(ctx->id("F1")); - new_cell->addOutput(ctx->id("Q1")); - - new_cell->addOutput(ctx->id("FCO")); - new_cell->addOutput(ctx->id("OFX0")); - new_cell->addOutput(ctx->id("OFX1")); - - new_cell->addOutput(ctx->id("WDO0")); - new_cell->addOutput(ctx->id("WDO1")); - new_cell->addOutput(ctx->id("WDO2")); - new_cell->addOutput(ctx->id("WDO3")); - new_cell->addOutput(ctx->id("WADO0")); - new_cell->addOutput(ctx->id("WADO1")); - new_cell->addOutput(ctx->id("WADO2")); - new_cell->addOutput(ctx->id("WADO3")); - } else if (type == ctx->id("TRELLIS_IO")) { - new_cell->params[ctx->id("DIR")] = std::string("INPUT"); - new_cell->attrs[ctx->id("IO_TYPE")] = std::string("LVCMOS33"); - new_cell->params[ctx->id("DATAMUX_ODDR")] = std::string("PADDO"); - new_cell->params[ctx->id("DATAMUX_MDDR")] = std::string("PADDO"); - - new_cell->addInout(ctx->id("B")); - new_cell->addInput(ctx->id("I")); - new_cell->addInput(ctx->id("T")); - new_cell->addOutput(ctx->id("O")); - - new_cell->addInput(ctx->id("IOLDO")); - new_cell->addInput(ctx->id("IOLTO")); - - } else if (type == ctx->id("LUT4")) { - new_cell->params[ctx->id("INIT")] = Property(0, 16); - - new_cell->addInput(ctx->id("A")); - new_cell->addInput(ctx->id("B")); - new_cell->addInput(ctx->id("C")); - new_cell->addInput(ctx->id("D")); - new_cell->addOutput(ctx->id("Z")); - } else if (type == ctx->id("CCU2C")) { - new_cell->params[ctx->id("INIT0")] = Property(0, 16); - new_cell->params[ctx->id("INIT1")] = Property(0, 16); - new_cell->params[ctx->id("INJECT1_0")] = std::string("YES"); - new_cell->params[ctx->id("INJECT1_1")] = std::string("YES"); - - new_cell->addInput(ctx->id("CIN")); - - new_cell->addInput(ctx->id("A0")); - new_cell->addInput(ctx->id("B0")); - new_cell->addInput(ctx->id("C0")); - new_cell->addInput(ctx->id("D0")); - - new_cell->addInput(ctx->id("A1")); - new_cell->addInput(ctx->id("B1")); - new_cell->addInput(ctx->id("C1")); - new_cell->addInput(ctx->id("D1")); - - new_cell->addOutput(ctx->id("S0")); - new_cell->addOutput(ctx->id("S1")); - new_cell->addOutput(ctx->id("COUT")); - - } else if (type == ctx->id("DCCA")) { - new_cell->addInput(ctx->id("CLKI")); - new_cell->addOutput(ctx->id("CLKO")); - new_cell->addInput(ctx->id("CE")); + if (type == id_TRELLIS_SLICE) { + new_cell->params[id_MODE] = std::string("LOGIC"); + new_cell->params[id_GSR] = std::string("DISABLED"); + new_cell->params[id_SRMODE] = std::string("LSR_OVER_CE"); + new_cell->params[id_CEMUX] = std::string("1"); + new_cell->params[id_CLKMUX] = std::string("CLK"); + new_cell->params[id_LSRMUX] = std::string("LSR"); + new_cell->params[id_LUT0_INITVAL] = Property(0, 16); + new_cell->params[id_LUT1_INITVAL] = Property(0, 16); + new_cell->params[id_REG0_SD] = std::string("0"); + new_cell->params[id_REG1_SD] = std::string("0"); + new_cell->params[id_REG0_REGSET] = std::string("RESET"); + new_cell->params[id_REG1_REGSET] = std::string("RESET"); + new_cell->params[id_CCU2_INJECT1_0] = std::string("NO"); + new_cell->params[id_CCU2_INJECT1_1] = std::string("NO"); + new_cell->params[id_WREMUX] = std::string("WRE"); + + new_cell->addInput(id_A0); + new_cell->addInput(id_B0); + new_cell->addInput(id_C0); + new_cell->addInput(id_D0); + + new_cell->addInput(id_A1); + new_cell->addInput(id_B1); + new_cell->addInput(id_C1); + new_cell->addInput(id_D1); + + new_cell->addInput(id_M0); + new_cell->addInput(id_M1); + + new_cell->addInput(id_FCI); + new_cell->addInput(id_FXA); + new_cell->addInput(id_FXB); + + new_cell->addInput(id_CLK); + new_cell->addInput(id_LSR); + new_cell->addInput(id_CE); + + new_cell->addInput(id_DI0); + new_cell->addInput(id_DI1); + + new_cell->addInput(id_WD0); + new_cell->addInput(id_WD1); + new_cell->addInput(id_WAD0); + new_cell->addInput(id_WAD1); + new_cell->addInput(id_WAD2); + new_cell->addInput(id_WAD3); + new_cell->addInput(id_WRE); + new_cell->addInput(id_WCK); + + new_cell->addOutput(id_F0); + new_cell->addOutput(id_Q0); + new_cell->addOutput(id_F1); + new_cell->addOutput(id_Q1); + + new_cell->addOutput(id_FCO); + new_cell->addOutput(id_OFX0); + new_cell->addOutput(id_OFX1); + + new_cell->addOutput(id_WDO0); + new_cell->addOutput(id_WDO1); + new_cell->addOutput(id_WDO2); + new_cell->addOutput(id_WDO3); + new_cell->addOutput(id_WADO0); + new_cell->addOutput(id_WADO1); + new_cell->addOutput(id_WADO2); + new_cell->addOutput(id_WADO3); + } else if (type == id_TRELLIS_IO) { + new_cell->params[id_DIR] = std::string("INPUT"); + new_cell->attrs[id_IO_TYPE] = std::string("LVCMOS33"); + new_cell->params[id_DATAMUX_ODDR] = std::string("PADDO"); + new_cell->params[id_DATAMUX_MDDR] = std::string("PADDO"); + + new_cell->addInout(id_B); + new_cell->addInput(id_I); + new_cell->addInput(id_T); + new_cell->addOutput(id_O); + + new_cell->addInput(id_IOLDO); + new_cell->addInput(id_IOLTO); + + } else if (type == id_LUT4) { + new_cell->params[id_INIT] = Property(0, 16); + + new_cell->addInput(id_A); + new_cell->addInput(id_B); + new_cell->addInput(id_C); + new_cell->addInput(id_D); + new_cell->addOutput(id_Z); + } else if (type == id_CCU2C) { + new_cell->params[id_INIT0] = Property(0, 16); + new_cell->params[id_INIT1] = Property(0, 16); + new_cell->params[id_INJECT1_0] = std::string("YES"); + new_cell->params[id_INJECT1_1] = std::string("YES"); + + new_cell->addInput(id_CIN); + + new_cell->addInput(id_A0); + new_cell->addInput(id_B0); + new_cell->addInput(id_C0); + new_cell->addInput(id_D0); + + new_cell->addInput(id_A1); + new_cell->addInput(id_B1); + new_cell->addInput(id_C1); + new_cell->addInput(id_D1); + + new_cell->addOutput(id_S0); + new_cell->addOutput(id_S1); + new_cell->addOutput(id_COUT); + + } else if (type == id_DCCA) { + new_cell->addInput(id_CLKI); + new_cell->addOutput(id_CLKO); + new_cell->addInput(id_CE); } else if (type == id_IOLOGIC || type == id_SIOLOGIC) { - new_cell->params[ctx->id("MODE")] = std::string("NONE"); - new_cell->params[ctx->id("GSR")] = std::string("DISABLED"); - new_cell->params[ctx->id("CLKIMUX")] = std::string("CLK"); - new_cell->params[ctx->id("CLKOMUX")] = std::string("CLK"); - new_cell->params[ctx->id("LSRIMUX")] = std::string("0"); - new_cell->params[ctx->id("LSROMUX")] = std::string("0"); - new_cell->params[ctx->id("LSRMUX")] = std::string("LSR"); + new_cell->params[id_MODE] = std::string("NONE"); + new_cell->params[id_GSR] = std::string("DISABLED"); + new_cell->params[id_CLKIMUX] = std::string("CLK"); + new_cell->params[id_CLKOMUX] = std::string("CLK"); + new_cell->params[id_LSRIMUX] = std::string("0"); + new_cell->params[id_LSROMUX] = std::string("0"); + new_cell->params[id_LSRMUX] = std::string("LSR"); new_cell->params[ctx->id("DELAY.OUTDEL")] = std::string("DISABLED"); new_cell->params[ctx->id("DELAY.DEL_VALUE")] = Property(0, 7); @@ -183,7 +183,7 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->params[ctx->id("MODDRX.MODE")] = std::string("NONE"); new_cell->params[ctx->id("MTDDRX.MODE")] = std::string("NONE"); - new_cell->params[ctx->id("IOLTOMUX")] = std::string("NONE"); + new_cell->params[id_IOLTOMUX] = std::string("NONE"); new_cell->params[ctx->id("MTDDRX.DQSW_INVERT")] = std::string("DISABLED"); new_cell->params[ctx->id("MTDDRX.REGSET")] = std::string("RESET"); @@ -192,8 +192,8 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str // Just copy ports from the Bel copy_bel_ports(); } else if (type == id_TRELLIS_ECLKBUF) { - new_cell->addInput(ctx->id("ECLKI")); - new_cell->addOutput(ctx->id("ECLKO")); + new_cell->addInput(id_ECLKI); + new_cell->addOutput(id_ECLKO); } else { log_error("unable to create ECP5 cell of type %s", type.c_str(ctx)); } @@ -225,38 +225,38 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive { if (lc->hierpath == IdString()) lc->hierpath = ff->hierpath; - bool has_ff = lc->ports.at(ctx->id("Q0")).net != nullptr || lc->ports.at(ctx->id("Q1")).net != nullptr; + bool has_ff = lc->ports.at(id_Q0).net != nullptr || lc->ports.at(id_Q1).net != nullptr; std::string reg = "REG" + std::to_string(index); - set_param_safe(has_ff, lc, ctx->id("SRMODE"), str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE")); - set_param_safe(has_ff, lc, ctx->id("GSR"), str_or_default(ff->params, ctx->id("GSR"), "DISABLED")); - set_param_safe(has_ff, lc, ctx->id("CEMUX"), str_or_default(ff->params, ctx->id("CEMUX"), "1")); - set_param_safe(has_ff, lc, ctx->id("LSRMUX"), str_or_default(ff->params, ctx->id("LSRMUX"), "LSR")); - set_param_safe(has_ff, lc, ctx->id("CLKMUX"), str_or_default(ff->params, ctx->id("CLKMUX"), "CLK")); + set_param_safe(has_ff, lc, id_SRMODE, str_or_default(ff->params, id_SRMODE, "LSR_OVER_CE")); + set_param_safe(has_ff, lc, id_GSR, str_or_default(ff->params, id_GSR, "DISABLED")); + set_param_safe(has_ff, lc, id_CEMUX, str_or_default(ff->params, id_CEMUX, "1")); + set_param_safe(has_ff, lc, id_LSRMUX, str_or_default(ff->params, id_LSRMUX, "LSR")); + set_param_safe(has_ff, lc, id_CLKMUX, str_or_default(ff->params, id_CLKMUX, "CLK")); lc->params[ctx->id(reg + "_SD")] = std::string(driven_by_lut ? "1" : "0"); - lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET"); - lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, ctx->id("LSRMODE"), "LSR"); - replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK")); - if (ff->ports.find(ctx->id("LSR")) != ff->ports.end()) - replace_port_safe(has_ff, ff, ctx->id("LSR"), lc, ctx->id("LSR")); - if (ff->ports.find(ctx->id("CE")) != ff->ports.end()) - replace_port_safe(has_ff, ff, ctx->id("CE"), lc, ctx->id("CE")); - - replace_port(ff, ctx->id("Q"), lc, ctx->id("Q" + std::to_string(index))); - if (get_net_or_empty(ff, ctx->id("M")) != nullptr) { + lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, id_REGSET, "RESET"); + lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, id_LSRMODE, "LSR"); + replace_port_safe(has_ff, ff, id_CLK, lc, id_CLK); + if (ff->ports.find(id_LSR) != ff->ports.end()) + replace_port_safe(has_ff, ff, id_LSR, lc, id_LSR); + if (ff->ports.find(id_CE) != ff->ports.end()) + replace_port_safe(has_ff, ff, id_CE, lc, id_CE); + + replace_port(ff, id_Q, lc, ctx->id("Q" + std::to_string(index))); + if (get_net_or_empty(ff, id_M) != nullptr) { // PRLD FFs that use both M and DI NPNR_ASSERT(!driven_by_lut); // As M is used; must route DI through a new LUT lc->params[ctx->id(reg + "_SD")] = std::string("1"); lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16); - replace_port(ff, ctx->id("DI"), lc, ctx->id("D" + std::to_string(index))); - replace_port(ff, ctx->id("M"), lc, ctx->id("M" + std::to_string(index))); + replace_port(ff, id_DI, lc, ctx->id("D" + std::to_string(index))); + replace_port(ff, id_M, lc, ctx->id("M" + std::to_string(index))); connect_ports(ctx, lc, ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index))); } else { if (driven_by_lut) { - replace_port(ff, ctx->id("DI"), lc, ctx->id("DI" + std::to_string(index))); + replace_port(ff, id_DI, lc, ctx->id("DI" + std::to_string(index))); } else { - replace_port(ff, ctx->id("DI"), lc, ctx->id("M" + std::to_string(index))); + replace_port(ff, id_DI, lc, ctx->id("M" + std::to_string(index))); } } } @@ -266,62 +266,62 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) if (lc->hierpath == IdString()) lc->hierpath = lut->hierpath; lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = - get_or_default(lut->params, ctx->id("INIT"), Property(0, 16)); - replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index))); - replace_port(lut, ctx->id("B"), lc, ctx->id("B" + std::to_string(index))); - replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index))); - replace_port(lut, ctx->id("D"), lc, ctx->id("D" + std::to_string(index))); - replace_port(lut, ctx->id("Z"), lc, ctx->id("F" + std::to_string(index))); + get_or_default(lut->params, id_INIT, Property(0, 16)); + replace_port(lut, id_A, lc, ctx->id("A" + std::to_string(index))); + replace_port(lut, id_B, lc, ctx->id("B" + std::to_string(index))); + replace_port(lut, id_C, lc, ctx->id("C" + std::to_string(index))); + replace_port(lut, id_D, lc, ctx->id("D" + std::to_string(index))); + replace_port(lut, id_Z, lc, ctx->id("F" + std::to_string(index))); } void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) { if (lc->hierpath == IdString()) lc->hierpath = ccu->hierpath; - lc->params[ctx->id("MODE")] = std::string("CCU2"); - lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16)); - lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16)); + lc->params[id_MODE] = std::string("CCU2"); + lc->params[id_LUT0_INITVAL] = get_or_default(ccu->params, id_INIT0, Property(0, 16)); + lc->params[id_LUT1_INITVAL] = get_or_default(ccu->params, id_INIT1, Property(0, 16)); - lc->params[ctx->id("CCU2_INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES"); - lc->params[ctx->id("CCU2_INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES"); + lc->params[id_CCU2_INJECT1_0] = str_or_default(ccu->params, id_INJECT1_0, "YES"); + lc->params[id_CCU2_INJECT1_1] = str_or_default(ccu->params, id_INJECT1_1, "YES"); - replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI")); + replace_port(ccu, id_CIN, lc, id_FCI); - replace_port(ccu, ctx->id("A0"), lc, ctx->id("A0")); - replace_port(ccu, ctx->id("B0"), lc, ctx->id("B0")); - replace_port(ccu, ctx->id("C0"), lc, ctx->id("C0")); - replace_port(ccu, ctx->id("D0"), lc, ctx->id("D0")); + replace_port(ccu, id_A0, lc, id_A0); + replace_port(ccu, id_B0, lc, id_B0); + replace_port(ccu, id_C0, lc, id_C0); + replace_port(ccu, id_D0, lc, id_D0); - replace_port(ccu, ctx->id("A1"), lc, ctx->id("A1")); - replace_port(ccu, ctx->id("B1"), lc, ctx->id("B1")); - replace_port(ccu, ctx->id("C1"), lc, ctx->id("C1")); - replace_port(ccu, ctx->id("D1"), lc, ctx->id("D1")); + replace_port(ccu, id_A1, lc, id_A1); + replace_port(ccu, id_B1, lc, id_B1); + replace_port(ccu, id_C1, lc, id_C1); + replace_port(ccu, id_D1, lc, id_D1); - replace_port(ccu, ctx->id("S0"), lc, ctx->id("F0")); - replace_port(ccu, ctx->id("S1"), lc, ctx->id("F1")); + replace_port(ccu, id_S0, lc, id_F0); + replace_port(ccu, id_S1, lc, id_F1); - replace_port(ccu, ctx->id("COUT"), lc, ctx->id("FCO")); + replace_port(ccu, id_COUT, lc, id_FCO); } void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc) { if (lc->hierpath == IdString()) lc->hierpath = ram->hierpath; - lc->params[ctx->id("MODE")] = std::string("RAMW"); - replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0")); - replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0")); - replace_port(ram, ctx->id("WAD[2]"), lc, ctx->id("C0")); - replace_port(ram, ctx->id("WAD[3]"), lc, ctx->id("A0")); - - replace_port(ram, ctx->id("DI[0]"), lc, ctx->id("C1")); - replace_port(ram, ctx->id("DI[1]"), lc, ctx->id("A1")); - replace_port(ram, ctx->id("DI[2]"), lc, ctx->id("D1")); - replace_port(ram, ctx->id("DI[3]"), lc, ctx->id("B1")); + lc->params[id_MODE] = std::string("RAMW"); + replace_port(ram, ctx->id("WAD[0]"), lc, id_D0); + replace_port(ram, ctx->id("WAD[1]"), lc, id_B0); + replace_port(ram, ctx->id("WAD[2]"), lc, id_C0); + replace_port(ram, ctx->id("WAD[3]"), lc, id_A0); + + replace_port(ram, ctx->id("DI[0]"), lc, id_C1); + replace_port(ram, ctx->id("DI[1]"), lc, id_A1); + replace_port(ram, ctx->id("DI[2]"), lc, id_D1); + replace_port(ram, ctx->id("DI[3]"), lc, id_B1); } static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit) { - auto init_prop = get_or_default(ram->params, ctx->id("INITVAL"), Property(0, 64)); + auto init_prop = get_or_default(ram->params, id_INITVAL, Property(0, 64)); NPNR_ASSERT(!init_prop.is_string); const std::string &idata = init_prop.str; NPNR_ASSERT(idata.length() == 64); @@ -340,9 +340,9 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw { if (lc->hierpath == IdString()) lc->hierpath = ram->hierpath; - lc->params[ctx->id("MODE")] = std::string("DPRAM"); - lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE"); - lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK"); + lc->params[id_MODE] = std::string("DPRAM"); + lc->params[id_WREMUX] = str_or_default(ram->params, id_WREMUX, "WRE"); + lc->params[id_WCKMUX] = str_or_default(ram->params, id_WCKMUX, "WCK"); unsigned permuted_init0 = 0, permuted_init1 = 0; unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1); @@ -363,30 +363,30 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw permuted_init1 |= (1 << i); } - lc->params[ctx->id("LUT0_INITVAL")] = Property(permuted_init0, 16); - lc->params[ctx->id("LUT1_INITVAL")] = Property(permuted_init1, 16); + lc->params[id_LUT0_INITVAL] = Property(permuted_init0, 16); + lc->params[id_LUT1_INITVAL] = Property(permuted_init1, 16); if (ram->ports.count(ctx->id("RAD[0]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D0")); - connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D1")); + connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, id_D0); + connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, id_D1); } if (ram->ports.count(ctx->id("RAD[1]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B0")); - connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B1")); + connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, id_B0); + connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, id_B1); } if (ram->ports.count(ctx->id("RAD[2]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C0")); - connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C1")); + connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, id_C0); + connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, id_C1); } if (ram->ports.count(ctx->id("RAD[3]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A0")); - connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A1")); + connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, id_A0); + connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, id_A1); } - if (ram->ports.count(ctx->id("WRE"))) - connect_port(ctx, ram->ports.at(ctx->id("WRE")).net, lc, ctx->id("WRE")); - if (ram->ports.count(ctx->id("WCK"))) - connect_port(ctx, ram->ports.at(ctx->id("WCK")).net, lc, ctx->id("WCK")); + if (ram->ports.count(id_WRE)) + connect_port(ctx, ram->ports.at(id_WRE).net, lc, id_WRE); + if (ram->ports.count(id_WCK)) + connect_port(ctx, ram->ports.at(id_WCK).net, lc, id_WCK); connect_ports(ctx, ramw, id_WADO0, lc, id_WAD0); connect_ports(ctx, ramw, id_WADO1, lc, id_WAD1); @@ -415,26 +415,26 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector &todelete_cells) { if (nxio->type == ctx->id("$nextpnr_ibuf")) { - trio->params[ctx->id("DIR")] = std::string("INPUT"); - replace_port(nxio, ctx->id("O"), trio, ctx->id("O")); + trio->params[id_DIR] = std::string("INPUT"); + replace_port(nxio, id_O, trio, id_O); } else if (nxio->type == ctx->id("$nextpnr_obuf")) { - trio->params[ctx->id("DIR")] = std::string("OUTPUT"); - replace_port(nxio, ctx->id("I"), trio, ctx->id("I")); + trio->params[id_DIR] = std::string("OUTPUT"); + replace_port(nxio, id_I, trio, id_I); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below - NetInfo *i = get_net_or_empty(nxio, ctx->id("I")); + NetInfo *i = get_net_or_empty(nxio, id_I); if (i == nullptr || i->driver.cell == nullptr) - trio->params[ctx->id("DIR")] = std::string("INPUT"); + trio->params[id_DIR] = std::string("INPUT"); else { log_info("%s: %s.%s\n", ctx->nameOf(i), ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); - trio->params[ctx->id("DIR")] = std::string("BIDIR"); + trio->params[id_DIR] = std::string("BIDIR"); } - replace_port(nxio, ctx->id("I"), trio, ctx->id("I")); - replace_port(nxio, ctx->id("O"), trio, ctx->id("O")); + replace_port(nxio, id_I, trio, id_I); + replace_port(nxio, id_O, trio, id_O); } else { NPNR_ASSERT(false); } - NetInfo *donet = trio->ports.at(ctx->id("I")).net, *dinet = trio->ports.at(ctx->id("O")).net; + NetInfo *donet = trio->ports.at(id_I).net, *dinet = trio->ports.at(id_O).net; // Rename I/O nets to avoid conflicts if (donet != nullptr && donet->name == nxio->name) @@ -458,20 +458,20 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectornet_aliases.erase(tn_netname); NetInfo *toplevel_net = ctx->createNet(tn_netname); toplevel_net->name = tn_netname; - connect_port(ctx, toplevel_net, trio, ctx->id("B")); + connect_port(ctx, toplevel_net, trio, id_B); ctx->ports[nxio->name].net = toplevel_net; } CellInfo *tbuf = net_driven_by( ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); }, - ctx->id("Y")); + id_Y); if (tbuf) { - replace_port(tbuf, ctx->id("A"), trio, ctx->id("I")); + replace_port(tbuf, id_A, trio, id_I); // Need to invert E to form T - std::unique_ptr inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T"); - replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A")); - inv_lut->params[ctx->id("INIT")] = Property(21845, 16); - connect_ports(ctx, inv_lut.get(), ctx->id("Z"), trio, ctx->id("T")); + std::unique_ptr inv_lut = create_ecp5_cell(ctx, id_LUT4, trio->name.str(ctx) + "$invert_T"); + replace_port(tbuf, id_E, inv_lut.get(), id_A); + inv_lut->params[id_INIT] = Property(21845, 16); + connect_ports(ctx, inv_lut.get(), id_Z, trio, id_T); created_cells.push_back(std::move(inv_lut)); if (donet->users.size() > 1) { diff --git a/ecp5/cells.h b/ecp5/cells.h index c1165ddcf8..8f0a8cbf70 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -30,37 +30,36 @@ NEXTPNR_NAMESPACE_BEGIN std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::string name = ""); // Return true if a cell is a LUT -inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("LUT4"); } +inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_LUT4; } // Return true if a cell is a flipflop -inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_FF"); } +inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_FF; } -inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("CCU2C"); } +inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; } -inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_SLICE"); } +inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_SLICE; } -inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); } +inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; } -inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_DPR16X4"); } +inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; } -inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("PFUMX"); } +inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_PFUMX; } -inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("L6MUX21"); } +inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_L6MUX21; } inline bool is_iologic_input_cell(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("IDDRX1F") || cell->type == ctx->id("IDDRX2F") || cell->type == ctx->id("IDDR71B") || - cell->type == ctx->id("IDDRX2DQA") || - (cell->type == ctx->id("TRELLIS_FF") && bool_or_default(cell->attrs, ctx->id("syn_useioff")) && - (str_or_default(cell->attrs, ctx->id("ioff_dir"), "") != "output")); + return cell->type == id_IDDRX1F || cell->type == id_IDDRX2F || cell->type == id_IDDR71B || + cell->type == id_IDDRX2DQA || + (cell->type == id_TRELLIS_FF && bool_or_default(cell->attrs, id_syn_useioff) && + (str_or_default(cell->attrs, id_ioff_dir, "") != "output")); } inline bool is_iologic_output_cell(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("ODDRX1F") || cell->type == ctx->id("ODDRX2F") || cell->type == ctx->id("ODDR71B") || - cell->type == ctx->id("ODDRX2DQA") || cell->type == ctx->id("ODDRX2DQSB") || - cell->type == ctx->id("OSHX2A") || - (cell->type == ctx->id("TRELLIS_FF") && bool_or_default(cell->attrs, ctx->id("syn_useioff")) && - (str_or_default(cell->attrs, ctx->id("ioff_dir"), "") != "input")); + return cell->type == id_ODDRX1F || cell->type == id_ODDRX2F || cell->type == id_ODDR71B || + cell->type == id_ODDRX2DQA || cell->type == id_ODDRX2DQSB || cell->type == id_OSHX2A || + (cell->type == id_TRELLIS_FF && bool_or_default(cell->attrs, id_syn_useioff) && + (str_or_default(cell->attrs, id_ioff_dir, "") != "input")); } void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut); diff --git a/ecp5/constids.inc b/ecp5/constids.inc index 335f822a8a..760e162305 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -1341,3 +1341,513 @@ X(IOLOGIC_MODE_TSREG) X(DCSC) X(DCSOUT) X(MODESEL) + +X(ALUT) +X(ASYNC_RESET_RELEASE) +X(BEL) +X(BLUT) +X(C) +X(CCU2C) +X(CCU2_INJECT1_0) +X(CCU2_INJECT1_1) +X(CEAMUX) +X(CEBMUX) +X(CEIMUX) +X(CEMUX) +X(CEOMUX) +X(CER) +X(CEW) +X(CH0_AUTO_CALIB_EN) +X(CH0_AUTO_FACQ_EN) +X(CH0_BAND_THRESHOLD) +X(CH0_CALIB_CK_MODE) +X(CH0_CC_MATCH_1) +X(CH0_CC_MATCH_2) +X(CH0_CC_MATCH_3) +X(CH0_CC_MATCH_4) +X(CH0_CDR_CNT4SEL) +X(CH0_CDR_CNT8SEL) +X(CH0_CTC_BYPASS) +X(CH0_DCOATDCFG) +X(CH0_DCOATDDLY) +X(CH0_DCOBYPSATD) +X(CH0_DCOCALDIV) +X(CH0_DCOCTLGI) +X(CH0_DCODISBDAVOID) +X(CH0_DCOFLTDAC) +X(CH0_DCOFTNRG) +X(CH0_DCOIOSTUNE) +X(CH0_DCOITUNE) +X(CH0_DCOITUNE4LSB) +X(CH0_DCOIUPDNX2) +X(CH0_DCONUOFLSB) +X(CH0_DCOSCALEI) +X(CH0_DCOSTARTVAL) +X(CH0_DCOSTEP) +X(CH0_DEC_BYPASS) +X(CH0_ENABLE_CG_ALIGN) +X(CH0_ENC_BYPASS) +X(CH0_FF_RX_F_CLK_DIS) +X(CH0_FF_RX_H_CLK_EN) +X(CH0_FF_TX_F_CLK_DIS) +X(CH0_FF_TX_H_CLK_EN) +X(CH0_GE_AN_ENABLE) +X(CH0_INVERT_RX) +X(CH0_INVERT_TX) +X(CH0_LDR_CORE2TX_SEL) +X(CH0_LDR_RX2CORE_SEL) +X(CH0_LEQ_OFFSET_SEL) +X(CH0_LEQ_OFFSET_TRIM) +X(CH0_LSM_DISABLE) +X(CH0_MATCH_2_ENABLE) +X(CH0_MATCH_4_ENABLE) +X(CH0_MIN_IPG_CNT) +X(CH0_PCIE_EI_EN) +X(CH0_PCIE_MODE) +X(CH0_PCS_DET_TIME_SEL) +X(CH0_PDEN_SEL) +X(CH0_PRBS_ENABLE) +X(CH0_PRBS_LOCK) +X(CH0_PRBS_SELECTION) +X(CH0_RATE_MODE_RX) +X(CH0_RATE_MODE_TX) +X(CH0_RCV_DCC_EN) +X(CH0_REG_BAND_OFFSET) +X(CH0_REG_BAND_SEL) +X(CH0_REG_IDAC_EN) +X(CH0_REG_IDAC_SEL) +X(CH0_REQ_EN) +X(CH0_REQ_LVL_SET) +X(CH0_RIO_MODE) +X(CH0_RLOS_SEL) +X(CH0_RPWDNB) +X(CH0_RTERM_RX) +X(CH0_RTERM_TX) +X(CH0_RXIN_CM) +X(CH0_RXTERM_CM) +X(CH0_RX_DCO_CK_DIV) +X(CH0_RX_DIV11_SEL) +X(CH0_RX_GEAR_BYPASS) +X(CH0_RX_GEAR_MODE) +X(CH0_RX_LOS_CEQ) +X(CH0_RX_LOS_EN) +X(CH0_RX_LOS_HYST_EN) +X(CH0_RX_LOS_LVL) +X(CH0_RX_RATE_SEL) +X(CH0_RX_SB_BYPASS) +X(CH0_SB_BYPASS) +X(CH0_SEL_SD_RX_CLK) +X(CH0_TDRV_DAT_SEL) +X(CH0_TDRV_POST_EN) +X(CH0_TDRV_PRE_EN) +X(CH0_TDRV_SLICE0_CUR) +X(CH0_TDRV_SLICE0_SEL) +X(CH0_TDRV_SLICE1_CUR) +X(CH0_TDRV_SLICE1_SEL) +X(CH0_TDRV_SLICE2_CUR) +X(CH0_TDRV_SLICE2_SEL) +X(CH0_TDRV_SLICE3_CUR) +X(CH0_TDRV_SLICE3_SEL) +X(CH0_TDRV_SLICE4_CUR) +X(CH0_TDRV_SLICE4_SEL) +X(CH0_TDRV_SLICE5_CUR) +X(CH0_TDRV_SLICE5_SEL) +X(CH0_TPWDNB) +X(CH0_TX_CM_SEL) +X(CH0_TX_DIV11_SEL) +X(CH0_TX_GEAR_BYPASS) +X(CH0_TX_GEAR_MODE) +X(CH0_TX_POST_SIGN) +X(CH0_TX_PRE_SIGN) +X(CH0_UC_MODE) +X(CH0_UDF_COMMA_A) +X(CH0_UDF_COMMA_B) +X(CH0_UDF_COMMA_MASK) +X(CH0_WA_BYPASS) +X(CH0_WA_MODE) +X(CH1_AUTO_CALIB_EN) +X(CH1_AUTO_FACQ_EN) +X(CH1_BAND_THRESHOLD) +X(CH1_CALIB_CK_MODE) +X(CH1_CC_MATCH_1) +X(CH1_CC_MATCH_2) +X(CH1_CC_MATCH_3) +X(CH1_CC_MATCH_4) +X(CH1_CDR_CNT4SEL) +X(CH1_CDR_CNT8SEL) +X(CH1_CTC_BYPASS) +X(CH1_DCOATDCFG) +X(CH1_DCOATDDLY) +X(CH1_DCOBYPSATD) +X(CH1_DCOCALDIV) +X(CH1_DCOCTLGI) +X(CH1_DCODISBDAVOID) +X(CH1_DCOFLTDAC) +X(CH1_DCOFTNRG) +X(CH1_DCOIOSTUNE) +X(CH1_DCOITUNE) +X(CH1_DCOITUNE4LSB) +X(CH1_DCOIUPDNX2) +X(CH1_DCONUOFLSB) +X(CH1_DCOSCALEI) +X(CH1_DCOSTARTVAL) +X(CH1_DCOSTEP) +X(CH1_DEC_BYPASS) +X(CH1_ENABLE_CG_ALIGN) +X(CH1_ENC_BYPASS) +X(CH1_FF_RX_F_CLK_DIS) +X(CH1_FF_RX_H_CLK_EN) +X(CH1_FF_TX_F_CLK_DIS) +X(CH1_FF_TX_H_CLK_EN) +X(CH1_GE_AN_ENABLE) +X(CH1_INVERT_RX) +X(CH1_INVERT_TX) +X(CH1_LDR_CORE2TX_SEL) +X(CH1_LDR_RX2CORE_SEL) +X(CH1_LEQ_OFFSET_SEL) +X(CH1_LEQ_OFFSET_TRIM) +X(CH1_LSM_DISABLE) +X(CH1_MATCH_2_ENABLE) +X(CH1_MATCH_4_ENABLE) +X(CH1_MIN_IPG_CNT) +X(CH1_PCIE_EI_EN) +X(CH1_PCIE_MODE) +X(CH1_PCS_DET_TIME_SEL) +X(CH1_PDEN_SEL) +X(CH1_PRBS_ENABLE) +X(CH1_PRBS_LOCK) +X(CH1_PRBS_SELECTION) +X(CH1_RATE_MODE_RX) +X(CH1_RATE_MODE_TX) +X(CH1_RCV_DCC_EN) +X(CH1_REG_BAND_OFFSET) +X(CH1_REG_BAND_SEL) +X(CH1_REG_IDAC_EN) +X(CH1_REG_IDAC_SEL) +X(CH1_REQ_EN) +X(CH1_REQ_LVL_SET) +X(CH1_RIO_MODE) +X(CH1_RLOS_SEL) +X(CH1_RPWDNB) +X(CH1_RTERM_RX) +X(CH1_RTERM_TX) +X(CH1_RXIN_CM) +X(CH1_RXTERM_CM) +X(CH1_RX_DCO_CK_DIV) +X(CH1_RX_DIV11_SEL) +X(CH1_RX_GEAR_BYPASS) +X(CH1_RX_GEAR_MODE) +X(CH1_RX_LOS_CEQ) +X(CH1_RX_LOS_EN) +X(CH1_RX_LOS_HYST_EN) +X(CH1_RX_LOS_LVL) +X(CH1_RX_RATE_SEL) +X(CH1_RX_SB_BYPASS) +X(CH1_SB_BYPASS) +X(CH1_SEL_SD_RX_CLK) +X(CH1_TDRV_DAT_SEL) +X(CH1_TDRV_POST_EN) +X(CH1_TDRV_PRE_EN) +X(CH1_TDRV_SLICE0_CUR) +X(CH1_TDRV_SLICE0_SEL) +X(CH1_TDRV_SLICE1_CUR) +X(CH1_TDRV_SLICE1_SEL) +X(CH1_TDRV_SLICE2_CUR) +X(CH1_TDRV_SLICE2_SEL) +X(CH1_TDRV_SLICE3_CUR) +X(CH1_TDRV_SLICE3_SEL) +X(CH1_TDRV_SLICE4_CUR) +X(CH1_TDRV_SLICE4_SEL) +X(CH1_TDRV_SLICE5_CUR) +X(CH1_TDRV_SLICE5_SEL) +X(CH1_TPWDNB) +X(CH1_TX_CM_SEL) +X(CH1_TX_DIV11_SEL) +X(CH1_TX_GEAR_BYPASS) +X(CH1_TX_GEAR_MODE) +X(CH1_TX_POST_SIGN) +X(CH1_TX_PRE_SIGN) +X(CH1_UC_MODE) +X(CH1_UDF_COMMA_A) +X(CH1_UDF_COMMA_B) +X(CH1_UDF_COMMA_MASK) +X(CH1_WA_BYPASS) +X(CH1_WA_MODE) +X(CIN) +X(CLAMP) +X(CLK0_DIV) +X(CLK1_DIV) +X(CLK2_DIV) +X(CLK3_DIV) +X(CLKAMUX) +X(CLKBMUX) +X(CLKFB_DIV) +X(CLKIMUX) +X(CLKI_DIV) +X(CLKOMUX) +X(CLKOP_DIV) +X(CLKOP_ENABLE) +X(CLKOP_TRIM_DELAY) +X(CLKOP_TRIM_POL) +X(CLKOS2_DIV) +X(CLKOS2_ENABLE) +X(CLKOS3_DIV) +X(CLKOS3_ENABLE) +X(CLKOS_DIV) +X(CLKOS_ENABLE) +X(CLKOS_TRIM_DELAY) +X(CLKOS_TRIM_POL) +X(CLKR) +X(CLKW) +X(COUT) +X(CSDECODE_A) +X(CSDECODE_B) +X(D) +X(D2) +X(D3) +X(D4) +X(D5) +X(D6) +X(DATAMUX_MDDR) +X(DATAMUX_ODDR) +X(DATAMUX_OREG) +X(DATA_WIDTH_A) +X(DATA_WIDTH_B) +X(DATA_WIDTH_W) +X(DCSMODE) +X(DDRDLLA) +X(DELAYF) +X(DELAYG) +X(DEL_MODE) +X(DEL_VALUE) +X(DIFFRESISTOR) +X(DIR) +X(DIV) +X(DPHASE_SOURCE) +X(DQS_LI_DEL_ADJ) +X(DQS_LI_DEL_VAL) +X(DQS_LO_DEL_ADJ) +X(DQS_LO_DEL_VAL) +X(DRIVE) +X(D_BITCLK_FROM_ND_EN) +X(D_BITCLK_LOCAL_EN) +X(D_BITCLK_ND_EN) +X(D_BUS8BIT_SEL) +X(D_CDR_LOL_SET) +X(D_CMUSETBIASI) +X(D_CMUSETI4CPP) +X(D_CMUSETI4CPZ) +X(D_CMUSETI4VCO) +X(D_CMUSETICP4P) +X(D_CMUSETICP4Z) +X(D_CMUSETINITVCT) +X(D_CMUSETISCL4VCO) +X(D_CMUSETP1GM) +X(D_CMUSETP2AGM) +X(D_CMUSETZGM) +X(D_DCO_CALIB_TIME_SEL) +X(D_HIGH_MARK) +X(D_IB_PWDNB) +X(D_ISETLOS) +X(D_LOW_MARK) +X(D_MACROPDB) +X(D_PD_ISET) +X(D_PLL_LOL_SET) +X(D_REFCK_MODE) +X(D_REQ_ISET) +X(D_RG_EN) +X(D_RG_SET) +X(D_SETICONST_AUX) +X(D_SETICONST_CH) +X(D_SETIRPOLY_AUX) +X(D_SETIRPOLY_CH) +X(D_SETPLLRC) +X(D_SYNC_LOCAL_EN) +X(D_SYNC_ND_EN) +X(D_TXPLL_PWDNB) +X(D_TX_VCO_CK_DIV) +X(D_XGE_MODE) +X(E) +X(ECP5_IS_GLOBAL) +X(ER1) +X(ER2) +X(FEEDBK_PATH) +X(FORCE_MAX_DELAY) +X(FORCE_ZERO_BARREL_SHIFT) +X(FREQ_LOCK_ACCURACY) +X(GND) +X(HYSTERESIS) +X(ICP_CURRENT) +X(IDDR71B) +X(IDDRX1F) +X(IDDRX2DQA) +X(IDDRX2F) +X(INIT) +X(INIT0) +X(INIT1) +X(INITVAL) +X(INJECT1_0) +X(INJECT1_1) +X(INTFB_WAKE) +X(INT_LOCK_STICKY) +X(INV) +X(IOLTOMUX) +X(IO_TYPE) +X(KVCO) +X(L6MUX21) +X(LEGACY) +X(LOC) +X(LPF_CAPACITOR) +X(LPF_RESISTOR) +X(LSRIMUX) +X(LSRMODE) +X(LSROMUX) +X(LUT0_INITVAL) +X(LUT1_INITVAL) +X(LUT4) +X(M) +X(MASK01) +X(MASKPAT) +X(MASKPAT_SOURCE) +X(MCPAT) +X(MCPAT_SOURCE) +X(MFG1_TEST) +X(MFG2_TEST) +X(MFG_ENABLE_FILTEROPAMP) +X(MFG_EN_UP) +X(MFG_FLOAT_ICP) +X(MFG_FORCE_VFILTER) +X(MFG_GMCREF_SEL) +X(MFG_GMC_GAIN) +X(MFG_GMC_PRESET) +X(MFG_GMC_RESET) +X(MFG_GMC_TEST) +X(MFG_ICP_TEST) +X(MFG_LF_PRESET) +X(MFG_LF_RESET) +X(MFG_LF_RESGRND) +X(MODE) +X(MULT_BYPASS) +X(OCEAMUX) +X(OCEBMUX) +X(OCER) +X(ODDR71B) +X(ODDRX1F) +X(ODDRX2DQA) +X(ODDRX2DQSB) +X(ODDRX2F) +X(OPENDRAIN) +X(OSHX2A) +X(OUTDIVIDER_MUXA) +X(OUTDIVIDER_MUXB) +X(OUTDIVIDER_MUXC) +X(OUTDIVIDER_MUXD) +X(PDPW16KD) +X(PFUMX) +X(PLLRST_ENA) +X(PLL_LOCK_MODE) +X(PULLMODE) +X(Q) +X(Q2) +X(Q3) +X(Q4) +X(Q5) +X(Q6) +X(QWL) +X(REFCK_DCBIAS_EN) +X(REFCK_PWDNB) +X(REFCK_RTERM) +X(REFIN_RESET) +X(REG0_LSRMODE) +X(REG0_REGSET) +X(REG0_SD) +X(REG1_LSRMODE) +X(REG1_REGSET) +X(REG1_SD) +X(REGMODE) +X(REGMODE_A) +X(REGMODE_B) +X(REGSET) +X(REG_FLAG_CLK) +X(REG_INPUTA_CE) +X(REG_INPUTA_CLK) +X(REG_INPUTA_RST) +X(REG_INPUTB_CE) +X(REG_INPUTB_CLK) +X(REG_INPUTB_RST) +X(REG_INPUTC0_CLK) +X(REG_INPUTC1_CLK) +X(REG_INPUTC_CLK) +X(REG_OPCODEIN_0_CE) +X(REG_OPCODEIN_0_CLK) +X(REG_OPCODEIN_0_RST) +X(REG_OPCODEIN_1_CE) +X(REG_OPCODEIN_1_CLK) +X(REG_OPCODEIN_1_RST) +X(REG_OPCODEOP0_0_CE) +X(REG_OPCODEOP0_0_CLK) +X(REG_OPCODEOP0_0_RST) +X(REG_OPCODEOP0_1_CE) +X(REG_OPCODEOP0_1_CLK) +X(REG_OPCODEOP0_1_RST) +X(REG_OPCODEOP1_0_CLK) +X(REG_OPCODEOP1_1_CLK) +X(REG_OUTPUT0_CLK) +X(REG_OUTPUT1_CLK) +X(REG_OUTPUT_CLK) +X(REG_OUTPUT_RST) +X(REG_PIPELINE_CE) +X(REG_PIPELINE_CLK) +X(REG_PIPELINE_RST) +X(RESETMODE) +X(RNDPAT) +X(RSTAMUX) +X(RSTBMUX) +X(S0) +X(S1) +X(SD) +X(SGSR) +X(SLEWRATE) +X(SOURCEB_MODE) +X(STDBY_ENABLE) +X(SYNCMODE) +X(SYNC_ENABLE) +X(T0) +X(T1) +X(TERMINATION) +X(TILE_WIRE_ID) +X(TRELLIS_DPR16X4) +X(TRELLIS_FF) +X(TRIMUX_TSREG) +X(TSHX2DQA) +X(TSHX2DQSA) +X(USRMCLKI) +X(USRMCLKO) +X(USRMCLKTS) +X(VCC) +X(WCKMUX) +X(WEAMUX) +X(WEBMUX) +X(WID) +X(WREMUX) +X(WRITEMODE_A) +X(WRITEMODE_B) +X(Y) +X(ioff_dir) +X(lfe5u_12f) +X(lfe5u_25f) +X(lfe5u_45f) +X(lfe5u_85f) +X(lfe5um5g_25f) +X(lfe5um5g_45f) +X(lfe5um5g_85f) +X(lfe5um_25f) +X(lfe5um_45f) +X(lfe5um_85f) +X(noglobal) +X(pack) +X(place) +X(placer) +X(route) +X(router) +X(syn_useioff) diff --git a/ecp5/dcu_bitstream.h b/ecp5/dcu_bitstream.h index 0c461a6bb7..4f0e63e101 100644 --- a/ecp5/dcu_bitstream.h +++ b/ecp5/dcu_bitstream.h @@ -1,504 +1,424 @@ tg.config.add_word("DCU.CH0_AUTO_CALIB_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_AUTO_CALIB_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_AUTO_CALIB_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_AUTO_FACQ_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_AUTO_FACQ_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_AUTO_FACQ_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_BAND_THRESHOLD", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_BAND_THRESHOLD"), Property(0)), 6)); + parse_config_str(get_or_default(ci->params, id_CH0_BAND_THRESHOLD, Property(0)), 6)); tg.config.add_word("DCU.CH0_CALIB_CK_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CALIB_CK_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_CALIB_CK_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH0_CC_MATCH_1", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CC_MATCH_1"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH0_CC_MATCH_1, Property(0)), 10)); tg.config.add_word("DCU.CH0_CC_MATCH_2", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CC_MATCH_2"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH0_CC_MATCH_2, Property(0)), 10)); tg.config.add_word("DCU.CH0_CC_MATCH_3", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CC_MATCH_3"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH0_CC_MATCH_3, Property(0)), 10)); tg.config.add_word("DCU.CH0_CC_MATCH_4", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CC_MATCH_4"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH0_CC_MATCH_4, Property(0)), 10)); tg.config.add_word("DCU.CH0_CDR_CNT4SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CDR_CNT4SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_CDR_CNT4SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_CDR_CNT8SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CDR_CNT8SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_CDR_CNT8SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_CTC_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_CTC_BYPASS"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_DCOATDCFG", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOATDCFG"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_DCOATDDLY", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOATDDLY"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_CTC_BYPASS, Property(0)), 1)); +tg.config.add_word("DCU.CH0_DCOATDCFG", parse_config_str(get_or_default(ci->params, id_CH0_DCOATDCFG, Property(0)), 2)); +tg.config.add_word("DCU.CH0_DCOATDDLY", parse_config_str(get_or_default(ci->params, id_CH0_DCOATDDLY, Property(0)), 2)); tg.config.add_word("DCU.CH0_DCOBYPSATD", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOBYPSATD"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_DCOCALDIV", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOCALDIV"), Property(0)), 3)); -tg.config.add_word("DCU.CH0_DCOCTLGI", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOCTLGI"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_DCOBYPSATD, Property(0)), 1)); +tg.config.add_word("DCU.CH0_DCOCALDIV", parse_config_str(get_or_default(ci->params, id_CH0_DCOCALDIV, Property(0)), 3)); +tg.config.add_word("DCU.CH0_DCOCTLGI", parse_config_str(get_or_default(ci->params, id_CH0_DCOCTLGI, Property(0)), 3)); tg.config.add_word("DCU.CH0_DCODISBDAVOID", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCODISBDAVOID"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_DCOFLTDAC", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOFLTDAC"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_DCOFTNRG", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOFTNRG"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_DCODISBDAVOID, Property(0)), 1)); +tg.config.add_word("DCU.CH0_DCOFLTDAC", parse_config_str(get_or_default(ci->params, id_CH0_DCOFLTDAC, Property(0)), 2)); +tg.config.add_word("DCU.CH0_DCOFTNRG", parse_config_str(get_or_default(ci->params, id_CH0_DCOFTNRG, Property(0)), 3)); tg.config.add_word("DCU.CH0_DCOIOSTUNE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOIOSTUNE"), Property(0)), 3)); -tg.config.add_word("DCU.CH0_DCOITUNE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOITUNE"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_DCOIOSTUNE, Property(0)), 3)); +tg.config.add_word("DCU.CH0_DCOITUNE", parse_config_str(get_or_default(ci->params, id_CH0_DCOITUNE, Property(0)), 2)); tg.config.add_word("DCU.CH0_DCOITUNE4LSB", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOITUNE4LSB"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_DCOITUNE4LSB, Property(0)), 3)); tg.config.add_word("DCU.CH0_DCOIUPDNX2", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOIUPDNX2"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_DCOIUPDNX2, Property(0)), 1)); tg.config.add_word("DCU.CH0_DCONUOFLSB", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCONUOFLSB"), Property(0)), 3)); -tg.config.add_word("DCU.CH0_DCOSCALEI", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOSCALEI"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_DCONUOFLSB, Property(0)), 3)); +tg.config.add_word("DCU.CH0_DCOSCALEI", parse_config_str(get_or_default(ci->params, id_CH0_DCOSCALEI, Property(0)), 2)); tg.config.add_word("DCU.CH0_DCOSTARTVAL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOSTARTVAL"), Property(0)), 3)); -tg.config.add_word("DCU.CH0_DCOSTEP", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DCOSTEP"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_DCOSTARTVAL, Property(0)), 3)); +tg.config.add_word("DCU.CH0_DCOSTEP", parse_config_str(get_or_default(ci->params, id_CH0_DCOSTEP, Property(0)), 2)); tg.config.add_word("DCU.CH0_DEC_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_DEC_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_DEC_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH0_ENABLE_CG_ALIGN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_ENABLE_CG_ALIGN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_ENABLE_CG_ALIGN, Property(0)), 1)); tg.config.add_word("DCU.CH0_ENC_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_ENC_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_ENC_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH0_FF_RX_F_CLK_DIS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_FF_RX_F_CLK_DIS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_FF_RX_F_CLK_DIS, Property(0)), 1)); tg.config.add_word("DCU.CH0_FF_RX_H_CLK_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_FF_RX_H_CLK_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_FF_RX_H_CLK_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_FF_TX_F_CLK_DIS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_FF_TX_F_CLK_DIS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_FF_TX_F_CLK_DIS, Property(0)), 1)); tg.config.add_word("DCU.CH0_FF_TX_H_CLK_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_FF_TX_H_CLK_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_FF_TX_H_CLK_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_GE_AN_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_GE_AN_ENABLE"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_INVERT_RX", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_INVERT_RX"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_INVERT_TX", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_INVERT_TX"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_GE_AN_ENABLE, Property(0)), 1)); +tg.config.add_word("DCU.CH0_INVERT_RX", parse_config_str(get_or_default(ci->params, id_CH0_INVERT_RX, Property(0)), 1)); +tg.config.add_word("DCU.CH0_INVERT_TX", parse_config_str(get_or_default(ci->params, id_CH0_INVERT_TX, Property(0)), 1)); tg.config.add_word("DCU.CH0_LDR_CORE2TX_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_LDR_CORE2TX_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_LDR_CORE2TX_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH0_LDR_RX2CORE_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_LDR_RX2CORE_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_LDR_RX2CORE_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH0_LEQ_OFFSET_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_LEQ_OFFSET_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_LEQ_OFFSET_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH0_LEQ_OFFSET_TRIM", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_LEQ_OFFSET_TRIM"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_LEQ_OFFSET_TRIM, Property(0)), 3)); tg.config.add_word("DCU.CH0_LSM_DISABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_LSM_DISABLE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_LSM_DISABLE, Property(0)), 1)); tg.config.add_word("DCU.CH0_MATCH_2_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_MATCH_2_ENABLE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_MATCH_2_ENABLE, Property(0)), 1)); tg.config.add_word("DCU.CH0_MATCH_4_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_MATCH_4_ENABLE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_MATCH_4_ENABLE, Property(0)), 1)); tg.config.add_word("DCU.CH0_MIN_IPG_CNT", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_MIN_IPG_CNT"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_MIN_IPG_CNT, Property(0)), 2)); tg.config.add_word("DCU.CH0_PCIE_EI_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PCIE_EI_EN"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_PCIE_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PCIE_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_PCIE_EI_EN, Property(0)), 1)); +tg.config.add_word("DCU.CH0_PCIE_MODE", parse_config_str(get_or_default(ci->params, id_CH0_PCIE_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH0_PCS_DET_TIME_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PCS_DET_TIME_SEL"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_PDEN_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PDEN_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_PCS_DET_TIME_SEL, Property(0)), 2)); +tg.config.add_word("DCU.CH0_PDEN_SEL", parse_config_str(get_or_default(ci->params, id_CH0_PDEN_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH0_PRBS_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PRBS_ENABLE"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_PRBS_LOCK", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PRBS_LOCK"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_PRBS_ENABLE, Property(0)), 1)); +tg.config.add_word("DCU.CH0_PRBS_LOCK", parse_config_str(get_or_default(ci->params, id_CH0_PRBS_LOCK, Property(0)), 1)); tg.config.add_word("DCU.CH0_PRBS_SELECTION", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_PRBS_SELECTION"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_PRBS_SELECTION, Property(0)), 1)); tg.config.add_word("DCU.CH0_RATE_MODE_RX", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RATE_MODE_RX"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RATE_MODE_RX, Property(0)), 1)); tg.config.add_word("DCU.CH0_RATE_MODE_TX", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RATE_MODE_TX"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RATE_MODE_TX, Property(0)), 1)); tg.config.add_word("DCU.CH0_RCV_DCC_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RCV_DCC_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RCV_DCC_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_REG_BAND_OFFSET", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_REG_BAND_OFFSET"), Property(0)), 4)); + parse_config_str(get_or_default(ci->params, id_CH0_REG_BAND_OFFSET, Property(0)), 4)); tg.config.add_word("DCU.CH0_REG_BAND_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_REG_BAND_SEL"), Property(0)), 6)); + parse_config_str(get_or_default(ci->params, id_CH0_REG_BAND_SEL, Property(0)), 6)); tg.config.add_word("DCU.CH0_REG_IDAC_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_REG_IDAC_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_REG_IDAC_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_REG_IDAC_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_REG_IDAC_SEL"), Property(0)), 10)); -tg.config.add_word("DCU.CH0_REQ_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_REQ_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_REG_IDAC_SEL, Property(0)), 10)); +tg.config.add_word("DCU.CH0_REQ_EN", parse_config_str(get_or_default(ci->params, id_CH0_REQ_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_REQ_LVL_SET", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_REQ_LVL_SET"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_RIO_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RIO_MODE"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_RLOS_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RLOS_SEL"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_RPWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RPWDNB"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_RTERM_RX", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RTERM_RX"), Property(0)), 5)); -tg.config.add_word("DCU.CH0_RTERM_TX", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RTERM_TX"), Property(0)), 5)); -tg.config.add_word("DCU.CH0_RXIN_CM", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RXIN_CM"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_RXTERM_CM", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RXTERM_CM"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_REQ_LVL_SET, Property(0)), 2)); +tg.config.add_word("DCU.CH0_RIO_MODE", parse_config_str(get_or_default(ci->params, id_CH0_RIO_MODE, Property(0)), 1)); +tg.config.add_word("DCU.CH0_RLOS_SEL", parse_config_str(get_or_default(ci->params, id_CH0_RLOS_SEL, Property(0)), 1)); +tg.config.add_word("DCU.CH0_RPWDNB", parse_config_str(get_or_default(ci->params, id_CH0_RPWDNB, Property(0)), 1)); +tg.config.add_word("DCU.CH0_RTERM_RX", parse_config_str(get_or_default(ci->params, id_CH0_RTERM_RX, Property(0)), 5)); +tg.config.add_word("DCU.CH0_RTERM_TX", parse_config_str(get_or_default(ci->params, id_CH0_RTERM_TX, Property(0)), 5)); +tg.config.add_word("DCU.CH0_RXIN_CM", parse_config_str(get_or_default(ci->params, id_CH0_RXIN_CM, Property(0)), 2)); +tg.config.add_word("DCU.CH0_RXTERM_CM", parse_config_str(get_or_default(ci->params, id_CH0_RXTERM_CM, Property(0)), 2)); tg.config.add_word("DCU.CH0_RX_DCO_CK_DIV", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_DCO_CK_DIV"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_DCO_CK_DIV, Property(0)), 3)); tg.config.add_word("DCU.CH0_RX_DIV11_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_DIV11_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_DIV11_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH0_RX_GEAR_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_GEAR_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_GEAR_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH0_RX_GEAR_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_GEAR_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_GEAR_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH0_RX_LOS_CEQ", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_LOS_CEQ"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_RX_LOS_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_LOS_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_LOS_CEQ, Property(0)), 2)); +tg.config.add_word("DCU.CH0_RX_LOS_EN", parse_config_str(get_or_default(ci->params, id_CH0_RX_LOS_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_RX_LOS_HYST_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_LOS_HYST_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_LOS_HYST_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_RX_LOS_LVL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_LOS_LVL"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_LOS_LVL, Property(0)), 3)); tg.config.add_word("DCU.CH0_RX_RATE_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_RATE_SEL"), Property(0)), 4)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_RATE_SEL, Property(0)), 4)); tg.config.add_word("DCU.CH0_RX_SB_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_RX_SB_BYPASS"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_SB_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_SB_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_RX_SB_BYPASS, Property(0)), 1)); +tg.config.add_word("DCU.CH0_SB_BYPASS", parse_config_str(get_or_default(ci->params, id_CH0_SB_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH0_SEL_SD_RX_CLK", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_SEL_SD_RX_CLK"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_SEL_SD_RX_CLK, Property(0)), 1)); tg.config.add_word("DCU.CH0_TDRV_DAT_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_DAT_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_DAT_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_POST_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_POST_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_POST_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_TDRV_PRE_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_PRE_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_PRE_EN, Property(0)), 1)); tg.config.add_word("DCU.CH0_TDRV_SLICE0_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE0_CUR"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE0_CUR, Property(0)), 3)); tg.config.add_word("DCU.CH0_TDRV_SLICE0_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE0_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE0_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE1_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE1_CUR"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE1_CUR, Property(0)), 3)); tg.config.add_word("DCU.CH0_TDRV_SLICE1_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE1_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE1_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE2_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE2_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE2_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE2_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE2_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE2_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE3_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE3_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE3_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE3_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE3_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE3_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE4_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE4_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE4_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE4_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE4_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE4_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE5_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE5_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE5_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH0_TDRV_SLICE5_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TDRV_SLICE5_SEL"), Property(0)), 2)); -tg.config.add_word("DCU.CH0_TPWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TPWDNB"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_TX_CM_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TX_CM_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH0_TDRV_SLICE5_SEL, Property(0)), 2)); +tg.config.add_word("DCU.CH0_TPWDNB", parse_config_str(get_or_default(ci->params, id_CH0_TPWDNB, Property(0)), 1)); +tg.config.add_word("DCU.CH0_TX_CM_SEL", parse_config_str(get_or_default(ci->params, id_CH0_TX_CM_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH0_TX_DIV11_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TX_DIV11_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TX_DIV11_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH0_TX_GEAR_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TX_GEAR_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TX_GEAR_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH0_TX_GEAR_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TX_GEAR_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TX_GEAR_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH0_TX_POST_SIGN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TX_POST_SIGN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TX_POST_SIGN, Property(0)), 1)); tg.config.add_word("DCU.CH0_TX_PRE_SIGN", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_TX_PRE_SIGN"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_UC_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_UC_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_TX_PRE_SIGN, Property(0)), 1)); +tg.config.add_word("DCU.CH0_UC_MODE", parse_config_str(get_or_default(ci->params, id_CH0_UC_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH0_UDF_COMMA_A", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_UDF_COMMA_A"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH0_UDF_COMMA_A, Property(0)), 10)); tg.config.add_word("DCU.CH0_UDF_COMMA_B", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_UDF_COMMA_B"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH0_UDF_COMMA_B, Property(0)), 10)); tg.config.add_word("DCU.CH0_UDF_COMMA_MASK", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_UDF_COMMA_MASK"), Property(0)), 10)); -tg.config.add_word("DCU.CH0_WA_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_WA_BYPASS"), Property(0)), 1)); -tg.config.add_word("DCU.CH0_WA_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH0_WA_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH0_UDF_COMMA_MASK, Property(0)), 10)); +tg.config.add_word("DCU.CH0_WA_BYPASS", parse_config_str(get_or_default(ci->params, id_CH0_WA_BYPASS, Property(0)), 1)); +tg.config.add_word("DCU.CH0_WA_MODE", parse_config_str(get_or_default(ci->params, id_CH0_WA_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH1_AUTO_CALIB_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_AUTO_CALIB_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_AUTO_CALIB_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_AUTO_FACQ_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_AUTO_FACQ_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_AUTO_FACQ_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_BAND_THRESHOLD", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_BAND_THRESHOLD"), Property(0)), 6)); + parse_config_str(get_or_default(ci->params, id_CH1_BAND_THRESHOLD, Property(0)), 6)); tg.config.add_word("DCU.CH1_CALIB_CK_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CALIB_CK_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_CALIB_CK_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH1_CC_MATCH_1", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CC_MATCH_1"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH1_CC_MATCH_1, Property(0)), 10)); tg.config.add_word("DCU.CH1_CC_MATCH_2", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CC_MATCH_2"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH1_CC_MATCH_2, Property(0)), 10)); tg.config.add_word("DCU.CH1_CC_MATCH_3", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CC_MATCH_3"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH1_CC_MATCH_3, Property(0)), 10)); tg.config.add_word("DCU.CH1_CC_MATCH_4", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CC_MATCH_4"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH1_CC_MATCH_4, Property(0)), 10)); tg.config.add_word("DCU.CH1_CDR_CNT4SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CDR_CNT4SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_CDR_CNT4SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_CDR_CNT8SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CDR_CNT8SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_CDR_CNT8SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_CTC_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_CTC_BYPASS"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_DCOATDCFG", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOATDCFG"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_DCOATDDLY", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOATDDLY"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_CTC_BYPASS, Property(0)), 1)); +tg.config.add_word("DCU.CH1_DCOATDCFG", parse_config_str(get_or_default(ci->params, id_CH1_DCOATDCFG, Property(0)), 2)); +tg.config.add_word("DCU.CH1_DCOATDDLY", parse_config_str(get_or_default(ci->params, id_CH1_DCOATDDLY, Property(0)), 2)); tg.config.add_word("DCU.CH1_DCOBYPSATD", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOBYPSATD"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_DCOCALDIV", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOCALDIV"), Property(0)), 3)); -tg.config.add_word("DCU.CH1_DCOCTLGI", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOCTLGI"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_DCOBYPSATD, Property(0)), 1)); +tg.config.add_word("DCU.CH1_DCOCALDIV", parse_config_str(get_or_default(ci->params, id_CH1_DCOCALDIV, Property(0)), 3)); +tg.config.add_word("DCU.CH1_DCOCTLGI", parse_config_str(get_or_default(ci->params, id_CH1_DCOCTLGI, Property(0)), 3)); tg.config.add_word("DCU.CH1_DCODISBDAVOID", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCODISBDAVOID"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_DCOFLTDAC", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOFLTDAC"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_DCOFTNRG", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOFTNRG"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_DCODISBDAVOID, Property(0)), 1)); +tg.config.add_word("DCU.CH1_DCOFLTDAC", parse_config_str(get_or_default(ci->params, id_CH1_DCOFLTDAC, Property(0)), 2)); +tg.config.add_word("DCU.CH1_DCOFTNRG", parse_config_str(get_or_default(ci->params, id_CH1_DCOFTNRG, Property(0)), 3)); tg.config.add_word("DCU.CH1_DCOIOSTUNE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOIOSTUNE"), Property(0)), 3)); -tg.config.add_word("DCU.CH1_DCOITUNE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOITUNE"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_DCOIOSTUNE, Property(0)), 3)); +tg.config.add_word("DCU.CH1_DCOITUNE", parse_config_str(get_or_default(ci->params, id_CH1_DCOITUNE, Property(0)), 2)); tg.config.add_word("DCU.CH1_DCOITUNE4LSB", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOITUNE4LSB"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_DCOITUNE4LSB, Property(0)), 3)); tg.config.add_word("DCU.CH1_DCOIUPDNX2", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOIUPDNX2"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_DCOIUPDNX2, Property(0)), 1)); tg.config.add_word("DCU.CH1_DCONUOFLSB", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCONUOFLSB"), Property(0)), 3)); -tg.config.add_word("DCU.CH1_DCOSCALEI", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOSCALEI"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_DCONUOFLSB, Property(0)), 3)); +tg.config.add_word("DCU.CH1_DCOSCALEI", parse_config_str(get_or_default(ci->params, id_CH1_DCOSCALEI, Property(0)), 2)); tg.config.add_word("DCU.CH1_DCOSTARTVAL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOSTARTVAL"), Property(0)), 3)); -tg.config.add_word("DCU.CH1_DCOSTEP", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DCOSTEP"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_DCOSTARTVAL, Property(0)), 3)); +tg.config.add_word("DCU.CH1_DCOSTEP", parse_config_str(get_or_default(ci->params, id_CH1_DCOSTEP, Property(0)), 2)); tg.config.add_word("DCU.CH1_DEC_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_DEC_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_DEC_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH1_ENABLE_CG_ALIGN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_ENABLE_CG_ALIGN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_ENABLE_CG_ALIGN, Property(0)), 1)); tg.config.add_word("DCU.CH1_ENC_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_ENC_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_ENC_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH1_FF_RX_F_CLK_DIS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_FF_RX_F_CLK_DIS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_FF_RX_F_CLK_DIS, Property(0)), 1)); tg.config.add_word("DCU.CH1_FF_RX_H_CLK_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_FF_RX_H_CLK_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_FF_RX_H_CLK_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_FF_TX_F_CLK_DIS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_FF_TX_F_CLK_DIS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_FF_TX_F_CLK_DIS, Property(0)), 1)); tg.config.add_word("DCU.CH1_FF_TX_H_CLK_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_FF_TX_H_CLK_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_FF_TX_H_CLK_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_GE_AN_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_GE_AN_ENABLE"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_INVERT_RX", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_INVERT_RX"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_INVERT_TX", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_INVERT_TX"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_GE_AN_ENABLE, Property(0)), 1)); +tg.config.add_word("DCU.CH1_INVERT_RX", parse_config_str(get_or_default(ci->params, id_CH1_INVERT_RX, Property(0)), 1)); +tg.config.add_word("DCU.CH1_INVERT_TX", parse_config_str(get_or_default(ci->params, id_CH1_INVERT_TX, Property(0)), 1)); tg.config.add_word("DCU.CH1_LDR_CORE2TX_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_LDR_CORE2TX_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_LDR_CORE2TX_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH1_LDR_RX2CORE_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_LDR_RX2CORE_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_LDR_RX2CORE_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH1_LEQ_OFFSET_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_LEQ_OFFSET_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_LEQ_OFFSET_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH1_LEQ_OFFSET_TRIM", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_LEQ_OFFSET_TRIM"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_LEQ_OFFSET_TRIM, Property(0)), 3)); tg.config.add_word("DCU.CH1_LSM_DISABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_LSM_DISABLE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_LSM_DISABLE, Property(0)), 1)); tg.config.add_word("DCU.CH1_MATCH_2_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_MATCH_2_ENABLE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_MATCH_2_ENABLE, Property(0)), 1)); tg.config.add_word("DCU.CH1_MATCH_4_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_MATCH_4_ENABLE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_MATCH_4_ENABLE, Property(0)), 1)); tg.config.add_word("DCU.CH1_MIN_IPG_CNT", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_MIN_IPG_CNT"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_MIN_IPG_CNT, Property(0)), 2)); tg.config.add_word("DCU.CH1_PCIE_EI_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PCIE_EI_EN"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_PCIE_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PCIE_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_PCIE_EI_EN, Property(0)), 1)); +tg.config.add_word("DCU.CH1_PCIE_MODE", parse_config_str(get_or_default(ci->params, id_CH1_PCIE_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH1_PCS_DET_TIME_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PCS_DET_TIME_SEL"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_PDEN_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PDEN_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_PCS_DET_TIME_SEL, Property(0)), 2)); +tg.config.add_word("DCU.CH1_PDEN_SEL", parse_config_str(get_or_default(ci->params, id_CH1_PDEN_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH1_PRBS_ENABLE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PRBS_ENABLE"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_PRBS_LOCK", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PRBS_LOCK"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_PRBS_ENABLE, Property(0)), 1)); +tg.config.add_word("DCU.CH1_PRBS_LOCK", parse_config_str(get_or_default(ci->params, id_CH1_PRBS_LOCK, Property(0)), 1)); tg.config.add_word("DCU.CH1_PRBS_SELECTION", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_PRBS_SELECTION"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_PRBS_SELECTION, Property(0)), 1)); tg.config.add_word("DCU.CH1_RATE_MODE_RX", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RATE_MODE_RX"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RATE_MODE_RX, Property(0)), 1)); tg.config.add_word("DCU.CH1_RATE_MODE_TX", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RATE_MODE_TX"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RATE_MODE_TX, Property(0)), 1)); tg.config.add_word("DCU.CH1_RCV_DCC_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RCV_DCC_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RCV_DCC_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_REG_BAND_OFFSET", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_REG_BAND_OFFSET"), Property(0)), 4)); + parse_config_str(get_or_default(ci->params, id_CH1_REG_BAND_OFFSET, Property(0)), 4)); tg.config.add_word("DCU.CH1_REG_BAND_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_REG_BAND_SEL"), Property(0)), 6)); + parse_config_str(get_or_default(ci->params, id_CH1_REG_BAND_SEL, Property(0)), 6)); tg.config.add_word("DCU.CH1_REG_IDAC_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_REG_IDAC_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_REG_IDAC_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_REG_IDAC_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_REG_IDAC_SEL"), Property(0)), 10)); -tg.config.add_word("DCU.CH1_REQ_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_REQ_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_REG_IDAC_SEL, Property(0)), 10)); +tg.config.add_word("DCU.CH1_REQ_EN", parse_config_str(get_or_default(ci->params, id_CH1_REQ_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_REQ_LVL_SET", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_REQ_LVL_SET"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_RIO_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RIO_MODE"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_RLOS_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RLOS_SEL"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_RPWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RPWDNB"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_RTERM_RX", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RTERM_RX"), Property(0)), 5)); -tg.config.add_word("DCU.CH1_RTERM_TX", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RTERM_TX"), Property(0)), 5)); -tg.config.add_word("DCU.CH1_RXIN_CM", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RXIN_CM"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_RXTERM_CM", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RXTERM_CM"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_REQ_LVL_SET, Property(0)), 2)); +tg.config.add_word("DCU.CH1_RIO_MODE", parse_config_str(get_or_default(ci->params, id_CH1_RIO_MODE, Property(0)), 1)); +tg.config.add_word("DCU.CH1_RLOS_SEL", parse_config_str(get_or_default(ci->params, id_CH1_RLOS_SEL, Property(0)), 1)); +tg.config.add_word("DCU.CH1_RPWDNB", parse_config_str(get_or_default(ci->params, id_CH1_RPWDNB, Property(0)), 1)); +tg.config.add_word("DCU.CH1_RTERM_RX", parse_config_str(get_or_default(ci->params, id_CH1_RTERM_RX, Property(0)), 5)); +tg.config.add_word("DCU.CH1_RTERM_TX", parse_config_str(get_or_default(ci->params, id_CH1_RTERM_TX, Property(0)), 5)); +tg.config.add_word("DCU.CH1_RXIN_CM", parse_config_str(get_or_default(ci->params, id_CH1_RXIN_CM, Property(0)), 2)); +tg.config.add_word("DCU.CH1_RXTERM_CM", parse_config_str(get_or_default(ci->params, id_CH1_RXTERM_CM, Property(0)), 2)); tg.config.add_word("DCU.CH1_RX_DCO_CK_DIV", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_DCO_CK_DIV"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_DCO_CK_DIV, Property(0)), 3)); tg.config.add_word("DCU.CH1_RX_DIV11_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_DIV11_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_DIV11_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH1_RX_GEAR_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_GEAR_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_GEAR_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH1_RX_GEAR_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_GEAR_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_GEAR_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH1_RX_LOS_CEQ", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_LOS_CEQ"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_RX_LOS_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_LOS_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_LOS_CEQ, Property(0)), 2)); +tg.config.add_word("DCU.CH1_RX_LOS_EN", parse_config_str(get_or_default(ci->params, id_CH1_RX_LOS_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_RX_LOS_HYST_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_LOS_HYST_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_LOS_HYST_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_RX_LOS_LVL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_LOS_LVL"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_LOS_LVL, Property(0)), 3)); tg.config.add_word("DCU.CH1_RX_RATE_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_RATE_SEL"), Property(0)), 4)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_RATE_SEL, Property(0)), 4)); tg.config.add_word("DCU.CH1_RX_SB_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_RX_SB_BYPASS"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_SB_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_SB_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_RX_SB_BYPASS, Property(0)), 1)); +tg.config.add_word("DCU.CH1_SB_BYPASS", parse_config_str(get_or_default(ci->params, id_CH1_SB_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH1_SEL_SD_RX_CLK", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_SEL_SD_RX_CLK"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_SEL_SD_RX_CLK, Property(0)), 1)); tg.config.add_word("DCU.CH1_TDRV_DAT_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_DAT_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_DAT_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_POST_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_POST_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_POST_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_TDRV_PRE_EN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_PRE_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_PRE_EN, Property(0)), 1)); tg.config.add_word("DCU.CH1_TDRV_SLICE0_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE0_CUR"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE0_CUR, Property(0)), 3)); tg.config.add_word("DCU.CH1_TDRV_SLICE0_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE0_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE0_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE1_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE1_CUR"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE1_CUR, Property(0)), 3)); tg.config.add_word("DCU.CH1_TDRV_SLICE1_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE1_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE1_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE2_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE2_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE2_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE2_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE2_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE2_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE3_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE3_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE3_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE3_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE3_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE3_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE4_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE4_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE4_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE4_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE4_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE4_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE5_CUR", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE5_CUR"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE5_CUR, Property(0)), 2)); tg.config.add_word("DCU.CH1_TDRV_SLICE5_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TDRV_SLICE5_SEL"), Property(0)), 2)); -tg.config.add_word("DCU.CH1_TPWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TPWDNB"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_TX_CM_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TX_CM_SEL"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_CH1_TDRV_SLICE5_SEL, Property(0)), 2)); +tg.config.add_word("DCU.CH1_TPWDNB", parse_config_str(get_or_default(ci->params, id_CH1_TPWDNB, Property(0)), 1)); +tg.config.add_word("DCU.CH1_TX_CM_SEL", parse_config_str(get_or_default(ci->params, id_CH1_TX_CM_SEL, Property(0)), 2)); tg.config.add_word("DCU.CH1_TX_DIV11_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TX_DIV11_SEL"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TX_DIV11_SEL, Property(0)), 1)); tg.config.add_word("DCU.CH1_TX_GEAR_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TX_GEAR_BYPASS"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TX_GEAR_BYPASS, Property(0)), 1)); tg.config.add_word("DCU.CH1_TX_GEAR_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TX_GEAR_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TX_GEAR_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH1_TX_POST_SIGN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TX_POST_SIGN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TX_POST_SIGN, Property(0)), 1)); tg.config.add_word("DCU.CH1_TX_PRE_SIGN", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_TX_PRE_SIGN"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_UC_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_UC_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_TX_PRE_SIGN, Property(0)), 1)); +tg.config.add_word("DCU.CH1_UC_MODE", parse_config_str(get_or_default(ci->params, id_CH1_UC_MODE, Property(0)), 1)); tg.config.add_word("DCU.CH1_UDF_COMMA_A", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_UDF_COMMA_A"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH1_UDF_COMMA_A, Property(0)), 10)); tg.config.add_word("DCU.CH1_UDF_COMMA_B", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_UDF_COMMA_B"), Property(0)), 10)); + parse_config_str(get_or_default(ci->params, id_CH1_UDF_COMMA_B, Property(0)), 10)); tg.config.add_word("DCU.CH1_UDF_COMMA_MASK", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_UDF_COMMA_MASK"), Property(0)), 10)); -tg.config.add_word("DCU.CH1_WA_BYPASS", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_WA_BYPASS"), Property(0)), 1)); -tg.config.add_word("DCU.CH1_WA_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("CH1_WA_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_CH1_UDF_COMMA_MASK, Property(0)), 10)); +tg.config.add_word("DCU.CH1_WA_BYPASS", parse_config_str(get_or_default(ci->params, id_CH1_WA_BYPASS, Property(0)), 1)); +tg.config.add_word("DCU.CH1_WA_MODE", parse_config_str(get_or_default(ci->params, id_CH1_WA_MODE, Property(0)), 1)); tg.config.add_word("DCU.D_BITCLK_FROM_ND_EN", - parse_config_str(get_or_default(ci->params, ctx->id("D_BITCLK_FROM_ND_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_D_BITCLK_FROM_ND_EN, Property(0)), 1)); tg.config.add_word("DCU.D_BITCLK_LOCAL_EN", - parse_config_str(get_or_default(ci->params, ctx->id("D_BITCLK_LOCAL_EN"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_D_BITCLK_LOCAL_EN, Property(0)), 1)); tg.config.add_word("DCU.D_BITCLK_ND_EN", - parse_config_str(get_or_default(ci->params, ctx->id("D_BITCLK_ND_EN"), Property(0)), 1)); -tg.config.add_word("DCU.D_BUS8BIT_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("D_BUS8BIT_SEL"), Property(0)), 1)); -tg.config.add_word("DCU.D_CDR_LOL_SET", - parse_config_str(get_or_default(ci->params, ctx->id("D_CDR_LOL_SET"), Property(0)), 2)); -tg.config.add_word("DCU.D_CMUSETBIASI", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETBIASI"), Property(0)), 2)); -tg.config.add_word("DCU.D_CMUSETI4CPP", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETI4CPP"), Property(0)), 4)); -tg.config.add_word("DCU.D_CMUSETI4CPZ", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETI4CPZ"), Property(0)), 4)); -tg.config.add_word("DCU.D_CMUSETI4VCO", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETI4VCO"), Property(0)), 2)); -tg.config.add_word("DCU.D_CMUSETICP4P", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETICP4P"), Property(0)), 2)); -tg.config.add_word("DCU.D_CMUSETICP4Z", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETICP4Z"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_D_BITCLK_ND_EN, Property(0)), 1)); +tg.config.add_word("DCU.D_BUS8BIT_SEL", parse_config_str(get_or_default(ci->params, id_D_BUS8BIT_SEL, Property(0)), 1)); +tg.config.add_word("DCU.D_CDR_LOL_SET", parse_config_str(get_or_default(ci->params, id_D_CDR_LOL_SET, Property(0)), 2)); +tg.config.add_word("DCU.D_CMUSETBIASI", parse_config_str(get_or_default(ci->params, id_D_CMUSETBIASI, Property(0)), 2)); +tg.config.add_word("DCU.D_CMUSETI4CPP", parse_config_str(get_or_default(ci->params, id_D_CMUSETI4CPP, Property(0)), 4)); +tg.config.add_word("DCU.D_CMUSETI4CPZ", parse_config_str(get_or_default(ci->params, id_D_CMUSETI4CPZ, Property(0)), 4)); +tg.config.add_word("DCU.D_CMUSETI4VCO", parse_config_str(get_or_default(ci->params, id_D_CMUSETI4VCO, Property(0)), 2)); +tg.config.add_word("DCU.D_CMUSETICP4P", parse_config_str(get_or_default(ci->params, id_D_CMUSETICP4P, Property(0)), 2)); +tg.config.add_word("DCU.D_CMUSETICP4Z", parse_config_str(get_or_default(ci->params, id_D_CMUSETICP4Z, Property(0)), 3)); tg.config.add_word("DCU.D_CMUSETINITVCT", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETINITVCT"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_D_CMUSETINITVCT, Property(0)), 2)); tg.config.add_word("DCU.D_CMUSETISCL4VCO", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETISCL4VCO"), Property(0)), 3)); -tg.config.add_word("DCU.D_CMUSETP1GM", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETP1GM"), Property(0)), 3)); -tg.config.add_word("DCU.D_CMUSETP2AGM", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETP2AGM"), Property(0)), 3)); -tg.config.add_word("DCU.D_CMUSETZGM", - parse_config_str(get_or_default(ci->params, ctx->id("D_CMUSETZGM"), Property(0)), 3)); + parse_config_str(get_or_default(ci->params, id_D_CMUSETISCL4VCO, Property(0)), 3)); +tg.config.add_word("DCU.D_CMUSETP1GM", parse_config_str(get_or_default(ci->params, id_D_CMUSETP1GM, Property(0)), 3)); +tg.config.add_word("DCU.D_CMUSETP2AGM", parse_config_str(get_or_default(ci->params, id_D_CMUSETP2AGM, Property(0)), 3)); +tg.config.add_word("DCU.D_CMUSETZGM", parse_config_str(get_or_default(ci->params, id_D_CMUSETZGM, Property(0)), 3)); tg.config.add_word("DCU.D_DCO_CALIB_TIME_SEL", - parse_config_str(get_or_default(ci->params, ctx->id("D_DCO_CALIB_TIME_SEL"), Property(0)), 2)); -tg.config.add_word("DCU.D_HIGH_MARK", - parse_config_str(get_or_default(ci->params, ctx->id("D_HIGH_MARK"), Property(0)), 4)); -tg.config.add_word("DCU.D_IB_PWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("D_IB_PWDNB"), Property(0)), 1)); -tg.config.add_word("DCU.D_ISETLOS", parse_config_str(get_or_default(ci->params, ctx->id("D_ISETLOS"), Property(0)), 8)); -tg.config.add_word("DCU.D_LOW_MARK", - parse_config_str(get_or_default(ci->params, ctx->id("D_LOW_MARK"), Property(0)), 4)); -tg.config.add_word("DCU.D_MACROPDB", - parse_config_str(get_or_default(ci->params, ctx->id("D_MACROPDB"), Property(0)), 1)); -tg.config.add_word("DCU.D_PD_ISET", parse_config_str(get_or_default(ci->params, ctx->id("D_PD_ISET"), Property(0)), 2)); -tg.config.add_word("DCU.D_PLL_LOL_SET", - parse_config_str(get_or_default(ci->params, ctx->id("D_PLL_LOL_SET"), Property(0)), 2)); -tg.config.add_word("DCU.D_REFCK_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("D_REFCK_MODE"), Property(0)), 3)); -tg.config.add_word("DCU.D_REQ_ISET", - parse_config_str(get_or_default(ci->params, ctx->id("D_REQ_ISET"), Property(0)), 3)); -tg.config.add_word("DCU.D_RG_EN", parse_config_str(get_or_default(ci->params, ctx->id("D_RG_EN"), Property(0)), 1)); -tg.config.add_word("DCU.D_RG_SET", parse_config_str(get_or_default(ci->params, ctx->id("D_RG_SET"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_D_DCO_CALIB_TIME_SEL, Property(0)), 2)); +tg.config.add_word("DCU.D_HIGH_MARK", parse_config_str(get_or_default(ci->params, id_D_HIGH_MARK, Property(0)), 4)); +tg.config.add_word("DCU.D_IB_PWDNB", parse_config_str(get_or_default(ci->params, id_D_IB_PWDNB, Property(0)), 1)); +tg.config.add_word("DCU.D_ISETLOS", parse_config_str(get_or_default(ci->params, id_D_ISETLOS, Property(0)), 8)); +tg.config.add_word("DCU.D_LOW_MARK", parse_config_str(get_or_default(ci->params, id_D_LOW_MARK, Property(0)), 4)); +tg.config.add_word("DCU.D_MACROPDB", parse_config_str(get_or_default(ci->params, id_D_MACROPDB, Property(0)), 1)); +tg.config.add_word("DCU.D_PD_ISET", parse_config_str(get_or_default(ci->params, id_D_PD_ISET, Property(0)), 2)); +tg.config.add_word("DCU.D_PLL_LOL_SET", parse_config_str(get_or_default(ci->params, id_D_PLL_LOL_SET, Property(0)), 2)); +tg.config.add_word("DCU.D_REFCK_MODE", parse_config_str(get_or_default(ci->params, id_D_REFCK_MODE, Property(0)), 3)); +tg.config.add_word("DCU.D_REQ_ISET", parse_config_str(get_or_default(ci->params, id_D_REQ_ISET, Property(0)), 3)); +tg.config.add_word("DCU.D_RG_EN", parse_config_str(get_or_default(ci->params, id_D_RG_EN, Property(0)), 1)); +tg.config.add_word("DCU.D_RG_SET", parse_config_str(get_or_default(ci->params, id_D_RG_SET, Property(0)), 2)); tg.config.add_word("DCU.D_SETICONST_AUX", - parse_config_str(get_or_default(ci->params, ctx->id("D_SETICONST_AUX"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_D_SETICONST_AUX, Property(0)), 2)); tg.config.add_word("DCU.D_SETICONST_CH", - parse_config_str(get_or_default(ci->params, ctx->id("D_SETICONST_CH"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_D_SETICONST_CH, Property(0)), 2)); tg.config.add_word("DCU.D_SETIRPOLY_AUX", - parse_config_str(get_or_default(ci->params, ctx->id("D_SETIRPOLY_AUX"), Property(0)), 2)); + parse_config_str(get_or_default(ci->params, id_D_SETIRPOLY_AUX, Property(0)), 2)); tg.config.add_word("DCU.D_SETIRPOLY_CH", - parse_config_str(get_or_default(ci->params, ctx->id("D_SETIRPOLY_CH"), Property(0)), 2)); -tg.config.add_word("DCU.D_SETPLLRC", - parse_config_str(get_or_default(ci->params, ctx->id("D_SETPLLRC"), Property(0)), 6)); + parse_config_str(get_or_default(ci->params, id_D_SETIRPOLY_CH, Property(0)), 2)); +tg.config.add_word("DCU.D_SETPLLRC", parse_config_str(get_or_default(ci->params, id_D_SETPLLRC, Property(0)), 6)); tg.config.add_word("DCU.D_SYNC_LOCAL_EN", - parse_config_str(get_or_default(ci->params, ctx->id("D_SYNC_LOCAL_EN"), Property(0)), 1)); -tg.config.add_word("DCU.D_SYNC_ND_EN", - parse_config_str(get_or_default(ci->params, ctx->id("D_SYNC_ND_EN"), Property(0)), 1)); -tg.config.add_word("DCU.D_TXPLL_PWDNB", - parse_config_str(get_or_default(ci->params, ctx->id("D_TXPLL_PWDNB"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_D_SYNC_LOCAL_EN, Property(0)), 1)); +tg.config.add_word("DCU.D_SYNC_ND_EN", parse_config_str(get_or_default(ci->params, id_D_SYNC_ND_EN, Property(0)), 1)); +tg.config.add_word("DCU.D_TXPLL_PWDNB", parse_config_str(get_or_default(ci->params, id_D_TXPLL_PWDNB, Property(0)), 1)); tg.config.add_word("DCU.D_TX_VCO_CK_DIV", - parse_config_str(get_or_default(ci->params, ctx->id("D_TX_VCO_CK_DIV"), Property(0)), 3)); -tg.config.add_word("DCU.D_XGE_MODE", - parse_config_str(get_or_default(ci->params, ctx->id("D_XGE_MODE"), Property(0)), 1)); + parse_config_str(get_or_default(ci->params, id_D_TX_VCO_CK_DIV, Property(0)), 3)); +tg.config.add_word("DCU.D_XGE_MODE", parse_config_str(get_or_default(ci->params, id_D_XGE_MODE, Property(0)), 1)); diff --git a/ecp5/globals.cc b/ecp5/globals.cc index cc2208e00a..844c596b91 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -299,8 +299,8 @@ class Ecp5GlobalRouter dedicated_routing = false; if (drv.cell == nullptr) { return 0; - } else if (drv.cell->attrs.count(ctx->id("BEL"))) { - drv_bel = ctx->getBelByNameStr(drv.cell->attrs.at(ctx->id("BEL")).as_string()); + } else if (drv.cell->attrs.count(id_BEL)) { + drv_bel = ctx->getBelByNameStr(drv.cell->attrs.at(id_BEL).as_string()); } else { // Check if driver is a singleton BelId last_bel; @@ -399,7 +399,7 @@ class Ecp5GlobalRouter { BelId best_bel; WireId best_bel_pclkcib; - bool using_ce = get_net_or_empty(dcc, ctx->id("CE")) != nullptr; + bool using_ce = get_net_or_empty(dcc, id_CE) != nullptr; wirelen_t best_wirelen = 9999999; bool dedicated_routing = false; for (auto bel : ctx->getBels()) { @@ -492,8 +492,8 @@ class Ecp5GlobalRouter dccptr = dcc.get(); ctx->cells[dcc->name] = std::move(dcc); } - glbptr->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1; - if (str_or_default(dccptr->attrs, ctx->id("BEL"), "") == "") + glbptr->attrs[id_ECP5_IS_GLOBAL] = 1; + if (str_or_default(dccptr->attrs, id_BEL, "") == "") place_dcc_dcs(dccptr); return glbptr; } @@ -514,8 +514,8 @@ class Ecp5GlobalRouter log_info("Promoting globals...\n"); auto clocks = get_clocks(); for (auto clock : clocks) { - bool is_noglobal = bool_or_default(clock->attrs, ctx->id("noglobal"), false) || - bool_or_default(clock->attrs, ctx->id("ECP5_IS_GLOBAL"), false); + bool is_noglobal = bool_or_default(clock->attrs, id_noglobal, false) || + bool_or_default(clock->attrs, id_ECP5_IS_GLOBAL, false); if (is_noglobal) continue; log_info(" promoting clock net %s to global network\n", clock->name.c_str(ctx)); diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc index 3fd840cd5f..70ebfe81c3 100644 --- a/ecp5/lpf.cc +++ b/ecp5/lpf.cc @@ -134,7 +134,7 @@ bool Arch::apply_lpf(std::string filename, std::istream &in) if (words.size() > 5) log_error("unexpected input following LOCATE clause (on line %d)\n", lineno); if (fnd_cell != cells.end()) { - fnd_cell->second->attrs[id("LOC")] = strip_quotes(words.at(4)); + fnd_cell->second->attrs[id_LOC] = strip_quotes(words.at(4)); } } else if (verb == "IOBUF") { if (words.size() < 3) diff --git a/ecp5/main.cc b/ecp5/main.cc index 1864548bac..f143d583c7 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -277,7 +277,7 @@ void ECP5CommandHandler::customAfterLoad(Context *ctx) CellInfo *ci = cell.second.get(); if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { - if (!ci->attrs.count(ctx->id("LOC"))) { + if (!ci->attrs.count(id_LOC)) { if (vm.count("lpf-allow-unconstrained")) log_warning("IO '%s' is unconstrained in LPF and will be automatically placed\n", cell.first.c_str(ctx)); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 79644d1331..d49dbdf37b 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -108,11 +108,11 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_lut(ctx, ci) || is_pfumx(ctx, ci) || is_l6mux(ctx, ci)) { - NetInfo *znet = ci->ports.at(ctx->id("Z")).net; + NetInfo *znet = ci->ports.at(id_Z).net; if (znet != nullptr) { - CellInfo *ff = net_only_drives(ctx, znet, is_ff, ctx->id("DI"), false); + CellInfo *ff = net_only_drives(ctx, znet, is_ff, id_DI, false); // Can't combine preload FF with LUT due to conflict on M - if (ff != nullptr && get_net_or_empty(ff, ctx->id("M")) == nullptr) { + if (ff != nullptr && get_net_or_empty(ff, id_M) == nullptr) { lutffPairs[ci->name] = ff->name; fflutPairs[ff->name] = ci->name; } @@ -134,13 +134,13 @@ class Ecp5Packer // Check if a flipflop can be added to a slice bool can_add_ff_to_slice(CellInfo *slice, CellInfo *ff) { - std::string clkmux = str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"); - std::string lsrmux = str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"); + std::string clkmux = str_or_default(ff->params, id_CLKMUX, "CLK"); + std::string lsrmux = str_or_default(ff->params, id_LSRMUX, "LSR"); - bool has_dpram = str_or_default(slice->params, ctx->id("MODE"), "LOGIC") == "DPRAM"; + bool has_dpram = str_or_default(slice->params, id_MODE, "LOGIC") == "DPRAM"; if (has_dpram) { - std::string wckmux = str_or_default(slice->params, ctx->id("WCKMUX"), "WCK"); - std::string wremux = str_or_default(slice->params, ctx->id("WREMUX"), "WRE"); + std::string wckmux = str_or_default(slice->params, id_WCKMUX, "WCK"); + std::string wremux = str_or_default(slice->params, id_WREMUX, "WRE"); if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK")) return false; if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR")) @@ -150,25 +150,22 @@ class Ecp5Packer bool has_ff1 = get_net_or_empty(slice, id_Q1) != nullptr; if (!has_ff0 && !has_ff1) return true; - if (str_or_default(ff->params, ctx->id("GSR"), "DISABLED") != - str_or_default(slice->params, ctx->id("GSR"), "DISABLED")) + if (str_or_default(ff->params, id_GSR, "DISABLED") != str_or_default(slice->params, id_GSR, "DISABLED")) return false; - if (str_or_default(ff->params, ctx->id("SRMODE"), "LSR_OVER_CE") != - str_or_default(slice->params, ctx->id("SRMODE"), "LSR_OVER_CE")) + if (str_or_default(ff->params, id_SRMODE, "LSR_OVER_CE") != + str_or_default(slice->params, id_SRMODE, "LSR_OVER_CE")) return false; - if (str_or_default(ff->params, ctx->id("CEMUX"), "1") != str_or_default(slice->params, ctx->id("CEMUX"), "1")) + if (str_or_default(ff->params, id_CEMUX, "1") != str_or_default(slice->params, id_CEMUX, "1")) return false; - if (str_or_default(ff->params, ctx->id("LSRMUX"), "LSR") != - str_or_default(slice->params, ctx->id("LSRMUX"), "LSR")) + if (str_or_default(ff->params, id_LSRMUX, "LSR") != str_or_default(slice->params, id_LSRMUX, "LSR")) return false; - if (str_or_default(ff->params, ctx->id("CLKMUX"), "CLK") != - str_or_default(slice->params, ctx->id("CLKMUX"), "CLK")) + if (str_or_default(ff->params, id_CLKMUX, "CLK") != str_or_default(slice->params, id_CLKMUX, "CLK")) return false; - if (net_or_nullptr(ff, ctx->id("CLK")) != net_or_nullptr(slice, ctx->id("CLK"))) + if (net_or_nullptr(ff, id_CLK) != net_or_nullptr(slice, id_CLK)) return false; - if (net_or_nullptr(ff, ctx->id("CE")) != net_or_nullptr(slice, ctx->id("CE"))) + if (net_or_nullptr(ff, id_CE) != net_or_nullptr(slice, id_CE)) return false; - if (net_or_nullptr(ff, ctx->id("LSR")) != net_or_nullptr(slice, ctx->id("LSR"))) + if (net_or_nullptr(ff, id_LSR) != net_or_nullptr(slice, id_LSR)) return false; return true; } @@ -185,25 +182,22 @@ class Ecp5Packer // Return whether two FFs can be packed together in the same slice bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1) { - if (str_or_default(ff0->params, ctx->id("GSR"), "DISABLED") != - str_or_default(ff1->params, ctx->id("GSR"), "DISABLED")) + if (str_or_default(ff0->params, id_GSR, "DISABLED") != str_or_default(ff1->params, id_GSR, "DISABLED")) return false; - if (str_or_default(ff0->params, ctx->id("SRMODE"), "LSR_OVER_CE") != - str_or_default(ff1->params, ctx->id("SRMODE"), "LSR_OVER_CE")) + if (str_or_default(ff0->params, id_SRMODE, "LSR_OVER_CE") != + str_or_default(ff1->params, id_SRMODE, "LSR_OVER_CE")) return false; - if (str_or_default(ff0->params, ctx->id("CEMUX"), "1") != str_or_default(ff1->params, ctx->id("CEMUX"), "1")) + if (str_or_default(ff0->params, id_CEMUX, "1") != str_or_default(ff1->params, id_CEMUX, "1")) return false; - if (str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR") != - str_or_default(ff1->params, ctx->id("LSRMUX"), "LSR")) + if (str_or_default(ff0->params, id_LSRMUX, "LSR") != str_or_default(ff1->params, id_LSRMUX, "LSR")) return false; - if (str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK") != - str_or_default(ff1->params, ctx->id("CLKMUX"), "CLK")) + if (str_or_default(ff0->params, id_CLKMUX, "CLK") != str_or_default(ff1->params, id_CLKMUX, "CLK")) return false; - if (net_or_nullptr(ff0, ctx->id("CLK")) != net_or_nullptr(ff1, ctx->id("CLK"))) + if (net_or_nullptr(ff0, id_CLK) != net_or_nullptr(ff1, id_CLK)) return false; - if (net_or_nullptr(ff0, ctx->id("CE")) != net_or_nullptr(ff1, ctx->id("CE"))) + if (net_or_nullptr(ff0, id_CE) != net_or_nullptr(ff1, id_CE)) return false; - if (net_or_nullptr(ff0, ctx->id("LSR")) != net_or_nullptr(ff1, ctx->id("LSR"))) + if (net_or_nullptr(ff0, id_LSR) != net_or_nullptr(ff1, id_LSR)) return false; return true; } @@ -212,18 +206,16 @@ class Ecp5Packer bool can_add_ff_to_tile(const std::vector &tile_ffs, CellInfo *ff0) { for (const auto &existing : tile_ffs) { - if (net_or_nullptr(existing, ctx->id("CLK")) != net_or_nullptr(ff0, ctx->id("CLK"))) + if (net_or_nullptr(existing, id_CLK) != net_or_nullptr(ff0, id_CLK)) return false; - if (net_or_nullptr(existing, ctx->id("LSR")) != net_or_nullptr(ff0, ctx->id("LSR"))) + if (net_or_nullptr(existing, id_LSR) != net_or_nullptr(ff0, id_LSR)) return false; - if (str_or_default(existing->params, ctx->id("CLKMUX"), "CLK") != - str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK")) + if (str_or_default(existing->params, id_CLKMUX, "CLK") != str_or_default(ff0->params, id_CLKMUX, "CLK")) return false; - if (str_or_default(existing->params, ctx->id("LSRMUX"), "LSR") != - str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR")) + if (str_or_default(existing->params, id_LSRMUX, "LSR") != str_or_default(ff0->params, id_LSRMUX, "LSR")) return false; - if (str_or_default(existing->params, ctx->id("SRMODE"), "LSR_OVER_CE") != - str_or_default(ff0->params, ctx->id("SRMODE"), "LSR_OVER_CE")) + if (str_or_default(existing->params, id_SRMODE, "LSR_OVER_CE") != + str_or_default(ff0->params, id_SRMODE, "LSR_OVER_CE")) return false; } return true; @@ -232,14 +224,14 @@ class Ecp5Packer // Return true if a FF can be added to a DPRAM slice bool can_pack_ff_dram(CellInfo *dpram, CellInfo *ff) { - if (get_net_or_empty(ff, ctx->id("M")) != nullptr) + if (get_net_or_empty(ff, id_M) != nullptr) return false; // skip PRLD FFs due to M/DI conflict - std::string wckmux = str_or_default(dpram->params, ctx->id("WCKMUX"), "WCK"); - std::string clkmux = str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"); + std::string wckmux = str_or_default(dpram->params, id_WCKMUX, "WCK"); + std::string clkmux = str_or_default(ff->params, id_CLKMUX, "CLK"); if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK")) return false; - std::string wremux = str_or_default(dpram->params, ctx->id("WREMUX"), "WRE"); - std::string lsrmux = str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"); + std::string wremux = str_or_default(dpram->params, id_WREMUX, "WRE"); + std::string lsrmux = str_or_default(ff->params, id_LSRMUX, "LSR"); if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR")) return false; return true; @@ -264,7 +256,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_lut(ctx, ci) && procdLuts.find(cell.first) == procdLuts.end()) { - NetInfo *znet = ci->ports.at(ctx->id("Z")).net; + NetInfo *znet = ci->ports.at(id_Z).net; std::vector inpnets; if (znet != nullptr) { for (auto user : znet->users) { @@ -284,7 +276,7 @@ class Ecp5Packer } } if (lutffPairs.find(ci->name) != lutffPairs.end()) { - NetInfo *qnet = ctx->cells.at(lutffPairs[ci->name])->ports.at(ctx->id("Q")).net; + NetInfo *qnet = ctx->cells.at(lutffPairs[ci->name])->ports.at(id_Q).net; if (qnet != nullptr) { for (auto user : qnet->users) { if (is_lut(ctx, user.cell) && user.cell != ci && @@ -309,7 +301,7 @@ class Ecp5Packer NetInfo *innet = ci->ports.at(ctx->id(inp)).net; if (innet != nullptr && innet->driver.cell != nullptr) { CellInfo *drv = innet->driver.cell; - if (is_lut(ctx, drv) && drv != ci && innet->driver.port == ctx->id("Z")) { + if (is_lut(ctx, drv) && drv != ci && innet->driver.port == id_Z) { if (procdLuts.find(drv->name) == procdLuts.end()) { if (can_pack_lutff(ci->name, drv->name)) { procdLuts.insert(ci->name); @@ -318,7 +310,7 @@ class Ecp5Packer goto paired_inlut; } } - } else if (is_ff(ctx, drv) && innet->driver.port == ctx->id("Q")) { + } else if (is_ff(ctx, drv) && innet->driver.port == id_Q) { auto fflut = fflutPairs.find(drv->name); if (fflut != fflutPairs.end() && fflut->second != ci->name && procdLuts.find(fflut->second) == procdLuts.end()) { @@ -336,7 +328,7 @@ class Ecp5Packer // Pack LUTs feeding the same CCU2, RAM or DFF into a SLICE if (znet != nullptr && znet->users.size() < 10) { for (auto user : znet->users) { - if (is_lc(ctx, user.cell) || user.cell->type == ctx->id("DP16KD") || is_ff(ctx, user.cell)) { + if (is_lc(ctx, user.cell) || user.cell->type == id_DP16KD || is_ff(ctx, user.cell)) { for (auto port : user.cell->ports) { if (port.second.type != PORT_IN || port.second.net == nullptr || port.second.net == znet) @@ -449,12 +441,12 @@ class Ecp5Packer NetInfo *ionet = nullptr; PortRef tp; if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { - ionet = ci->ports.at(ctx->id("O")).net; - trio = net_only_drives(ctx, ionet, is_trellis_io, ctx->id("B"), true, ci); + ionet = ci->ports.at(id_O).net; + trio = net_only_drives(ctx, ionet, is_trellis_io, id_B, true, ci); } else if (ci->type == ctx->id("$nextpnr_obuf")) { - ionet = ci->ports.at(ctx->id("I")).net; - trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci); + ionet = ci->ports.at(id_I).net; + trio = net_only_drives(ctx, ci->ports.at(id_I).net, is_trellis_io, id_B, true, ci); } if (bool_or_default(ctx->settings, ctx->id("arch.ooc"))) { // No IO buffer insertion in out-of-context mode, just remove the nextpnr buffer @@ -466,18 +458,18 @@ class Ecp5Packer log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), ci->type.c_str(ctx), ci->name.c_str(ctx)); - NetInfo *net = trio->ports.at(ctx->id("B")).net; + NetInfo *net = trio->ports.at(id_B).net; if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) && net->users.size() > 1) || (ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr)) || - (ci->type == ctx->id("$nextpnr_iobuf") && ci->ports.at(ctx->id("I")).net != nullptr && - ci->ports.at(ctx->id("I")).net->driver.cell != nullptr)) + (ci->type == ctx->id("$nextpnr_iobuf") && ci->ports.at(id_I).net != nullptr && + ci->ports.at(id_I).net->driver.cell != nullptr)) log_error("Pin B of %s '%s' connected to more than a single top level IO.\n", trio->type.c_str(ctx), trio->name.c_str(ctx)); if (net != nullptr) { - if (net->clkconstr != nullptr && trio->ports.count(ctx->id("O"))) { - NetInfo *onet = trio->ports.at(ctx->id("O")).net; + if (net->clkconstr != nullptr && trio->ports.count(id_O)) { + NetInfo *onet = trio->ports.at(id_O).net; if (onet != nullptr && !onet->clkconstr) { // Move clock constraint from IO pad to input buffer output std::swap(net->clkconstr, onet->clkconstr); @@ -492,7 +484,7 @@ class Ecp5Packer tp.cell->ports.at(tp.port).net = nullptr; } if (ci->type == ctx->id("$nextpnr_iobuf")) { - NetInfo *net2 = ci->ports.at(ctx->id("I")).net; + NetInfo *net2 = ci->ports.at(id_I).net; if (net2 != nullptr) { ctx->nets.erase(net2->name); } @@ -500,7 +492,7 @@ class Ecp5Packer } else { // Create a TRELLIS_IO buffer std::unique_ptr tr_cell = - create_ecp5_cell(ctx, ctx->id("TRELLIS_IO"), ci->name.str(ctx) + "$tr_io"); + create_ecp5_cell(ctx, id_TRELLIS_IO, ci->name.str(ctx) + "$tr_io"); nxio_to_tr(ctx, ci, tr_cell.get(), new_cells, packed_cells); new_cells.push_back(std::move(tr_cell)); trio = new_cells.back().get(); @@ -512,7 +504,7 @@ class Ecp5Packer for (const auto &attr : ci->attrs) trio->attrs[attr.first] = attr.second; - auto loc_attr = trio->attrs.find(ctx->id("LOC")); + auto loc_attr = trio->attrs.find(id_LOC); if (loc_attr != trio->attrs.end()) { std::string pin = loc_attr->second.as_string(); BelId pinBel = ctx->get_package_pin_bel(pin); @@ -523,7 +515,7 @@ class Ecp5Packer log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx), ctx->nameOfBel(pinBel)); } - trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx); + trio->attrs[id_BEL] = ctx->getBelName(pinBel).str(ctx); } } } @@ -539,35 +531,33 @@ class Ecp5Packer CellInfo *ci = cell.second.get(); if (is_pfumx(ctx, ci)) { std::unique_ptr packed = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); - NetInfo *f0 = ci->ports.at(ctx->id("BLUT")).net; + create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "_SLICE"); + NetInfo *f0 = ci->ports.at(id_BLUT).net; if (f0 == nullptr) log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx)); - NetInfo *f1 = ci->ports.at(ctx->id("ALUT")).net; + NetInfo *f1 = ci->ports.at(id_ALUT).net; if (f1 == nullptr) log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx)); - CellInfo *lut0 = net_driven_by(ctx, f0, is_lut, ctx->id("Z")); - CellInfo *lut1 = net_driven_by(ctx, f1, is_lut, ctx->id("Z")); + CellInfo *lut0 = net_driven_by(ctx, f0, is_lut, id_Z); + CellInfo *lut1 = net_driven_by(ctx, f1, is_lut, id_Z); if (lut0 == nullptr) log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); if (lut1 == nullptr) log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); if (ctx->verbose) log_info(" mux '%s' forms part of a LUT5\n", cell.first.c_str(ctx)); - replace_port(lut0, ctx->id("A"), packed.get(), ctx->id("A0")); - replace_port(lut0, ctx->id("B"), packed.get(), ctx->id("B0")); - replace_port(lut0, ctx->id("C"), packed.get(), ctx->id("C0")); - replace_port(lut0, ctx->id("D"), packed.get(), ctx->id("D0")); - replace_port(lut1, ctx->id("A"), packed.get(), ctx->id("A1")); - replace_port(lut1, ctx->id("B"), packed.get(), ctx->id("B1")); - replace_port(lut1, ctx->id("C"), packed.get(), ctx->id("C1")); - replace_port(lut1, ctx->id("D"), packed.get(), ctx->id("D1")); - replace_port(ci, ctx->id("C0"), packed.get(), ctx->id("M0")); - replace_port(ci, ctx->id("Z"), packed.get(), ctx->id("OFX0")); - packed->params[ctx->id("LUT0_INITVAL")] = - get_or_default(lut0->params, ctx->id("INIT"), Property(0, 16)); - packed->params[ctx->id("LUT1_INITVAL")] = - get_or_default(lut1->params, ctx->id("INIT"), Property(0, 16)); + replace_port(lut0, id_A, packed.get(), id_A0); + replace_port(lut0, id_B, packed.get(), id_B0); + replace_port(lut0, id_C, packed.get(), id_C0); + replace_port(lut0, id_D, packed.get(), id_D0); + replace_port(lut1, id_A, packed.get(), id_A1); + replace_port(lut1, id_B, packed.get(), id_B1); + replace_port(lut1, id_C, packed.get(), id_C1); + replace_port(lut1, id_D, packed.get(), id_D1); + replace_port(ci, id_C0, packed.get(), id_M0); + replace_port(ci, id_Z, packed.get(), id_OFX0); + packed->params[id_LUT0_INITVAL] = get_or_default(lut0->params, id_INIT, Property(0, 16)); + packed->params[id_LUT1_INITVAL] = get_or_default(lut1->params, id_INIT, Property(0, 16)); ctx->nets.erase(f0->name); ctx->nets.erase(f1->name); @@ -595,17 +585,16 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_l6mux(ctx, ci)) { - NetInfo *ofx0_0 = ci->ports.at(ctx->id("D0")).net; + NetInfo *ofx0_0 = ci->ports.at(id_D0).net; if (ofx0_0 == nullptr) log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); - NetInfo *ofx0_1 = ci->ports.at(ctx->id("D1")).net; + NetInfo *ofx0_1 = ci->ports.at(id_D1).net; if (ofx0_1 == nullptr) log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); - CellInfo *slice0 = net_driven_by(ctx, ofx0_0, is_lc, ctx->id("OFX0")); - CellInfo *slice1 = net_driven_by(ctx, ofx0_1, is_lc, ctx->id("OFX0")); + CellInfo *slice0 = net_driven_by(ctx, ofx0_0, is_lc, id_OFX0); + CellInfo *slice1 = net_driven_by(ctx, ofx0_1, is_lc, id_OFX0); if (slice0 == nullptr) { - if (!net_driven_by(ctx, ofx0_0, is_l6mux, ctx->id("Z")) && - !net_driven_by(ctx, ofx0_0, is_lc, ctx->id("OFX1"))) + if (!net_driven_by(ctx, ofx0_0, is_l6mux, id_Z) && !net_driven_by(ctx, ofx0_0, is_lc, id_OFX1)) log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux " "('%s.%s')\n", ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), @@ -613,8 +602,7 @@ class Ecp5Packer continue; } if (slice1 == nullptr) { - if (!net_driven_by(ctx, ofx0_1, is_l6mux, ctx->id("Z")) && - !net_driven_by(ctx, ofx0_1, is_lc, ctx->id("OFX1"))) + if (!net_driven_by(ctx, ofx0_1, is_l6mux, id_Z) && !net_driven_by(ctx, ofx0_1, is_lc, id_OFX1)) log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux " "('%s.%s')\n", ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), @@ -623,10 +611,10 @@ class Ecp5Packer } if (ctx->verbose) log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx)); - replace_port(ci, ctx->id("D0"), slice1, id_FXA); - replace_port(ci, ctx->id("D1"), slice1, id_FXB); - replace_port(ci, ctx->id("SD"), slice1, id_M1); - replace_port(ci, ctx->id("Z"), slice1, id_OFX1); + replace_port(ci, id_D0, slice1, id_FXA); + replace_port(ci, id_D1, slice1, id_FXB); + replace_port(ci, id_SD, slice1, id_M1); + replace_port(ci, id_Z, slice1, id_OFX1); slice0->constr_z = 1; slice0->constr_x = 0; slice0->constr_y = 0; @@ -653,14 +641,14 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_l6mux(ctx, ci)) { - NetInfo *ofx1_0 = ci->ports.at(ctx->id("D0")).net; + NetInfo *ofx1_0 = ci->ports.at(id_D0).net; if (ofx1_0 == nullptr) log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); - NetInfo *ofx1_1 = ci->ports.at(ctx->id("D1")).net; + NetInfo *ofx1_1 = ci->ports.at(id_D1).net; if (ofx1_1 == nullptr) log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); - CellInfo *slice1 = net_driven_by(ctx, ofx1_0, is_lc, ctx->id("OFX1")); - CellInfo *slice3 = net_driven_by(ctx, ofx1_1, is_lc, ctx->id("OFX1")); + CellInfo *slice1 = net_driven_by(ctx, ofx1_0, is_lc, id_OFX1); + CellInfo *slice3 = net_driven_by(ctx, ofx1_1, is_lc, id_OFX1); if (slice1 == nullptr) log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n", ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx), @@ -677,8 +665,8 @@ class Ecp5Packer if (fxa_1 == nullptr) log_error("SLICE '%s' has disconnected port 'FXA'\n", slice3->name.c_str(ctx)); - CellInfo *slice0 = net_driven_by(ctx, fxa_0, is_lc, ctx->id("OFX0")); - CellInfo *slice2 = net_driven_by(ctx, fxa_1, is_lc, ctx->id("OFX0")); + CellInfo *slice0 = net_driven_by(ctx, fxa_0, is_lc, id_OFX0); + CellInfo *slice2 = net_driven_by(ctx, fxa_1, is_lc, id_OFX0); if (slice0 == nullptr) log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n", slice1->name.c_str(ctx), fxa_0->driver.cell->name.c_str(ctx), @@ -688,10 +676,10 @@ class Ecp5Packer slice3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx), fxa_1->driver.port.c_str(ctx)); - replace_port(ci, ctx->id("D0"), slice2, id_FXA); - replace_port(ci, ctx->id("D1"), slice2, id_FXB); - replace_port(ci, ctx->id("SD"), slice2, id_M1); - replace_port(ci, ctx->id("Z"), slice2, id_OFX1); + replace_port(ci, id_D0, slice2, id_FXA); + replace_port(ci, id_D1, slice2, id_FXB); + replace_port(ci, id_SD, slice2, id_M1); + replace_port(ci, id_Z, slice2, id_OFX1); for (auto slice : {slice0, slice1, slice2, slice3}) { slice->constr_children.clear(); @@ -747,12 +735,12 @@ class Ecp5Packer // Create a feed in to the carry chain CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) { - std::unique_ptr feedin = create_ecp5_cell(ctx, ctx->id("CCU2C")); + std::unique_ptr feedin = create_ecp5_cell(ctx, id_CCU2C); - feedin->params[ctx->id("INIT0")] = Property(10, 16); // LUT4 = 0; LUT2 = A - feedin->params[ctx->id("INIT1")] = Property(65535, 16); - feedin->params[ctx->id("INJECT1_0")] = std::string("NO"); - feedin->params[ctx->id("INJECT1_1")] = std::string("YES"); + feedin->params[id_INIT0] = Property(10, 16); // LUT4 = 0; LUT2 = A + feedin->params[id_INIT1] = Property(65535, 16); + feedin->params[id_INJECT1_0] = std::string("NO"); + feedin->params[id_INJECT1_1] = std::string("YES"); carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), [chain_in](const PortRef &user) { @@ -762,7 +750,7 @@ class Ecp5Packer connect_port(ctx, carry, feedin.get(), id_A0); NetInfo *new_carry = ctx->createNet(ctx->id(feedin->name.str(ctx) + "$COUT")); - connect_port(ctx, new_carry, feedin.get(), ctx->id("COUT")); + connect_port(ctx, new_carry, feedin.get(), id_COUT); chain_in.cell->ports[chain_in.port].net = nullptr; connect_port(ctx, new_carry, chain_in.cell, chain_in.port); @@ -775,21 +763,21 @@ class Ecp5Packer // Create a feed out and loop through from the carry chain CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next = boost::optional()) { - std::unique_ptr feedout = create_ecp5_cell(ctx, ctx->id("CCU2C")); + std::unique_ptr feedout = create_ecp5_cell(ctx, id_CCU2C); - feedout->params[ctx->id("INIT0")] = Property(0, 16); - feedout->params[ctx->id("INIT1")] = Property(10, 16); // LUT4 = 0; LUT2 = A - feedout->params[ctx->id("INJECT1_0")] = std::string("NO"); - feedout->params[ctx->id("INJECT1_1")] = std::string("NO"); + feedout->params[id_INIT0] = Property(0, 16); + feedout->params[id_INIT1] = Property(10, 16); // LUT4 = 0; LUT2 = A + feedout->params[id_INJECT1_0] = std::string("NO"); + feedout->params[id_INJECT1_1] = std::string("NO"); PortRef carry_drv = carry->driver; carry->driver.cell = nullptr; - connect_port(ctx, carry, feedout.get(), ctx->id("S0")); + connect_port(ctx, carry, feedout.get(), id_S0); NetInfo *new_cin = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$CIN")); new_cin->driver = carry_drv; carry_drv.cell->ports.at(carry_drv.port).net = new_cin; - connect_port(ctx, new_cin, feedout.get(), ctx->id("CIN")); + connect_port(ctx, new_cin, feedout.get(), id_CIN); if (chain_next) { // Loop back into LUT4_1 for feedthrough @@ -802,7 +790,7 @@ class Ecp5Packer carry->users.end()); NetInfo *new_cout = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$COUT")); - connect_port(ctx, new_cout, feedout.get(), ctx->id("COUT")); + connect_port(ctx, new_cout, feedout.get(), id_COUT); chain_next->cell->ports[chain_next->port].net = nullptr; connect_port(ctx, new_cout, chain_next->cell, chain_next->port); @@ -827,23 +815,23 @@ class Ecp5Packer if (start_of_chain) { chains.emplace_back(); start_of_chain = false; - if (cell->ports.at(ctx->id("CIN")).net) { + if (cell->ports.at(id_CIN).net) { // CIN is not constant and not part of a chain. Must feed in from fabric PortRef inport; inport.cell = cell; - inport.port = ctx->id("CIN"); - CellInfo *feedin = make_carry_feed_in(cell->ports.at(ctx->id("CIN")).net, inport); + inport.port = id_CIN; + CellInfo *feedin = make_carry_feed_in(cell->ports.at(id_CIN).net, inport); chains.back().cells.push_back(feedin); } } chains.back().cells.push_back(cell); bool split_chain = int(chains.back().cells.size()) > max_length; if (split_chain) { - CellInfo *passout = make_carry_feed_out(cell->ports.at(ctx->id("COUT")).net); + CellInfo *passout = make_carry_feed_out(cell->ports.at(id_COUT).net); chains.back().cells.back() = passout; start_of_chain = true; } else { - NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net; + NetInfo *carry_net = cell->ports.at(id_COUT).net; bool at_end = (curr_cell == carryc.cells.end() - 1); if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) { boost::optional nextport; @@ -851,10 +839,10 @@ class Ecp5Packer auto next_cell = *(curr_cell + 1); PortRef nextpr; nextpr.cell = next_cell; - nextpr.port = ctx->id("CIN"); + nextpr.port = id_CIN; nextport = nextpr; } - CellInfo *passout = make_carry_feed_out(cell->ports.at(ctx->id("COUT")).net, nextport); + CellInfo *passout = make_carry_feed_out(cell->ports.at(id_COUT).net, nextport); chains.back().cells.push_back(passout); } ++curr_cell; @@ -871,10 +859,10 @@ class Ecp5Packer auto carry_chains = find_chains( ctx, [](const Context *ctx, const CellInfo *cell) { return is_carry(ctx, cell); }, [](const Context *ctx, const CellInfo *cell) { - return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_carry, ctx->id("COUT")); + return net_driven_by(ctx, cell->ports.at(id_CIN).net, is_carry, id_COUT); }, [](const Context *ctx, const CellInfo *cell) { - return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_carry, ctx->id("CIN"), false); + return net_only_drives(ctx, cell->ports.at(id_COUT).net, is_carry, id_CIN, false); }, 1); std::vector all_chains; @@ -905,14 +893,14 @@ class Ecp5Packer if (cell_count % 4 == 0) tile_ffs.clear(); std::unique_ptr slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), cell->name.str(ctx) + "$CCU2_SLICE"); + create_ecp5_cell(ctx, id_TRELLIS_SLICE, cell->name.str(ctx) + "$CCU2_SLICE"); ccu2c_to_slice(ctx, cell, slice.get()); CellInfo *ff0 = nullptr; - NetInfo *f0net = slice->ports.at(ctx->id("F0")).net; + NetInfo *f0net = slice->ports.at(id_F0).net; if (f0net != nullptr) { - ff0 = net_only_drives(ctx, f0net, is_ff, ctx->id("DI"), false); + ff0 = net_only_drives(ctx, f0net, is_ff, id_DI, false); if (ff0 != nullptr && can_add_ff_to_tile(tile_ffs, ff0)) { ff_packing.push_back(std::make_tuple(ff0, slice.get(), 0)); tile_ffs.push_back(ff0); @@ -921,9 +909,9 @@ class Ecp5Packer } CellInfo *ff1 = nullptr; - NetInfo *f1net = slice->ports.at(ctx->id("F1")).net; + NetInfo *f1net = slice->ports.at(id_F1).net; if (f1net != nullptr) { - ff1 = net_only_drives(ctx, f1net, is_ff, ctx->id("DI"), false); + ff1 = net_only_drives(ctx, f1net, is_ff, id_DI, false); if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) && can_add_ff_to_tile(tile_ffs, ff1)) { ff_packing.push_back(std::make_tuple(ff1, slice.get(), 1)); @@ -969,16 +957,16 @@ class Ecp5Packer // Create RAMW slice std::unique_ptr ramw_slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "$RAMW_SLICE"); + create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "$RAMW_SLICE"); dram_to_ramw(ctx, ci, ramw_slice.get()); // Create actual RAM slices std::unique_ptr ram0_slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "$DPRAM0_SLICE"); + create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "$DPRAM0_SLICE"); dram_to_ram_slice(ctx, ci, ram0_slice.get(), ramw_slice.get(), 0); std::unique_ptr ram1_slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "$DPRAM1_SLICE"); + create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "$DPRAM1_SLICE"); dram_to_ram_slice(ctx, ci, ram1_slice.get(), ramw_slice.get(), 1); // Disconnect ports of original cell after packing @@ -995,9 +983,9 @@ class Ecp5Packer std::vector tile_ffs; for (auto slice : {ram0_slice.get(), ram1_slice.get()}) { CellInfo *ff0 = nullptr; - NetInfo *f0net = slice->ports.at(ctx->id("F0")).net; + NetInfo *f0net = slice->ports.at(id_F0).net; if (f0net != nullptr) { - ff0 = net_only_drives(ctx, f0net, is_ff, ctx->id("DI"), false); + ff0 = net_only_drives(ctx, f0net, is_ff, id_DI, false); if (ff0 != nullptr && can_add_ff_to_tile(tile_ffs, ff0)) { if (can_pack_ff_dram(slice, ff0)) { ff_packing.push_back(std::make_tuple(ff0, slice, 0)); @@ -1008,9 +996,9 @@ class Ecp5Packer } CellInfo *ff1 = nullptr; - NetInfo *f1net = slice->ports.at(ctx->id("F1")).net; + NetInfo *f1net = slice->ports.at(id_F1).net; if (f1net != nullptr) { - ff1 = net_only_drives(ctx, f1net, is_ff, ctx->id("DI"), false); + ff1 = net_only_drives(ctx, f1net, is_ff, id_DI, false); if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) && can_add_ff_to_tile(tile_ffs, ff1)) { if (can_pack_ff_dram(slice, ff1)) { @@ -1060,8 +1048,7 @@ class Ecp5Packer for (auto pair : lutPairs) { CellInfo *lut0 = ctx->cells.at(pair.first).get(); CellInfo *lut1 = ctx->cells.at(pair.second).get(); - std::unique_ptr slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), lut0->name.str(ctx) + "_SLICE"); + std::unique_ptr slice = create_ecp5_cell(ctx, id_TRELLIS_SLICE, lut0->name.str(ctx) + "_SLICE"); lut_to_slice(ctx, lut0, slice.get(), 0); lut_to_slice(ctx, lut1, slice.get(), 1); @@ -1099,8 +1086,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_lut(ctx, ci)) { - std::unique_ptr slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); + std::unique_ptr slice = create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "_SLICE"); lut_to_slice(ctx, ci, slice.get(), 1); auto ff = lutffPairs.find(ci->name); @@ -1173,7 +1159,7 @@ class Ecp5Packer CellInfo *ci = cell.second.get(); if (is_ff(ctx, ci)) { bool pack_dense = used_slices > (dense_pack_mode_thresh * available_slices); - bool requires_m = get_net_or_empty(ci, ctx->id("M")) != nullptr; + bool requires_m = get_net_or_empty(ci, id_M) != nullptr; if (pack_dense && !requires_m) { // If dense packing threshold exceeded; always try and pack the FF into an existing slice // Find a SLICE with space "near" the flipflop in the netlist @@ -1222,8 +1208,7 @@ class Ecp5Packer } } - std::unique_ptr slice = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "_SLICE"); + std::unique_ptr slice = create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "_SLICE"); ff_to_slice(ctx, ci, slice.get(), 0, false); new_cells.push_back(std::move(slice)); ++used_slices; @@ -1252,9 +1237,9 @@ class Ecp5Packer void set_lut_input_constant(CellInfo *cell, IdString input, bool value) { int index = std::string("ABCD").find(input.str(ctx)); - int init = int_or_default(cell->params, ctx->id("INIT")); + int init = int_or_default(cell->params, id_INIT); int new_init = make_init_with_const_input(init, index, value); - cell->params[ctx->id("INIT")] = Property(new_init, 16); + cell->params[id_INIT] = Property(new_init, 16); cell->ports.at(input).net = nullptr; } @@ -1275,8 +1260,7 @@ class Ecp5Packer return true; // disconnected port is high if (cell->ports.at(input).net == nullptr || cell->ports.at(input).net->name == ctx->id("$PACKER_VCC_NET")) return true; // disconnected or tied-high port - if (cell->ports.at(input).net->driver.cell != nullptr && - cell->ports.at(input).net->driver.cell->type == ctx->id("VCC")) + if (cell->ports.at(input).net->driver.cell != nullptr && cell->ports.at(input).net->driver.cell->type == id_VCC) return true; // pre-pack high return false; } @@ -1292,8 +1276,8 @@ class Ecp5Packer log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx)); if (is_lut(ctx, uc)) { set_lut_input_constant(uc, user.port, constval); - } else if (is_ff(ctx, uc) && user.port == ctx->id("CE")) { - uc->params[ctx->id("CEMUX")] = std::string(constval ? "1" : "0"); + } else if (is_ff(ctx, uc) && user.port == id_CE) { + uc->params[id_CEMUX] = std::string(constval ? "1" : "0"); uc->ports[user.port].net = nullptr; } else if (is_carry(ctx, uc)) { if (constval && @@ -1326,9 +1310,9 @@ class Ecp5Packer uc->ports[user.port].net = constnet; constnet->users.push_back(user); } - } else if (is_ff(ctx, uc) && user.port == ctx->id("LSR") && - ((!constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "LSR") || - (constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "INV"))) { + } else if (is_ff(ctx, uc) && user.port == id_LSR && + ((!constval && str_or_default(uc->params, id_LSRMUX, "LSR") == "LSR") || + (constval && str_or_default(uc->params, id_LSRMUX, "LSR") == "INV"))) { uc->ports[user.port].net = nullptr; } else if (uc->type == id_DP16KD) { if (user.port == id_CLKA || user.port == id_CLKB || user.port == id_RSTA || user.port == id_RSTB || @@ -1371,19 +1355,19 @@ class Ecp5Packer { log_info("Packing constants..\n"); - std::unique_ptr gnd_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_GND"); - gnd_cell->params[ctx->id("INIT")] = Property(0, 16); + std::unique_ptr gnd_cell = create_ecp5_cell(ctx, id_LUT4, "$PACKER_GND"); + gnd_cell->params[id_INIT] = Property(0, 16); auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); - gnd_net->driver.port = ctx->id("Z"); - gnd_cell->ports.at(ctx->id("Z")).net = gnd_net.get(); + gnd_net->driver.port = id_Z; + gnd_cell->ports.at(id_Z).net = gnd_net.get(); - std::unique_ptr vcc_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_VCC"); - vcc_cell->params[ctx->id("INIT")] = Property(65535, 16); + std::unique_ptr vcc_cell = create_ecp5_cell(ctx, id_LUT4, "$PACKER_VCC"); + vcc_cell->params[id_INIT] = Property(65535, 16); auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); - vcc_net->driver.port = ctx->id("Z"); - vcc_cell->ports.at(ctx->id("Z")).net = vcc_net.get(); + vcc_net->driver.port = id_Z; + vcc_cell->ports.at(id_Z).net = vcc_net.get(); std::vector dead_nets; @@ -1391,13 +1375,13 @@ class Ecp5Packer for (auto &net : ctx->nets) { NetInfo *ni = net.second.get(); - if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { + if (ni->driver.cell != nullptr && ni->driver.cell->type == id_GND) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, gnd_net.get(), false); gnd_used = true; dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); - } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) { + } else if (ni->driver.cell != nullptr && ni->driver.cell->type == id_VCC) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, vcc_net.get(), true); vcc_used = true; @@ -1450,9 +1434,9 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); // Convert 36-bit PDP RAMs to regular 18-bit DP ones that match the Bel - if (ci->type == ctx->id("PDPW16KD")) { - ci->params[ctx->id("DATA_WIDTH_A")] = 36; // force PDP mode - ci->params.erase(ctx->id("DATA_WIDTH_W")); + if (ci->type == id_PDPW16KD) { + ci->params[id_DATA_WIDTH_A] = 36; // force PDP mode + ci->params.erase(id_DATA_WIDTH_W); rename_bus(ci, "BE", "ADA", 4, 0, 0); rename_bus(ci, "ADW", "ADA", 9, 0, 5); rename_bus(ci, "ADR", "ADB", 14, 0, 0); @@ -1462,23 +1446,23 @@ class Ecp5Packer rename_bus(ci, "DI", "DIB", 18, 18, 0); rename_bus(ci, "DO", "DOA", 18, 18, 0); rename_bus(ci, "DO", "DOB", 18, 0, 0); - rename_port(ctx, ci, ctx->id("CLKW"), ctx->id("CLKA")); - rename_port(ctx, ci, ctx->id("CLKR"), ctx->id("CLKB")); - rename_port(ctx, ci, ctx->id("CEW"), ctx->id("CEA")); - rename_port(ctx, ci, ctx->id("CER"), ctx->id("CEB")); - rename_port(ctx, ci, ctx->id("OCER"), ctx->id("OCEB")); + rename_port(ctx, ci, id_CLKW, id_CLKA); + rename_port(ctx, ci, id_CLKR, id_CLKB); + rename_port(ctx, ci, id_CEW, id_CEA); + rename_port(ctx, ci, id_CER, id_CEB); + rename_port(ctx, ci, id_OCER, id_OCEB); rename_param(ci, "CLKWMUX", "CLKAMUX"); - if (str_or_default(ci->params, ctx->id("CLKAMUX")) == "CLKW") - ci->params[ctx->id("CLKAMUX")] = std::string("CLKA"); - if (str_or_default(ci->params, ctx->id("CLKBMUX")) == "CLKR") - ci->params[ctx->id("CLKBMUX")] = std::string("CLKB"); + if (str_or_default(ci->params, id_CLKAMUX) == "CLKW") + ci->params[id_CLKAMUX] = std::string("CLKA"); + if (str_or_default(ci->params, id_CLKBMUX) == "CLKR") + ci->params[id_CLKBMUX] = std::string("CLKB"); rename_param(ci, "CLKRMUX", "CLKRMUX"); rename_param(ci, "CSDECODE_W", "CSDECODE_A"); rename_param(ci, "CSDECODE_R", "CSDECODE_B"); - std::string outreg = str_or_default(ci->params, ctx->id("REGMODE"), "NOREG"); - ci->params[ctx->id("REGMODE_A")] = outreg; - ci->params[ctx->id("REGMODE_B")] = outreg; - ci->params.erase(ctx->id("REGMODE")); + std::string outreg = str_or_default(ci->params, id_REGMODE, "NOREG"); + ci->params[id_REGMODE_A] = outreg; + ci->params[id_REGMODE_B] = outreg; + ci->params.erase(id_REGMODE); rename_param(ci, "DATA_WIDTH_R", "DATA_WIDTH_B"); if (ci->ports.count(id_RST)) { autocreate_empty_port(ci, id_RSTA); @@ -1525,7 +1509,7 @@ class Ecp5Packer autocreate_empty_port(ci, id_WEB); autocreate_empty_port(ci, id_RSTB); - ci->attrs[ctx->id("WID")] = wid++; + ci->attrs[id_WID] = wid++; } } } @@ -1615,9 +1599,9 @@ class Ecp5Packer // Placement doesn't work if only one or the other of // the ALU and MULTs have a BEL specified. - auto alu_has_bel = alu->attrs.count(ctx->id("BEL")); + auto alu_has_bel = alu->attrs.count(id_BEL); for (auto mult : {mult_a, mult_b}) { - auto mult_has_bel = mult->attrs.count(ctx->id("BEL")); + auto mult_has_bel = mult->attrs.count(id_BEL); if (alu_has_bel && !mult_has_bel) { log_error("ALU54B '%s' has a fixed BEL specified, but connected " "MULT18X18D '%s' does not, specify both or neither.\n", @@ -1632,8 +1616,8 @@ class Ecp5Packer // Cannot have MULT OUTPUT_CLK set when connected to an ALU unless // MULT_BYPASS is also enabled. for (auto mult : {mult_a, mult_b}) { - if (str_or_default(mult->params, ctx->id("REG_OUTPUT_CLK"), "NONE") != "NONE" && - str_or_default(mult->params, ctx->id("MULT_BYPASS"), "DISABLED") != "ENABLED") { + if (str_or_default(mult->params, id_REG_OUTPUT_CLK, "NONE") != "NONE" && + str_or_default(mult->params, id_MULT_BYPASS, "DISABLED") != "ENABLED") { log_error("MULT18X18D '%s' REG_OUTPUT_CLK must be NONE when driving ALU without MULT_BYPASS\n", mult->name.c_str(ctx)); } @@ -1700,27 +1684,27 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_DCUA) { - if (ci->attrs.count(ctx->id("LOC"))) { - std::string loc = ci->attrs.at(ctx->id("LOC")).as_string(); + if (ci->attrs.count(id_LOC)) { + std::string loc = ci->attrs.at(id_LOC).as_string(); if (loc == "DCU0" && (ctx->args.type == ArchArgs::LFE5UM_25F || ctx->args.type == ArchArgs::LFE5UM5G_25F)) - ci->attrs[ctx->id("BEL")] = std::string("X42/Y50/DCU"); + ci->attrs[id_BEL] = std::string("X42/Y50/DCU"); else if (loc == "DCU0" && (ctx->args.type == ArchArgs::LFE5UM_45F || ctx->args.type == ArchArgs::LFE5UM5G_45F)) - ci->attrs[ctx->id("BEL")] = std::string("X42/Y71/DCU"); + ci->attrs[id_BEL] = std::string("X42/Y71/DCU"); else if (loc == "DCU1" && (ctx->args.type == ArchArgs::LFE5UM_45F || ctx->args.type == ArchArgs::LFE5UM5G_45F)) - ci->attrs[ctx->id("BEL")] = std::string("X69/Y71/DCU"); + ci->attrs[id_BEL] = std::string("X69/Y71/DCU"); else if (loc == "DCU0" && (ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_85F)) - ci->attrs[ctx->id("BEL")] = std::string("X46/Y95/DCU"); + ci->attrs[id_BEL] = std::string("X46/Y95/DCU"); else if (loc == "DCU1" && (ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_85F)) - ci->attrs[ctx->id("BEL")] = std::string("X71/Y95/DCU"); + ci->attrs[id_BEL] = std::string("X71/Y95/DCU"); else log_error("no DCU location '%s' in device '%s'\n", loc.c_str(), ctx->getChipName().c_str()); } - if (!ci->attrs.count(ctx->id("BEL"))) + if (!ci->attrs.count(id_BEL)) log_error("DCU must be constrained to a Bel!\n"); // Empty port auto-creation to generate correct tie-downs BelId exemplar_bel; @@ -1741,7 +1725,7 @@ class Ecp5Packer if (net == nullptr || net->driver.cell == nullptr) continue; IdString ct = net->driver.cell->type; - if (ct == ctx->id("GND") || ct == ctx->id("VCC")) { + if (ct == id_GND || ct == id_VCC) { disconnect_port(ctx, ci, ndport); ci->ports.erase(ndport); } @@ -1755,8 +1739,8 @@ class Ecp5Packer CellInfo *dcu = nullptr; std::string loc_bel = std::string("NONE"); std::string dcu_bel = std::string("NONE"); - if (ci->attrs.count(ctx->id("LOC"))) { - std::string loc = ci->attrs.at(ctx->id("LOC")).as_string(); + if (ci->attrs.count(id_LOC)) { + std::string loc = ci->attrs.at(id_LOC).as_string(); if (loc == "EXTREF0" && (ctx->args.type == ArchArgs::LFE5UM_25F || ctx->args.type == ArchArgs::LFE5UM5G_25F)) loc_bel = std::string("X42/Y50/EXTREF"); @@ -1783,9 +1767,9 @@ class Ecp5Packer dcu = user.cell; } if (dcu != nullptr) { - if (!dcu->attrs.count(ctx->id("BEL"))) + if (!dcu->attrs.count(id_BEL)) log_error("DCU must be constrained to a Bel!\n"); - dcu_bel = dcu->attrs.at(ctx->id("BEL")).as_string(); + dcu_bel = dcu->attrs.at(id_BEL).as_string(); NPNR_ASSERT(dcu_bel.substr(dcu_bel.length() - 3) == "DCU"); dcu_bel.replace(dcu_bel.length() - 3, 3, "EXTREF"); } @@ -1793,10 +1777,10 @@ class Ecp5Packer if (dcu_bel == "NONE" && loc_bel == "NONE") { log_error("EXTREFB has neither a LOC or a directly associated DCUA\n"); } else if (dcu_bel == "NONE") { - ci->attrs[ctx->id("BEL")] = loc_bel; + ci->attrs[id_BEL] = loc_bel; dcu_bel = loc_bel; } else if (loc_bel == "NONE") { - ci->attrs[ctx->id("BEL")] = dcu_bel; + ci->attrs[id_BEL] = dcu_bel; } else { log_error("EXTREFB has conflicting LOC '%s' and associated DCUA '%s'\n", loc_bel.c_str(), dcu_bel.c_str()); @@ -1804,17 +1788,17 @@ class Ecp5Packer } else { if (dcu_bel == "NONE") log_error("EXTREFB has no LOC or associated DCUA\n"); - ci->attrs[ctx->id("BEL")] = dcu_bel; + ci->attrs[id_BEL] = dcu_bel; } } else if (ci->type == id_PCSCLKDIV) { const NetInfo *clki = net_or_nullptr(ci, id_CLKI); if (clki != nullptr && clki->driver.cell != nullptr && clki->driver.cell->type == id_DCUA) { CellInfo *dcu = clki->driver.cell; - if (!dcu->attrs.count(ctx->id("BEL"))) + if (!dcu->attrs.count(id_BEL)) log_error("DCU must be constrained to a Bel!\n"); - BelId bel = ctx->getBelByNameStr(dcu->attrs.at(ctx->id("BEL")).as_string()); + BelId bel = ctx->getBelByNameStr(dcu->attrs.at(id_BEL).as_string()); if (bel == BelId()) - log_error("Invalid DCU bel '%s'\n", dcu->attrs.at(ctx->id("BEL")).c_str()); + log_error("Invalid DCU bel '%s'\n", dcu->attrs.at(id_BEL).c_str()); Loc loc = ctx->getBelLocation(bel); // DCU0 -> CLKDIV z=0; DCU1 -> CLKDIV z=1 ci->constr_abs_z = true; @@ -1830,18 +1814,17 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_USRMCLK) { - rename_port(ctx, ci, ctx->id("USRMCLKI"), id_PADDO); - rename_port(ctx, ci, ctx->id("USRMCLKTS"), id_PADDT); - rename_port(ctx, ci, ctx->id("USRMCLKO"), id_PADDI); - } else if (ci->type == id_GSR || ci->type == ctx->id("SGSR")) { - ci->params[ctx->id("MODE")] = std::string("ACTIVE_LOW"); - ci->params[ctx->id("SYNCMODE")] = - ci->type == ctx->id("SGSR") ? std::string("SYNC") : std::string("ASYNC"); + rename_port(ctx, ci, id_USRMCLKI, id_PADDO); + rename_port(ctx, ci, id_USRMCLKTS, id_PADDT); + rename_port(ctx, ci, id_USRMCLKO, id_PADDI); + } else if (ci->type == id_GSR || ci->type == id_SGSR) { + ci->params[id_MODE] = std::string("ACTIVE_LOW"); + ci->params[id_SYNCMODE] = ci->type == id_SGSR ? std::string("SYNC") : std::string("ASYNC"); ci->type = id_GSR; for (BelId bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_GSR) continue; - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); ctx->gsrclk_wire = ctx->getBelPinWire(bel, id_CLK); } } @@ -1858,20 +1841,20 @@ class Ecp5Packer } for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_EHXPLLL && ci->attrs.count(ctx->id("BEL"))) - available_plls.erase(ctx->getBelByNameStr(ci->attrs.at(ctx->id("BEL")).as_string())); + if (ci->type == id_EHXPLLL && ci->attrs.count(id_BEL)) + available_plls.erase(ctx->getBelByNameStr(ci->attrs.at(id_BEL).as_string())); } // Place PLL connected to fixed drivers such as IO close to their source for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_EHXPLLL && !ci->attrs.count(ctx->id("BEL"))) { + if (ci->type == id_EHXPLLL && !ci->attrs.count(id_BEL)) { const NetInfo *drivernet = net_or_nullptr(ci, id_CLKI); if (drivernet == nullptr || drivernet->driver.cell == nullptr) continue; const CellInfo *drivercell = drivernet->driver.cell; - if (!drivercell->attrs.count(ctx->id("BEL"))) + if (!drivercell->attrs.count(id_BEL)) continue; - BelId drvbel = ctx->getBelByNameStr(drivercell->attrs.at(ctx->id("BEL")).as_string()); + BelId drvbel = ctx->getBelByNameStr(drivercell->attrs.at(id_BEL).as_string()); Loc drvloc = ctx->getBelLocation(drvbel); BelId closest_pll; int closest_distance = std::numeric_limits::max(); @@ -1886,18 +1869,18 @@ class Ecp5Packer if (closest_pll == BelId()) log_error("failed to place PLL '%s'\n", ci->name.c_str(ctx)); available_plls.erase(closest_pll); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(closest_pll).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(closest_pll).str(ctx); } } // Place PLLs driven by logic, etc, randomly for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_EHXPLLL && !ci->attrs.count(ctx->id("BEL"))) { + if (ci->type == id_EHXPLLL && !ci->attrs.count(id_BEL)) { if (available_plls.empty()) log_error("failed to place PLL '%s'\n", ci->name.c_str(ctx)); BelId next_pll = *(available_plls.begin()); available_plls.erase(next_pll); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(next_pll).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(next_pll).str(ctx); } } } @@ -1907,7 +1890,7 @@ class Ecp5Packer { if (a->driver.cell == nullptr || b->driver.cell == nullptr) return (a->driver.cell == nullptr && b->driver.cell == nullptr); - if (a->driver.cell->type != ctx->id("GND") && a->driver.cell->type != ctx->id("VCC")) + if (a->driver.cell->type != id_GND && a->driver.cell->type != id_VCC) return false; return a->driver.cell->type == b->driver.cell->type; } @@ -1955,7 +1938,7 @@ class Ecp5Packer std::to_string(free_eclk)); NetInfo *promoted_ecknet = ctx->createNet(eckname); - promoted_ecknet->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1; // Prevents router etc touching this special net + promoted_ecknet->attrs[id_ECP5_IS_GLOBAL] = 1; // Prevents router etc touching this special net eclk.buf = promoted_ecknet; // Insert TRELLIS_ECLKBUF to isolate edge clock from general routing @@ -1974,7 +1957,7 @@ class Ecp5Packer } NPNR_ASSERT(target_bel != BelId()); - eclkbuf->attrs[ctx->id("BEL")] = ctx->getBelName(target_bel).str(ctx); + eclkbuf->attrs[id_BEL] = ctx->getBelName(target_bel).str(ctx); connect_port(ctx, ecknet, eclkbuf.get(), id_ECLKI); connect_port(ctx, eclk.buf, eclkbuf.get(), id_ECLKO); @@ -2038,10 +2021,10 @@ class Ecp5Packer } IdString name = ctx->id(ci->name.str(ctx) + "$zero$" + port.str(ctx)); - auto zero_cell = std::make_unique(ctx, name, ctx->id("GND")); + auto zero_cell = std::make_unique(ctx, name, id_GND); NetInfo *zero_net = ctx->createNet(name); - zero_cell->addOutput(ctx->id("GND")); - connect_port(ctx, zero_net, zero_cell.get(), ctx->id("GND")); + zero_cell->addOutput(id_GND); + connect_port(ctx, zero_net, zero_cell.get(), id_GND); connect_port(ctx, zero_net, ci, port); new_cells.push_back(std::move(zero_cell)); } @@ -2053,15 +2036,15 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_DQSBUFM) { - CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("DQSI")).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(ctx->id("DQSI")).net->users.size() > 1) + CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_DQSI).net, is_trellis_io, id_O); + if (pio == nullptr || ci->ports.at(id_DQSI).net->users.size() > 1) log_error("DQSBUFM '%s' DQSI input must be connected only to a top level input\n", ci->name.c_str(ctx)); - if (!pio->attrs.count(ctx->id("BEL"))) + if (!pio->attrs.count(id_BEL)) log_error("DQSBUFM can only be used with a pin-constrained PIO connected to its DQSI input" "(while processing '%s').\n", ci->name.c_str(ctx)); - BelId pio_bel = ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string()); + BelId pio_bel = ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string()); NPNR_ASSERT(pio_bel != BelId()); Loc pio_loc = ctx->getBelLocation(pio_bel); if (pio_loc.z != 0) @@ -2072,7 +2055,7 @@ class Ecp5Packer if (dqsbuf == BelId() || ctx->getBelType(dqsbuf) != id_DQSBUFM) log_error("PIO '%s' does not appear to be a DQS site (didn't find a DQSBUFM).\n", ctx->nameOfBel(pio_bel)); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(dqsbuf).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(dqsbuf).str(ctx); bool got_dqsg = ctx->get_pio_dqs_group(pio_bel, dqsbuf_dqsg[ci->name].first, dqsbuf_dqsg[ci->name].second); NPNR_ASSERT(got_dqsg); @@ -2088,15 +2071,14 @@ class Ecp5Packer if (pn == nullptr) continue; for (auto &usr : pn->users) { - if (usr.port != port || - (usr.cell->type != ctx->id("ODDRX2DQA") && usr.cell->type != ctx->id("ODDRX2DQSB") && - usr.cell->type != ctx->id("TSHX2DQSA") && usr.cell->type != ctx->id("IDDRX2DQA") && - usr.cell->type != ctx->id("TSHX2DQA") && usr.cell->type != id_IOLOGIC)) + if (usr.port != port || (usr.cell->type != id_ODDRX2DQA && usr.cell->type != id_ODDRX2DQSB && + usr.cell->type != id_TSHX2DQSA && usr.cell->type != id_IDDRX2DQA && + usr.cell->type != id_TSHX2DQA && usr.cell->type != id_IOLOGIC)) log_error("Port '%s' of DQSBUFM '%s' cannot drive port '%s' of cell '%s'.\n", port.c_str(ctx), ci->name.c_str(ctx), usr.port.c_str(ctx), usr.cell->name.c_str(ctx)); } - pn->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1; + pn->attrs[id_ECP5_IS_GLOBAL] = 1; } for (auto zport : @@ -2146,9 +2128,9 @@ class Ecp5Packer if (prim->ports.count(port)) sclk = prim->ports[port].net; if (sclk == nullptr) { - iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = std::string("0"); + iol->params[input ? id_CLKIMUX : id_CLKOMUX] = std::string("0"); } else { - iol->params[input ? ctx->id("CLKIMUX") : ctx->id("CLKOMUX")] = std::string("CLK"); + iol->params[input ? id_CLKIMUX : id_CLKOMUX] = std::string("CLK"); if (iol->ports[id_CLK].net != nullptr) { if (iol->ports[id_CLK].net != sclk && !equal_constant(iol->ports[id_CLK].net, sclk)) log_error("IOLOGIC '%s' has conflicting clocks '%s' and '%s'\n", iol->name.c_str(ctx), @@ -2184,9 +2166,9 @@ class Ecp5Packer if (prim->ports.count(port)) lsr = prim->ports[port].net; if (lsr == nullptr) { - iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = std::string("0"); + iol->params[input ? id_LSRIMUX : id_LSROMUX] = std::string("0"); } else { - iol->params[input ? ctx->id("LSRIMUX") : ctx->id("LSROMUX")] = std::string("LSRMUX"); + iol->params[input ? id_LSRIMUX : id_LSROMUX] = std::string("LSRMUX"); if (iol->ports[id_LSR].net != nullptr && !equal_constant(iol->ports[id_LSR].net, lsr)) { if (iol->ports[id_LSR].net != lsr) log_error("IOLOGIC '%s' has conflicting LSR signals '%s' and '%s'\n", iol->name.c_str(ctx), @@ -2202,7 +2184,7 @@ class Ecp5Packer bool warned_oddrx_iddrx = false; auto set_iologic_mode = [&](CellInfo *iol, std::string mode) { - auto &curr_mode = iol->params[ctx->id("MODE")].str; + auto &curr_mode = iol->params[id_MODE].str; if (curr_mode != "NONE" && mode == "IREG_OREG") return; if ((curr_mode == "IDDRXN" && mode == "ODDRXN") || (curr_mode == "ODDRXN" && mode == "IDDRXN")) { @@ -2223,11 +2205,11 @@ class Ecp5Packer }; auto get_pio_bel = [&](CellInfo *pio, CellInfo *curr) { - if (!pio->attrs.count(ctx->id("BEL"))) + if (!pio->attrs.count(id_BEL)) log_error("IOLOGIC functionality (DDR, DELAY, DQS, etc) can only be used with pin-constrained PIO " "(while processing '%s').\n", curr->name.c_str(ctx)); - BelId bel = ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string()); + BelId bel = ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string()); NPNR_ASSERT(bel != BelId()); return bel; }; @@ -2243,7 +2225,7 @@ class Ecp5Packer create_ecp5_cell(ctx, s ? id_SIOLOGIC : id_IOLOGIC, pio->name.str(ctx) + "$IOL"); loc.z += s ? 2 : 4; - iol->attrs[ctx->id("BEL")] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx); + iol->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx); CellInfo *iol_ptr = iol.get(); pio_iologic[pio->name] = iol_ptr; @@ -2287,22 +2269,21 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == ctx->id("DELAYF") || ci->type == ctx->id("DELAYG")) { - CellInfo *i_pio = net_driven_by(ctx, ci->ports.at(ctx->id("A")).net, is_trellis_io, id_O); - CellInfo *o_pio = net_only_drives(ctx, ci->ports.at(ctx->id("Z")).net, is_trellis_io, id_I, true); + if (ci->type == id_DELAYF || ci->type == id_DELAYG) { + CellInfo *i_pio = net_driven_by(ctx, ci->ports.at(id_A).net, is_trellis_io, id_O); + CellInfo *o_pio = net_only_drives(ctx, ci->ports.at(id_Z).net, is_trellis_io, id_I, true); CellInfo *iol = nullptr; - if (i_pio != nullptr && ci->ports.at(ctx->id("A")).net->users.size() == 1) { + if (i_pio != nullptr && ci->ports.at(id_A).net->users.size() == 1) { iol = create_pio_iologic(i_pio, ci); set_iologic_mode(iol, "IREG_OREG"); bool drives_iologic = false; - for (auto user : ci->ports.at(ctx->id("Z")).net->users) + for (auto user : ci->ports.at(id_Z).net->users) if (is_iologic_input_cell(ctx, user.cell) && - (user.port == ctx->id("D") || - (user.cell->type == ctx->id("TRELLIS_FF") && user.port == ctx->id("DI")))) + (user.port == id_D || (user.cell->type == id_TRELLIS_FF && user.port == id_DI))) drives_iologic = true; if (drives_iologic) { // Reconnect to PIO which the packer expects later on - NetInfo *input_net = ci->ports.at(ctx->id("A")).net, *dly_net = ci->ports.at(ctx->id("Z")).net; + NetInfo *input_net = ci->ports.at(id_A).net, *dly_net = ci->ports.at(id_Z).net; disconnect_port(ctx, i_pio, id_O); i_pio->ports.at(id_O).net = nullptr; disconnect_port(ctx, ci, id_A); @@ -2321,9 +2302,9 @@ class Ecp5Packer iol = create_pio_iologic(o_pio, ci); iol->params[ctx->id("DELAY.OUTDEL")] = std::string("ENABLED"); bool driven_by_iol = false; - NetInfo *input_net = ci->ports.at(ctx->id("A")).net, *dly_net = ci->ports.at(ctx->id("Z")).net; + NetInfo *input_net = ci->ports.at(id_A).net, *dly_net = ci->ports.at(id_Z).net; if (input_net->driver.cell != nullptr && is_iologic_output_cell(ctx, input_net->driver.cell) && - input_net->driver.port == ctx->id("Q")) + input_net->driver.port == id_Q) driven_by_iol = true; if (driven_by_iol) { disconnect_port(ctx, o_pio, id_I); @@ -2335,8 +2316,8 @@ class Ecp5Packer connect_port(ctx, input_net, o_pio, id_I); ctx->nets.erase(dly_net->name); } else { - replace_port(ci, ctx->id("A"), iol, id_TXDATA0); - replace_port(ci, ctx->id("Z"), iol, id_IOLDO); + replace_port(ci, id_A, iol, id_TXDATA0); + replace_port(ci, id_Z, iol, id_IOLDO); if (!o_pio->ports.count(id_IOLDO)) { o_pio->ports[id_IOLDO].name = id_IOLDO; o_pio->ports[id_IOLDO].type = PORT_IN; @@ -2349,11 +2330,11 @@ class Ecp5Packer ci->name.c_str(ctx)); } iol->params[ctx->id("DELAY.DEL_VALUE")] = - lookup_delay(str_or_default(ci->params, ctx->id("DEL_MODE"), "USER_DEFINED")); - if (ci->params.count(ctx->id("DEL_VALUE")) && - (!ci->params.at(ctx->id("DEL_VALUE")).is_string || - std::string(ci->params.at(ctx->id("DEL_VALUE")).as_string()).substr(0, 5) != "DELAY")) - iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(ctx->id("DEL_VALUE")); + lookup_delay(str_or_default(ci->params, id_DEL_MODE, "USER_DEFINED")); + if (ci->params.count(id_DEL_VALUE) && + (!ci->params.at(id_DEL_VALUE).is_string || + std::string(ci->params.at(id_DEL_VALUE).as_string()).substr(0, 5) != "DELAY")) + iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(id_DEL_VALUE); if (ci->ports.count(id_LOADN)) replace_port(ci, id_LOADN, iol, id_LOADN); else @@ -2373,9 +2354,9 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == ctx->id("IDDRX1F")) { - CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("D")).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(ctx->id("D")).net->users.size() > 1) + if (ci->type == id_IDDRX1F) { + CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); + if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1) log_error("IDDRX1F '%s' D input must be connected only to a top level input\n", ci->name.c_str(ctx)); CellInfo *iol; @@ -2384,15 +2365,15 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IDDRX1_ODDRX1"); - replace_port(ci, ctx->id("D"), iol, id_PADDI); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), true); - set_iologic_lsr(iol, ci, ctx->id("RST"), true); - replace_port(ci, ctx->id("Q0"), iol, id_RXDATA0); - replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + replace_port(ci, id_D, iol, id_PADDI); + set_iologic_sclk(iol, ci, id_SCLK, true); + set_iologic_lsr(iol, ci, id_RST, true); + replace_port(ci, id_Q0, iol, id_RXDATA0); + replace_port(ci, id_Q1, iol, id_RXDATA1); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("ODDRX1F")) { - CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true); + } else if (ci->type == id_ODDRX1F) { + CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio == nullptr) log_error("ODDRX1F '%s' Q output must be connected only to a top level output\n", ci->name.c_str(ctx)); @@ -2402,21 +2383,21 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IDDRX1_ODDRX1"); - replace_port(ci, ctx->id("Q"), iol, id_IOLDO); + replace_port(ci, id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } replace_port(pio, id_I, pio, id_IOLDO); - pio->params[ctx->id("DATAMUX_ODDR")] = std::string("IOLDO"); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), false); - set_iologic_lsr(iol, ci, ctx->id("RST"), false); - replace_port(ci, ctx->id("D0"), iol, id_TXDATA0); - replace_port(ci, ctx->id("D1"), iol, id_TXDATA1); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + pio->params[id_DATAMUX_ODDR] = std::string("IOLDO"); + set_iologic_sclk(iol, ci, id_SCLK, false); + set_iologic_lsr(iol, ci, id_RST, false); + replace_port(ci, id_D0, iol, id_TXDATA0); + replace_port(ci, id_D1, iol, id_TXDATA1); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("ODDRX2F") || ci->type == ctx->id("ODDR71B")) { - CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true); + } else if (ci->type == id_ODDRX2F || ci->type == id_ODDR71B) { + CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio == nullptr) log_error("%s '%s' Q output must be connected only to a top level output\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); @@ -2426,38 +2407,38 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "ODDRXN"); - replace_port(ci, ctx->id("Q"), iol, id_IOLDO); + replace_port(ci, id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } replace_port(pio, id_I, pio, id_IOLDO); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), false, false); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), true); + set_iologic_sclk(iol, ci, id_SCLK, false, false); + set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_eclk(iol, ci, id_ECLK); - set_iologic_lsr(iol, ci, ctx->id("RST"), false, false); - set_iologic_lsr(iol, ci, ctx->id("RST"), true); - replace_port(ci, ctx->id("D0"), iol, id_TXDATA0); - replace_port(ci, ctx->id("D1"), iol, id_TXDATA1); - replace_port(ci, ctx->id("D2"), iol, id_TXDATA2); - replace_port(ci, ctx->id("D3"), iol, id_TXDATA3); - if (ci->type == ctx->id("ODDR71B")) { - Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string())); + set_iologic_lsr(iol, ci, id_RST, false, false); + set_iologic_lsr(iol, ci, id_RST, true); + replace_port(ci, id_D0, iol, id_TXDATA0); + replace_port(ci, id_D1, iol, id_TXDATA1); + replace_port(ci, id_D2, iol, id_TXDATA2); + replace_port(ci, id_D3, iol, id_TXDATA3); + if (ci->type == id_ODDR71B) { + Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string())); if (loc.z % 2 == 1) log_error("ODDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx)); - replace_port(ci, ctx->id("D4"), iol, id_TXDATA4); - replace_port(ci, ctx->id("D5"), iol, id_TXDATA5); - replace_port(ci, ctx->id("D6"), iol, id_TXDATA6); + replace_port(ci, id_D4, iol, id_TXDATA4); + replace_port(ci, id_D5, iol, id_TXDATA5); + replace_port(ci, id_D6, iol, id_TXDATA6); iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDR71"); } else { iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDRX2"); } - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); - pio->params[ctx->id("DATAMUX_ODDR")] = std::string("IOLDO"); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); + pio->params[id_DATAMUX_ODDR] = std::string("IOLDO"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("IDDRX2F") || ci->type == ctx->id("IDDR71B")) { - CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("D")).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(ctx->id("D")).net->users.size() > 1) + } else if (ci->type == id_IDDRX2F || ci->type == id_IDDR71B) { + CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); + if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1) log_error("%s '%s' D input must be connected only to a top level input\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); CellInfo *iol; @@ -2466,30 +2447,30 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IDDRXN"); - replace_port(ci, ctx->id("D"), iol, id_PADDI); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), true); + replace_port(ci, id_D, iol, id_PADDI); + set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_eclk(iol, ci, id_ECLK); - set_iologic_lsr(iol, ci, ctx->id("RST"), true); - replace_port(ci, ctx->id("Q0"), iol, id_RXDATA0); - replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1); - replace_port(ci, ctx->id("Q2"), iol, id_RXDATA2); - replace_port(ci, ctx->id("Q3"), iol, id_RXDATA3); - if (ci->type == ctx->id("IDDR71B")) { - Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string())); + set_iologic_lsr(iol, ci, id_RST, true); + replace_port(ci, id_Q0, iol, id_RXDATA0); + replace_port(ci, id_Q1, iol, id_RXDATA1); + replace_port(ci, id_Q2, iol, id_RXDATA2); + replace_port(ci, id_Q3, iol, id_RXDATA3); + if (ci->type == id_IDDR71B) { + Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string())); if (loc.z % 2 == 1) log_error("IDDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx)); - replace_port(ci, ctx->id("Q4"), iol, id_RXDATA4); - replace_port(ci, ctx->id("Q5"), iol, id_RXDATA5); - replace_port(ci, ctx->id("Q6"), iol, id_RXDATA6); - replace_port(ci, ctx->id("ALIGNWD"), iol, id_SLIP); + replace_port(ci, id_Q4, iol, id_RXDATA4); + replace_port(ci, id_Q5, iol, id_RXDATA5); + replace_port(ci, id_Q6, iol, id_RXDATA6); + replace_port(ci, id_ALIGNWD, iol, id_SLIP); iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDR71"); } else { iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDRX2"); } - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("OSHX2A")) { - CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true); + } else if (ci->type == id_OSHX2A) { + CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio == nullptr) log_error("OSHX2A '%s' Q output must be connected only to a top level output\n", ci->name.c_str(ctx)); @@ -2499,24 +2480,24 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, ctx->id("Q"), iol, id_IOLDO); + replace_port(ci, id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } replace_port(pio, id_I, pio, id_IOLDO); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), false); + set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_eclk(iol, ci, id_ECLK); - set_iologic_lsr(iol, ci, ctx->id("RST"), false, false); - set_iologic_lsr(iol, ci, ctx->id("RST"), true); - replace_port(ci, ctx->id("D0"), iol, id_TXDATA0); - replace_port(ci, ctx->id("D1"), iol, id_TXDATA2); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + set_iologic_lsr(iol, ci, id_RST, false, false); + set_iologic_lsr(iol, ci, id_RST, true); + replace_port(ci, id_D0, iol, id_TXDATA0); + replace_port(ci, id_D1, iol, id_TXDATA2); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MODDRX.MODE")] = std::string("MOSHX2"); - pio->params[ctx->id("DATAMUX_MDDR")] = std::string("IOLDO"); + pio->params[id_DATAMUX_MDDR] = std::string("IOLDO"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("ODDRX2DQA") || ci->type == ctx->id("ODDRX2DQSB")) { - CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true); + } else if (ci->type == id_ODDRX2DQA || ci->type == id_ODDRX2DQSB) { + CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio == nullptr) log_error("%s '%s' Q output must be connected only to a top level output\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); @@ -2526,30 +2507,30 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, ctx->id("Q"), iol, id_IOLDO); + replace_port(ci, id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } replace_port(pio, id_I, pio, id_IOLDO); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), false); + set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_eclk(iol, ci, id_ECLK); - set_iologic_lsr(iol, ci, ctx->id("RST"), false, false); - set_iologic_lsr(iol, ci, ctx->id("RST"), true); - replace_port(ci, ctx->id("D0"), iol, id_TXDATA0); - replace_port(ci, ctx->id("D1"), iol, id_TXDATA1); - replace_port(ci, ctx->id("D2"), iol, id_TXDATA2); - replace_port(ci, ctx->id("D3"), iol, id_TXDATA3); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + set_iologic_lsr(iol, ci, id_RST, false, false); + set_iologic_lsr(iol, ci, id_RST, true); + replace_port(ci, id_D0, iol, id_TXDATA0); + replace_port(ci, id_D1, iol, id_TXDATA1); + replace_port(ci, id_D2, iol, id_TXDATA2); + replace_port(ci, id_D3, iol, id_TXDATA3); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MODDRX.MODE")] = std::string("MODDRX2"); iol->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = - std::string(ci->type == ctx->id("ODDRX2DQSB") ? "DQSW" : "DQSW270"); - process_dqs_port(ci, pio, iol, ci->type == ctx->id("ODDRX2DQSB") ? id_DQSW : id_DQSW270); - pio->params[ctx->id("DATAMUX_MDDR")] = std::string("IOLDO"); + std::string(ci->type == id_ODDRX2DQSB ? "DQSW" : "DQSW270"); + process_dqs_port(ci, pio, iol, ci->type == id_ODDRX2DQSB ? id_DQSW : id_DQSW270); + pio->params[id_DATAMUX_MDDR] = std::string("IOLDO"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("IDDRX2DQA")) { - CellInfo *pio = net_driven_by(ctx, ci->ports.at(ctx->id("D")).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(ctx->id("D")).net->users.size() > 1) + } else if (ci->type == id_IDDRX2DQA) { + CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); + if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1) log_error("IDDRX2DQA '%s' D input must be connected only to a top level input\n", ci->name.c_str(ctx)); CellInfo *iol; @@ -2558,16 +2539,16 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, ctx->id("D"), iol, id_PADDI); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), true); + replace_port(ci, id_D, iol, id_PADDI); + set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_eclk(iol, ci, id_ECLK); - set_iologic_lsr(iol, ci, ctx->id("RST"), true); - replace_port(ci, ctx->id("Q0"), iol, id_RXDATA0); - replace_port(ci, ctx->id("Q1"), iol, id_RXDATA1); - replace_port(ci, ctx->id("Q2"), iol, id_RXDATA2); - replace_port(ci, ctx->id("Q3"), iol, id_RXDATA3); - replace_port(ci, ctx->id("QWL"), iol, id_INFF); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + set_iologic_lsr(iol, ci, id_RST, true); + replace_port(ci, id_Q0, iol, id_RXDATA0); + replace_port(ci, id_Q1, iol, id_RXDATA1); + replace_port(ci, id_Q2, iol, id_RXDATA2); + replace_port(ci, id_Q3, iol, id_RXDATA3); + replace_port(ci, id_QWL, iol, id_INFF); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MIDDRX.MODE")] = std::string("MIDDRX2"); process_dqs_port(ci, pio, iol, id_DQSR90); process_dqs_port(ci, pio, iol, id_RDPNTR2); @@ -2577,8 +2558,8 @@ class Ecp5Packer process_dqs_port(ci, pio, iol, id_WRPNTR1); process_dqs_port(ci, pio, iol, id_WRPNTR0); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("TSHX2DQA") || ci->type == ctx->id("TSHX2DQSA")) { - CellInfo *pio = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_T, true); + } else if (ci->type == id_TSHX2DQA || ci->type == id_TSHX2DQSA) { + CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_T, true); if (pio == nullptr) log_error("%s '%s' Q output must be connected only to a top level tristate\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); @@ -2588,33 +2569,33 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, ctx->id("Q"), iol, id_IOLTO); + replace_port(ci, id_Q, iol, id_IOLTO); if (!pio->ports.count(id_IOLTO)) { pio->ports[id_IOLTO].name = id_IOLTO; pio->ports[id_IOLTO].type = PORT_IN; } replace_port(pio, id_T, pio, id_IOLTO); - set_iologic_sclk(iol, ci, ctx->id("SCLK"), false); + set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_eclk(iol, ci, id_ECLK); - set_iologic_lsr(iol, ci, ctx->id("RST"), false); - replace_port(ci, ctx->id("T0"), iol, id_TSDATA0); - replace_port(ci, ctx->id("T1"), iol, id_TSDATA1); - process_dqs_port(ci, pio, iol, ci->type == ctx->id("TSHX2DQSA") ? id_DQSW : id_DQSW270); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + set_iologic_lsr(iol, ci, id_RST, false); + replace_port(ci, id_T0, iol, id_TSDATA0); + replace_port(ci, id_T1, iol, id_TSDATA1); + process_dqs_port(ci, pio, iol, ci->type == id_TSHX2DQSA ? id_DQSW : id_DQSW270); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MTDDRX.MODE")] = std::string("MTSHX2"); iol->params[ctx->id("MTDDRX.REGSET")] = std::string("SET"); iol->params[ctx->id("MTDDRX.DQSW_INVERT")] = - std::string(ci->type == ctx->id("TSHX2DQSA") ? "ENABLED" : "DISABLED"); + std::string(ci->type == id_TSHX2DQSA ? "ENABLED" : "DISABLED"); iol->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = - std::string(ci->type == ctx->id("TSHX2DQSA") ? "DQSW" : "DQSW270"); - iol->params[ctx->id("IOLTOMUX")] = std::string("TDDR"); + std::string(ci->type == id_TSHX2DQSA ? "DQSW" : "DQSW270"); + iol->params[id_IOLTOMUX] = std::string("TDDR"); packed_cells.insert(cell.first); - } else if (ci->type == ctx->id("TRELLIS_FF") && bool_or_default(ci->attrs, ctx->id("syn_useioff"))) { + } else if (ci->type == id_TRELLIS_FF && bool_or_default(ci->attrs, id_syn_useioff)) { // Pack IO flipflop into IOLOGIC - std::string mode = str_or_default(ci->attrs, ctx->id("ioff_dir"), ""); + std::string mode = str_or_default(ci->attrs, id_ioff_dir, ""); if (mode != "output") { // See if it can be packed as an input ff - NetInfo *d = get_net_or_empty(ci, ctx->id("DI")); + NetInfo *d = get_net_or_empty(ci, id_DI); CellInfo *pio = net_driven_by(ctx, d, is_trellis_io, id_O); if (pio != nullptr && d->users.size() == 1) { // Input FF @@ -2624,35 +2605,35 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IREG_OREG"); - set_iologic_sclk(iol, ci, ctx->id("CLK"), true); - set_iologic_lsr(iol, ci, ctx->id("LSR"), true); + set_iologic_sclk(iol, ci, id_CLK, true); + set_iologic_lsr(iol, ci, id_LSR, true); // Handle CLK and CE muxes - if (str_or_default(ci->params, ctx->id("CLKMUX")) == "INV") - iol->params[ctx->id("CLKIMUX")] = std::string("INV"); - if (str_or_default(ci->params, ctx->id("CEMUX"), "CE") == "CE") { - iol->params[ctx->id("CEIMUX")] = std::string("CEMUX"); - iol->params[ctx->id("CEMUX")] = std::string("CE"); - if (get_net_or_empty(ci, ctx->id("CE")) == nullptr) - replace_port(ci, ctx->id("CE"), iol, ctx->id("CE")); + if (str_or_default(ci->params, id_CLKMUX) == "INV") + iol->params[id_CLKIMUX] = std::string("INV"); + if (str_or_default(ci->params, id_CEMUX, "CE") == "CE") { + iol->params[id_CEIMUX] = std::string("CEMUX"); + iol->params[id_CEMUX] = std::string("CE"); + if (get_net_or_empty(ci, id_CE) == nullptr) + replace_port(ci, id_CE, iol, id_CE); else - disconnect_port(ctx, ci, ctx->id("CE")); + disconnect_port(ctx, ci, id_CE); } else { - iol->params[ctx->id("CEIMUX")] = std::string("1"); + iol->params[id_CEIMUX] = std::string("1"); } // Set IOLOGIC params from FF params iol->params[ctx->id("FF.INREGMODE")] = std::string("FF"); - iol->params[ctx->id("FF.REGSET")] = str_or_default(ci->params, ctx->id("REGSET"), "RESET"); - iol->params[ctx->id("SRMODE")] = str_or_default(ci->params, ctx->id("SRMODE"), "ASYNC"); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); - replace_port(ci, ctx->id("DI"), iol, id_PADDI); - replace_port(ci, ctx->id("Q"), iol, id_INFF); + iol->params[ctx->id("FF.REGSET")] = str_or_default(ci->params, id_REGSET, "RESET"); + iol->params[id_SRMODE] = str_or_default(ci->params, id_SRMODE, "ASYNC"); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); + replace_port(ci, id_DI, iol, id_PADDI); + replace_port(ci, id_Q, iol, id_INFF); packed_cells.insert(cell.first); continue; } } if (mode != "input") { - CellInfo *pio_t = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_T, true); - CellInfo *pio_i = net_only_drives(ctx, ci->ports.at(ctx->id("Q")).net, is_trellis_io, id_I, true); + CellInfo *pio_t = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_T, true); + CellInfo *pio_i = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio_t != nullptr || pio_i != nullptr) { // Output or tristate FF bool tri = (pio_t != nullptr); @@ -2664,47 +2645,47 @@ class Ecp5Packer iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IREG_OREG"); // Connection between FF and PIO - replace_port(ci, ctx->id("Q"), iol, tri ? id_IOLTO : id_IOLDO); + replace_port(ci, id_Q, iol, tri ? id_IOLTO : id_IOLDO); if (tri) { if (!pio->ports.count(id_IOLTO)) { pio->ports[id_IOLTO].name = id_IOLTO; pio->ports[id_IOLTO].type = PORT_IN; } - pio->params[ctx->id("TRIMUX_TSREG")] = std::string("IOLTO"); + pio->params[id_TRIMUX_TSREG] = std::string("IOLTO"); replace_port(pio, id_T, pio, id_IOLTO); } else { if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } - pio->params[ctx->id("DATAMUX_OREG")] = std::string("IOLDO"); + pio->params[id_DATAMUX_OREG] = std::string("IOLDO"); replace_port(pio, id_I, pio, id_IOLDO); } - set_iologic_sclk(iol, ci, ctx->id("CLK"), false); - set_iologic_lsr(iol, ci, ctx->id("LSR"), false); + set_iologic_sclk(iol, ci, id_CLK, false); + set_iologic_lsr(iol, ci, id_LSR, false); // Handle CLK and CE muxes - if (str_or_default(ci->params, ctx->id("CLKMUX")) == "INV") - iol->params[ctx->id("CLKOMUX")] = std::string("INV"); - if (str_or_default(ci->params, ctx->id("CEMUX"), "CE") == "CE") { - iol->params[ctx->id("CEOMUX")] = std::string("CEMUX"); - iol->params[ctx->id("CEMUX")] = std::string("CE"); - if (get_net_or_empty(ci, ctx->id("CE")) == nullptr) - replace_port(ci, ctx->id("CE"), iol, ctx->id("CE")); + if (str_or_default(ci->params, id_CLKMUX) == "INV") + iol->params[id_CLKOMUX] = std::string("INV"); + if (str_or_default(ci->params, id_CEMUX, "CE") == "CE") { + iol->params[id_CEOMUX] = std::string("CEMUX"); + iol->params[id_CEMUX] = std::string("CE"); + if (get_net_or_empty(ci, id_CE) == nullptr) + replace_port(ci, id_CE, iol, id_CE); else - disconnect_port(ctx, ci, ctx->id("CE")); + disconnect_port(ctx, ci, id_CE); } else { - iol->params[ctx->id("CEOMUX")] = std::string("1"); + iol->params[id_CEOMUX] = std::string("1"); } // FF params iol->params[ctx->id(tri ? "TSREG.OUTREGMODE" : "OUTREG.OUTREGMODE")] = std::string("FF"); iol->params[ctx->id(tri ? "TSREG.REGSET" : "OUTREG.REGSET")] = - str_or_default(ci->params, ctx->id("REGSET"), "RESET"); - iol->params[ctx->id("SRMODE")] = str_or_default(ci->params, ctx->id("SRMODE"), "ASYNC"); + str_or_default(ci->params, id_REGSET, "RESET"); + iol->params[id_SRMODE] = str_or_default(ci->params, id_SRMODE, "ASYNC"); // Data input - replace_port(ci, ctx->id("DI"), iol, tri ? id_TSDATA0 : id_TXDATA0); - iol->params[ctx->id("GSR")] = str_or_default(ci->params, ctx->id("GSR"), "DISABLED"); + replace_port(ci, id_DI, iol, tri ? id_TSDATA0 : id_TXDATA0); + iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); continue; } @@ -2724,26 +2705,25 @@ class Ecp5Packer if (input == nullptr) continue; for (auto user : input->users) { - if (!user.cell->attrs.count(ctx->id("BEL"))) + if (!user.cell->attrs.count(id_BEL)) continue; - Loc user_loc = ctx->getBelLocation( - ctx->getBelByNameStr(user.cell->attrs.at(ctx->id("BEL")).as_string())); + Loc user_loc = + ctx->getBelLocation(ctx->getBelByNameStr(user.cell->attrs.at(id_BEL).as_string())); for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_ECLKBRIDGECS) continue; loc = ctx->getBelLocation(bel); if (loc.x == user_loc.x) { - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); goto eclkbridge_done; } } } if (input->driver.cell != nullptr) { CellInfo *drv = input->driver.cell; - if (!drv->attrs.count(ctx->id("BEL"))) + if (!drv->attrs.count(id_BEL)) continue; - Loc drv_loc = - ctx->getBelLocation(ctx->getBelByNameStr(drv->attrs.at(ctx->id("BEL")).as_string())); + Loc drv_loc = ctx->getBelLocation(ctx->getBelByNameStr(drv->attrs.at(id_BEL).as_string())); BelId closest; int closest_x = -1; // aim for same side of chip for (auto bel : ctx->getBels()) { @@ -2757,7 +2737,7 @@ class Ecp5Packer } NPNR_ASSERT(closest != BelId()); loc = ctx->getBelLocation(closest); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(closest).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(closest).str(ctx); goto eclkbridge_done; } } @@ -2766,7 +2746,7 @@ class Ecp5Packer if (ctx->getBelType(bel) != id_ECLKBRIDGECS) continue; loc = ctx->getBelLocation(bel); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); } eclkbridge_done: if (o != nullptr) @@ -2787,7 +2767,7 @@ class Ecp5Packer if (ci->type == id_IOLOGIC || ci->type == id_DQSBUFM) { if (!ci->ports.count(id_ECLK) || ci->ports.at(id_ECLK).net == nullptr) continue; - BelId bel = ctx->getBelByNameStr(str_or_default(ci->attrs, ctx->id("BEL"))); + BelId bel = ctx->getBelByNameStr(str_or_default(ci->attrs, id_BEL)); NPNR_ASSERT(bel != BelId()); Loc pioLoc = ctx->getBelLocation(bel); if (ci->type == id_DQSBUFM) @@ -2818,7 +2798,7 @@ class Ecp5Packer // z-index of CLKDIVF must match index of ECLK if (loc.z != eclk.first.second) continue; - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); make_eclk(ci->ports.at(id_CLKI), ci, bel, eclk.first.first); goto clkdiv_done; } @@ -2831,10 +2811,9 @@ class Ecp5Packer const NetInfo *eclko = net_or_nullptr(ci, id_ECLKO); if (eclki != nullptr && eclki->driver.cell != nullptr) { if (eclki->driver.cell->type == id_ECLKBRIDGECS) { - BelId bel = ctx->getBelByNameStr(eclki->driver.cell->attrs.at(ctx->id("BEL")).as_string()); + BelId bel = ctx->getBelByNameStr(eclki->driver.cell->attrs.at(id_BEL).as_string()); Loc loc = ctx->getBelLocation(bel); - ci->attrs[ctx->id("BEL")] = - ctx->getBelName(ctx->getBelByLocation(Loc(loc.x, loc.y, 15))).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(Loc(loc.x, loc.y, 15))).str(ctx); used_eclksyncb.insert(bel); goto eclksync_done; } @@ -2843,14 +2822,14 @@ class Ecp5Packer log_error("ECLKSYNCB '%s' has disconnected port ECLKO\n", ci->name.c_str(ctx)); for (auto user : eclko->users) { if (user.cell->type == id_TRELLIS_ECLKBUF) { - Loc eckbuf_loc = ctx->getBelLocation( - ctx->getBelByNameStr(user.cell->attrs.at(ctx->id("BEL")).as_string())); + Loc eckbuf_loc = + ctx->getBelLocation(ctx->getBelByNameStr(user.cell->attrs.at(id_BEL).as_string())); for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_ECLKSYNCB) continue; Loc loc = ctx->getBelLocation(bel); if (loc.x == eckbuf_loc.x && loc.y == eckbuf_loc.y && loc.z == eckbuf_loc.z - 2) { - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); used_eclksyncb.insert(bel); goto eclksync_done; } @@ -2859,7 +2838,7 @@ class Ecp5Packer } eclksync_done: continue; - } else if (ci->type == ctx->id("DDRDLLA")) { + } else if (ci->type == id_DDRDLLA) { ci->type = id_DDRDLL; // transform from Verilog to Bel name const NetInfo *clk = net_or_nullptr(ci, id_CLK); if (clk == nullptr) @@ -2871,9 +2850,9 @@ class Ecp5Packer if (ddrdel != nullptr) { for (auto &usr : ddrdel->users) { const CellInfo *uc = usr.cell; - if (uc->type != id_DQSBUFM || !uc->attrs.count(ctx->id("BEL"))) + if (uc->type != id_DQSBUFM || !uc->attrs.count(id_BEL)) continue; - BelId dqsb_bel = ctx->getBelByNameStr(uc->attrs.at(ctx->id("BEL")).as_string()); + BelId dqsb_bel = ctx->getBelByNameStr(uc->attrs.at(id_BEL).as_string()); Loc dqsb_loc = ctx->getBelLocation(dqsb_bel); if (dqsb_loc.x > 15) right_bank_users = true; @@ -2907,7 +2886,7 @@ class Ecp5Packer if (eclk.first.first != ddrdll_bank) continue; log_info("Constraining DDRDLLA '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(bel)); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); make_eclk(ci->ports.at(id_CLK), ci, bel, eclk.first.first); goto ddrdll_done; } @@ -2923,7 +2902,7 @@ class Ecp5Packer if (ci->type == id_ECLKSYNCB) { // **All** ECLKSYNCBs must be constrained // Most will be dealt with above, but there might be some rogue cases - if (ci->attrs.count(ctx->id("BEL"))) + if (ci->attrs.count(id_BEL)) continue; for (BelId bel : ctx->getBels()) { if (ctx->getBelType(bel) != id_ECLKSYNCB) @@ -2932,7 +2911,7 @@ class Ecp5Packer if (used_eclksyncb.count(bel)) continue; log_info("Constraining ECLKSYNCB '%s' to bel '%s'\n", ctx->nameOf(ci), ctx->nameOfBel(bel)); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); goto eclksync_ii_done; } if (0) { @@ -2946,7 +2925,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_CLKDIVF) { - if (ci->attrs.count(ctx->id("BEL"))) + if (ci->attrs.count(id_BEL)) continue; // Case of a CLKDIVF driven by an ECLKSYNC constrained above; without the input being used elsewhere as // an edge clock @@ -2954,9 +2933,9 @@ class Ecp5Packer if (clki == nullptr || clki->driver.cell == nullptr) continue; CellInfo *drv = clki->driver.cell; - if (drv->type != id_ECLKSYNCB || !drv->attrs.count(ctx->id("BEL"))) + if (drv->type != id_ECLKSYNCB || !drv->attrs.count(id_BEL)) continue; - BelId bel = ctx->getBelByNameStr(drv->attrs.at(ctx->id("BEL")).as_string()); + BelId bel = ctx->getBelByNameStr(drv->attrs.at(id_BEL).as_string()); // Find a CLKDIVF that is routeable from the ECLKSYNC std::queue visit; visit.push(ctx->getBelPinWire(bel, id_ECLKO)); @@ -2966,7 +2945,7 @@ class Ecp5Packer for (BelPin bp : ctx->getWireBelPins(cursor)) { if (ctx->getBelType(bp.bel) != id_CLKDIVF || bp.pin != id_CLKI) continue; - ci->attrs[ctx->id("BEL")] = ctx->getBelName(bp.bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(bp.bel).str(ctx); log_info("Constraining CLKDIVF '%s' to bel '%s' based on ECLKSYNCB.\n", ctx->nameOf(ci), ctx->nameOfBel(bp.bel)); goto clkdiv_ii_done; @@ -3076,7 +3055,7 @@ class Ecp5Packer for (auto cell : changed_cells) { CellInfo *ci = ctx->cells.at(cell).get(); if (ci->type == id_CLKDIVF) { - std::string div = str_or_default(ci->params, ctx->id("DIV"), "2.0"); + std::string div = str_or_default(ci->params, id_DIV, "2.0"); double ratio; if (div == "2.0") ratio = 1 / 2.0; @@ -3098,17 +3077,17 @@ class Ecp5Packer continue; log_info(" Input frequency of PLL '%s' is constrained to %.1f MHz\n", ci->name.c_str(ctx), MHz(period_in)); - double period_in_div = period_in * int_or_default(ci->params, ctx->id("CLKI_DIV"), 1); - std::string path = str_or_default(ci->params, ctx->id("FEEDBK_PATH"), "CLKOP"); - int feedback_div = int_or_default(ci->params, ctx->id("CLKFB_DIV"), 1); + double period_in_div = period_in * int_or_default(ci->params, id_CLKI_DIV, 1); + std::string path = str_or_default(ci->params, id_FEEDBK_PATH, "CLKOP"); + int feedback_div = int_or_default(ci->params, id_CLKFB_DIV, 1); if (path == "CLKOP" || path == "INT_OP") - feedback_div *= int_or_default(ci->params, ctx->id("CLKOP_DIV"), 1); + feedback_div *= int_or_default(ci->params, id_CLKOP_DIV, 1); else if (path == "CLKOS" || path == "INT_OS") - feedback_div *= int_or_default(ci->params, ctx->id("CLKOS_DIV"), 1); + feedback_div *= int_or_default(ci->params, id_CLKOS_DIV, 1); else if (path == "CLKOS2" || path == "INT_OS2") - feedback_div *= int_or_default(ci->params, ctx->id("CLKOS2_DIV"), 1); + feedback_div *= int_or_default(ci->params, id_CLKOS2_DIV, 1); else if (path == "CLKOS3" || path == "INT_OS3") - feedback_div *= int_or_default(ci->params, ctx->id("CLKOS3_DIV"), 1); + feedback_div *= int_or_default(ci->params, id_CLKOS3_DIV, 1); else { log_info(" Unable to determine output frequencies for PLL '%s' with FEEDBK_PATH=%s\n", ci->name.c_str(ctx), path.c_str()); @@ -3120,12 +3099,12 @@ class Ecp5Packer log_info(" Derived VCO frequency %.1f MHz of PLL '%s' is out of legal range [400MHz, " "800MHz]\n", vco_freq, ci->name.c_str(ctx)); - set_period(ci, id_CLKOP, vco_period * int_or_default(ci->params, ctx->id("CLKOP_DIV"), 1)); - set_period(ci, id_CLKOS, vco_period * int_or_default(ci->params, ctx->id("CLKOS_DIV"), 1)); - set_period(ci, id_CLKOS2, vco_period * int_or_default(ci->params, ctx->id("CLKOS2_DIV"), 1)); - set_period(ci, id_CLKOS3, vco_period * int_or_default(ci->params, ctx->id("CLKOS3_DIV"), 1)); + set_period(ci, id_CLKOP, vco_period * int_or_default(ci->params, id_CLKOP_DIV, 1)); + set_period(ci, id_CLKOS, vco_period * int_or_default(ci->params, id_CLKOS_DIV, 1)); + set_period(ci, id_CLKOS2, vco_period * int_or_default(ci->params, id_CLKOS2_DIV, 1)); + set_period(ci, id_CLKOS3, vco_period * int_or_default(ci->params, id_CLKOS3_DIV, 1)); } else if (ci->type == id_OSCG) { - int div = int_or_default(ci->params, ctx->id("DIV"), 128); + int div = int_or_default(ci->params, id_DIV, 128); set_period(ci, id_OSC, delay_t((1.0e6 / (2.0 * 155)) * div)); } } @@ -3137,8 +3116,8 @@ class Ecp5Packer // Check for legacy-style JSON (use CEMUX as a clue) and error out, avoiding a confusing assertion failure // later for (auto &cell : ctx->cells) { - if (is_ff(ctx, cell.second.get()) && cell.second->params.count(ctx->id("CEMUX")) && - !cell.second->params[ctx->id("CEMUX")].is_string) + if (is_ff(ctx, cell.second.get()) && cell.second->params.count(id_CEMUX) && + !cell.second->params[id_CEMUX].is_string) log_error("Found netlist using legacy-style JSON parameter values, please update your Yosys.\n"); } } @@ -3199,7 +3178,7 @@ bool Arch::pack() Ecp5Packer(ctx).pack(); log_info("Checksum: 0x%08x\n", ctx->checksum()); assignArchInfo(); - ctx->settings[ctx->id("pack")] = 1; + ctx->settings[id_pack] = 1; archInfoToAttributes(); return true; } catch (log_execution_error_exception) { @@ -3233,24 +3212,24 @@ void Arch::assignArchInfo() ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK")); ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR")); ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); - std::string mode = str_or_default(ci->params, id("MODE"), "LOGIC"); + std::string mode = str_or_default(ci->params, id_MODE, "LOGIC"); ci->sliceInfo.is_carry = (mode == "CCU2"); ci->sliceInfo.is_memory = (mode == "DPRAM" || mode == "RAMW"); - ci->sliceInfo.sd0 = std::stoi(str_or_default(ci->params, id("REG0_SD"), "0")); - ci->sliceInfo.sd1 = std::stoi(str_or_default(ci->params, id("REG1_SD"), "0")); + ci->sliceInfo.sd0 = std::stoi(str_or_default(ci->params, id_REG0_SD, "0")); + ci->sliceInfo.sd1 = std::stoi(str_or_default(ci->params, id_REG1_SD, "0")); ci->sliceInfo.has_l6mux = false; if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr && ci->ports[id_FXA].net->driver.port == id_OFX0) ci->sliceInfo.has_l6mux = true; } else if (ci->type == id_DP16KD) { - ci->ramInfo.is_pdp = (int_or_default(ci->params, id("DATA_WIDTH_A"), 0) == 36); + ci->ramInfo.is_pdp = (int_or_default(ci->params, id_DATA_WIDTH_A, 0) == 36); // Output register mode (REGMODE_{A,B}). Valid options are 'NOREG' and 'OUTREG'. - std::string regmode_a = str_or_default(ci->params, id("REGMODE_A"), "NOREG"); + std::string regmode_a = str_or_default(ci->params, id_REGMODE_A, "NOREG"); if (regmode_a != "NOREG" && regmode_a != "OUTREG") log_error("DP16KD %s has invalid REGMODE_A configuration '%s'\n", ci->name.c_str(this), regmode_a.c_str()); - std::string regmode_b = str_or_default(ci->params, id("REGMODE_B"), "NOREG"); + std::string regmode_b = str_or_default(ci->params, id_REGMODE_B, "NOREG"); if (regmode_b != "NOREG" && regmode_b != "OUTREG") log_error("DP16KD %s has invalid REGMODE_B configuration '%s'\n", ci->name.c_str(this), regmode_b.c_str()); @@ -3326,7 +3305,7 @@ void Arch::assignArchInfo() } } for (auto &net : nets) { - net.second->is_global = bool_or_default(net.second->attrs, id("ECP5_IS_GLOBAL")); + net.second->is_global = bool_or_default(net.second->attrs, id_ECP5_IS_GLOBAL); } } diff --git a/gowin/arch.cc b/gowin/arch.cc index 0793832601..b104013dbc 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -688,7 +688,7 @@ void Arch::read_cst(std::istream &in) insloc } cst_type; - settings.erase(id("cst")); + settings.erase(id_cst); while (!in.eof()) { std::getline(in, line); cst_type = ioloc; @@ -756,7 +756,7 @@ void Arch::read_cst(std::istream &in) } } } - settings[id("cst")] = 1; + settings[id_cst] = 1; } // Add all MUXes for the cell @@ -1455,13 +1455,13 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bool Arch::place() { - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + std::string placer = str_or_default(settings, id_placer, defaultPlacer); bool retVal; if (placer == "heap") { bool have_iobuf_or_constr = false; for (auto &cell : cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id("IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { + if (ci->type == id_IOB || ci->bel != BelId() || ci->attrs.count(id_BEL)) { have_iobuf_or_constr = true; break; } @@ -1472,15 +1472,15 @@ bool Arch::place() retVal = placer1(getCtx(), Placer1Cfg(getCtx())); } else { PlacerHeapCfg cfg(getCtx()); - cfg.ioBufTypes.insert(id("IOB")); + cfg.ioBufTypes.insert(id_IOB); cfg.beta = 0.5; retVal = placer_heap(getCtx(), cfg); } - getCtx()->settings[getCtx()->id("place")] = 1; + getCtx()->settings[id_place] = 1; archInfoToAttributes(); } else if (placer == "sa") { retVal = placer1(getCtx(), Placer1Cfg(getCtx())); - getCtx()->settings[getCtx()->id("place")] = 1; + getCtx()->settings[id_place] = 1; archInfoToAttributes(); return retVal; } else { @@ -1497,7 +1497,7 @@ bool Arch::place() bool Arch::route() { - std::string router = str_or_default(settings, id("router"), defaultRouter); + std::string router = str_or_default(settings, id_router, defaultRouter); bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); @@ -1507,7 +1507,7 @@ bool Arch::route() } else { log_error("Gowin architecture does not support router '%s'\n", router.c_str()); } - getCtx()->settings[getCtx()->id("route")] = 1; + getCtx()->settings[id_route] = 1; archInfoToAttributes(); return result; } @@ -1600,9 +1600,9 @@ void Arch::assignArchInfo() ci->is_slice = true; ci->ff_used = ci->params.at(id_FF_USED).as_bool(); ci->ff_type = id(ci->params.at(id_FF_TYPE).as_string()); - ci->slice_clk = get_net_or_empty(ci, id("CLK")); - ci->slice_ce = get_net_or_empty(ci, id("CE")); - ci->slice_lsr = get_net_or_empty(ci, id("LSR")); + ci->slice_clk = get_net_or_empty(ci, id_CLK); + ci->slice_ce = get_net_or_empty(ci, id_CE); + ci->slice_lsr = get_net_or_empty(ci, id_LSR); // add timing paths addCellTimingClock(cname, id_CLK); diff --git a/gowin/arch.h b/gowin/arch.h index 9ce3bd1c29..7a5dfc466d 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -354,7 +354,7 @@ struct Arch : BaseArch std::string getChipName() const override { return device; } ArchArgs archArgs() const override { return args; } - IdString archArgsToId(ArchArgs args) const override { return id("none"); } + IdString archArgsToId(ArchArgs args) const override { return id_none; } int getGridDimX() const override { return gridDimX; } int getGridDimY() const override { return gridDimY; } diff --git a/gowin/constids.inc b/gowin/constids.inc index 82d0bb11df..be016e0e62 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -806,3 +806,12 @@ X(DECAL_ALU_ACTIVE) + +X(SINGLE_INPUT_MUX) +X(cst) +X(none) +X(pack) +X(place) +X(placer) +X(route) +X(router) diff --git a/gowin/pack.cc b/gowin/pack.cc index 3d33f2d8a4..1201e31086 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -114,7 +114,7 @@ static void pack_alus(Context *ctx) int alu_idx = 1; do { // go through the ALU chain - auto alu_bel = ci->attrs.find(ctx->id("BEL")); + auto alu_bel = ci->attrs.find(id_BEL); if (alu_bel != ci->attrs.end()) { log_error("ALU %s placement restrictions are not supported.\n", ctx->nameOf(ci)); return; @@ -243,7 +243,7 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce std::vector> &new_cells) { - if (bool_or_default(ci->attrs, ctx->id("SINGLE_INPUT_MUX"))) { + if (bool_or_default(ci->attrs, id_SINGLE_INPUT_MUX)) { // find the muxed LUT NetInfo *i1 = ci->ports.at(id_I1).net; @@ -257,14 +257,14 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce } // XXX enable the placement constraints - auto mux_bel = ci->attrs.find(ctx->id("BEL")); - auto lut1_bel = lut1->attrs.find(ctx->id("BEL")); + auto mux_bel = ci->attrs.find(id_BEL); + auto lut1_bel = lut1->attrs.find(id_BEL); if (lut1_bel != lut1->attrs.end() || mux_bel != ci->attrs.end()) { log_error("MUX2_LUT5 '%s' placement restrictions are not supported yet\n", ctx->nameOf(ci)); return; } - std::unique_ptr packed = create_generic_cell(ctx, ctx->id("GW_MUX2_LUT5"), ci->name.str(ctx) + "_LC"); + std::unique_ptr packed = create_generic_cell(ctx, id_GW_MUX2_LUT5, ci->name.str(ctx) + "_LC"); if (ctx->verbose) { log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get())); } @@ -299,15 +299,15 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce } // XXX enable the placement constraints - auto mux_bel = ci->attrs.find(ctx->id("BEL")); - auto lut0_bel = lut0->attrs.find(ctx->id("BEL")); - auto lut1_bel = lut1->attrs.find(ctx->id("BEL")); + auto mux_bel = ci->attrs.find(id_BEL); + auto lut0_bel = lut0->attrs.find(id_BEL); + auto lut1_bel = lut1->attrs.find(id_BEL); if (lut0_bel != lut0->attrs.end() || lut1_bel != lut1->attrs.end() || mux_bel != ci->attrs.end()) { log_error("MUX2_LUT5 '%s' placement restrictions are not supported yet\n", ctx->nameOf(ci)); return; } - std::unique_ptr packed = create_generic_cell(ctx, ctx->id("GW_MUX2_LUT5"), ci->name.str(ctx) + "_LC"); + std::unique_ptr packed = create_generic_cell(ctx, id_GW_MUX2_LUT5, ci->name.str(ctx) + "_LC"); if (ctx->verbose) { log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get())); } @@ -354,9 +354,9 @@ static void pack_mux2_lut(Context *ctx, CellInfo *ci, bool (*pred)(const BaseCtx } // XXX enable the placement constraints - auto mux_bel = ci->attrs.find(ctx->id("BEL")); - auto mux0_bel = mux0->attrs.find(ctx->id("BEL")); - auto mux1_bel = mux1->attrs.find(ctx->id("BEL")); + auto mux_bel = ci->attrs.find(id_BEL); + auto mux0_bel = mux0->attrs.find(id_BEL); + auto mux1_bel = mux1->attrs.find(id_BEL); if (mux0_bel != mux0->attrs.end() || mux1_bel != mux1->attrs.end() || mux_bel != ci->attrs.end()) { log_error("MUX2_LUT%c '%s' placement restrictions are not supported yet\n", type_suffix, ctx->nameOf(ci)); return; @@ -512,7 +512,7 @@ static void pack_lut_lutffs(Context *ctx) if (ctx->verbose) log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); if (is_lut(ctx, ci)) { - std::unique_ptr packed = create_generic_cell(ctx, ctx->id("SLICE"), ci->name.str(ctx) + "_LC"); + std::unique_ptr packed = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "_LC"); for (auto &attr : ci->attrs) packed->attrs[attr.first] = attr.second; packed_cells.insert(ci->name); @@ -520,14 +520,14 @@ static void pack_lut_lutffs(Context *ctx) log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get())); // See if we can pack into a DFF // TODO: LUT cascade - NetInfo *o = ci->ports.at(ctx->id("F")).net; - CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true); - auto lut_bel = ci->attrs.find(ctx->id("BEL")); + NetInfo *o = ci->ports.at(id_F).net; + CellInfo *dff = net_only_drives(ctx, o, is_ff, id_D, true); + auto lut_bel = ci->attrs.find(id_BEL); bool packed_dff = false; if (dff) { if (ctx->verbose) log_info("found attached dff %s\n", ctx->nameOf(dff)); - auto dff_bel = dff->attrs.find(ctx->id("BEL")); + auto dff_bel = dff->attrs.find(id_BEL); if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { // Locations don't match, can't pack } else { @@ -535,7 +535,7 @@ static void pack_lut_lutffs(Context *ctx) dff_to_lc(ctx, dff, packed.get(), false); ctx->nets.erase(o->name); if (dff_bel != dff->attrs.end()) - packed->attrs[ctx->id("BEL")] = dff_bel->second; + packed->attrs[id_BEL] = dff_bel->second; packed_cells.insert(dff->name); if (ctx->verbose) log_info("packed cell %s into %s\n", ctx->nameOf(dff), ctx->nameOf(packed.get())); @@ -567,7 +567,7 @@ static void pack_nonlut_ffs(Context *ctx) for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_ff(ctx, ci)) { - std::unique_ptr packed = create_generic_cell(ctx, ctx->id("SLICE"), ci->name.str(ctx) + "_DFFLC"); + std::unique_ptr packed = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "_DFFLC"); for (auto &attr : ci->attrs) packed->attrs[attr.first] = attr.second; if (ctx->verbose) @@ -610,20 +610,20 @@ static void pack_constants(Context *ctx) { log_info("Packing constants..\n"); - std::unique_ptr gnd_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_GND"); - gnd_cell->params[ctx->id("INIT")] = Property(0, 1 << 4); + std::unique_ptr gnd_cell = create_generic_cell(ctx, id_SLICE, "$PACKER_GND"); + gnd_cell->params[id_INIT] = Property(0, 1 << 4); auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); - gnd_net->driver.port = ctx->id("F"); - gnd_cell->ports.at(ctx->id("F")).net = gnd_net.get(); + gnd_net->driver.port = id_F; + gnd_cell->ports.at(id_F).net = gnd_net.get(); - std::unique_ptr vcc_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_VCC"); + std::unique_ptr vcc_cell = create_generic_cell(ctx, id_SLICE, "$PACKER_VCC"); // Fill with 1s - vcc_cell->params[ctx->id("INIT")] = Property(Property::S1).extract(0, (1 << 4), Property::S1); + vcc_cell->params[id_INIT] = Property(Property::S1).extract(0, (1 << 4), Property::S1); auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); - vcc_net->driver.port = ctx->id("F"); - vcc_cell->ports.at(ctx->id("F")).net = vcc_net.get(); + vcc_net->driver.port = id_F; + vcc_cell->ports.at(id_F).net = vcc_net.get(); std::vector dead_nets; @@ -631,13 +631,13 @@ static void pack_constants(Context *ctx) for (auto &net : ctx->nets) { NetInfo *ni = net.second.get(); - if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { + if (ni->driver.cell != nullptr && ni->driver.cell->type == id_GND) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, gnd_net.get(), false); gnd_used = true; dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); - } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) { + } else if (ni->driver.cell != nullptr && ni->driver.cell->type == id_VCC) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, vcc_net.get(), true); dead_nets.push_back(net.first); @@ -780,7 +780,7 @@ bool Arch::pack() pack_alus(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); - ctx->settings[ctx->id("pack")] = 1; + ctx->settings[id_pack] = 1; ctx->assignArchInfo(); log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; diff --git a/ice40/arch.cc b/ice40/arch.cc index 0f0246ef0a..cf7e99a54b 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -169,29 +169,29 @@ std::string Arch::getChipName() const IdString Arch::archArgsToId(ArchArgs args) const { if (args.type == ArchArgs::LP384) - return id("lp384"); + return id_lp384; if (args.type == ArchArgs::LP1K) - return id("lp1k"); + return id_lp1k; if (args.type == ArchArgs::HX1K) - return id("hx1k"); + return id_hx1k; if (args.type == ArchArgs::UP3K) - return id("up3k"); + return id_up3k; if (args.type == ArchArgs::UP5K) - return id("up5k"); + return id_up5k; if (args.type == ArchArgs::U1K) - return id("u1k"); + return id_u1k; if (args.type == ArchArgs::U2K) - return id("u2k"); + return id_u2k; if (args.type == ArchArgs::U4K) - return id("u4k"); + return id_u4k; if (args.type == ArchArgs::LP4K) - return id("lp4k"); + return id_lp4k; if (args.type == ArchArgs::LP8K) - return id("lp8k"); + return id_lp8k; if (args.type == ArchArgs::HX4K) - return id("hx4k"); + return id_hx4k; if (args.type == ArchArgs::HX8K) - return id("hx8k"); + return id_hx8k; return IdString(); } @@ -289,7 +289,7 @@ std::vector> Arch::getBelAttrs(BelId bel) const { std::vector> ret; - ret.push_back(std::make_pair(id("INDEX"), stringf("%d", bel.index))); + ret.push_back(std::make_pair(id_INDEX, stringf("%d", bel.index))); return ret; } @@ -387,33 +387,33 @@ IdString Arch::getWireType(WireId wire) const case WireInfoPOD::WIRE_TYPE_NONE: return IdString(); case WireInfoPOD::WIRE_TYPE_GLB2LOCAL: - return id("GLB2LOCAL"); + return id_GLB2LOCAL; case WireInfoPOD::WIRE_TYPE_GLB_NETWK: - return id("GLB_NETWK"); + return id_GLB_NETWK; case WireInfoPOD::WIRE_TYPE_LOCAL: - return id("LOCAL"); + return id_LOCAL; case WireInfoPOD::WIRE_TYPE_LUTFF_IN: - return id("LUTFF_IN"); + return id_LUTFF_IN; case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT: - return id("LUTFF_IN_LUT"); + return id_LUTFF_IN_LUT; case WireInfoPOD::WIRE_TYPE_LUTFF_LOUT: - return id("LUTFF_LOUT"); + return id_LUTFF_LOUT; case WireInfoPOD::WIRE_TYPE_LUTFF_OUT: - return id("LUTFF_OUT"); + return id_LUTFF_OUT; case WireInfoPOD::WIRE_TYPE_LUTFF_COUT: - return id("LUTFF_COUT"); + return id_LUTFF_COUT; case WireInfoPOD::WIRE_TYPE_LUTFF_GLOBAL: - return id("LUTFF_GLOBAL"); + return id_LUTFF_GLOBAL; case WireInfoPOD::WIRE_TYPE_CARRY_IN_MUX: - return id("CARRY_IN_MUX"); + return id_CARRY_IN_MUX; case WireInfoPOD::WIRE_TYPE_SP4_V: - return id("SP4_V"); + return id_SP4_V; case WireInfoPOD::WIRE_TYPE_SP4_H: - return id("SP4_H"); + return id_SP4_H; case WireInfoPOD::WIRE_TYPE_SP12_V: - return id("SP12_V"); + return id_SP12_V; case WireInfoPOD::WIRE_TYPE_SP12_H: - return id("SP12_H"); + return id_SP12_H; } return IdString(); } @@ -423,11 +423,11 @@ std::vector> Arch::getWireAttrs(WireId wire) co std::vector> ret; auto &wi = chip_info->wire_data[wire.index]; - ret.push_back(std::make_pair(id("INDEX"), stringf("%d", wire.index))); + ret.push_back(std::make_pair(id_INDEX, stringf("%d", wire.index))); - ret.push_back(std::make_pair(id("GRID_X"), stringf("%d", wi.x))); - ret.push_back(std::make_pair(id("GRID_Y"), stringf("%d", wi.y))); - ret.push_back(std::make_pair(id("GRID_Z"), stringf("%d", wi.z))); + ret.push_back(std::make_pair(id_GRID_X, stringf("%d", wi.x))); + ret.push_back(std::make_pair(id_GRID_Y, stringf("%d", wi.y))); + ret.push_back(std::make_pair(id_GRID_Z, stringf("%d", wi.z))); return ret; } @@ -476,7 +476,7 @@ std::vector> Arch::getPipAttrs(PipId pip) const { std::vector> ret; - ret.push_back(std::make_pair(id("INDEX"), stringf("%d", pip.index))); + ret.push_back(std::make_pair(id_INDEX, stringf("%d", pip.index))); return ret; } @@ -682,7 +682,7 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay bool Arch::place() { - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + std::string placer = str_or_default(settings, id_placer, defaultPlacer); if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); cfg.ioBufTypes.insert(id_SB_IO); @@ -695,19 +695,19 @@ bool Arch::place() log_error("iCE40 architecture does not support placer '%s'\n", placer.c_str()); } bool retVal = true; - if (bool_or_default(settings, id("opt_timing"), false)) { + if (bool_or_default(settings, id_opt_timing, false)) { TimingOptCfg tocfg(getCtx()); tocfg.cellTypes.insert(id_ICESTORM_LC); retVal = timing_opt(getCtx(), tocfg); } - getCtx()->settings[getCtx()->id("place")] = 1; + getCtx()->settings[id_place] = 1; archInfoToAttributes(); return retVal; } bool Arch::route() { - std::string router = str_or_default(settings, id("router"), defaultRouter); + std::string router = str_or_default(settings, id_router, defaultRouter); bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); @@ -717,7 +717,7 @@ bool Arch::route() } else { log_error("iCE40 architecture does not support router '%s'\n", router.c_str()); } - getCtx()->settings[getCtx()->id("route")] = 1; + getCtx()->settings[id_route] = 1; archInfoToAttributes(); return result; } @@ -1064,7 +1064,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; return TMG_IGNORE; } else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) { - if (port == this->id("SBCLKI")) + if (port == id_SBCLKI) return TMG_CLOCK_INPUT; clockInfoCount = 1; @@ -1108,10 +1108,10 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } else if (cell->type == id_ICESTORM_RAM) { if (port.str(this)[0] == 'R') { info.clock_port = id_RCLK; - info.edge = bool_or_default(cell->params, id("NEG_CLK_R")) ? FALLING_EDGE : RISING_EDGE; + info.edge = bool_or_default(cell->params, id_NEG_CLK_R) ? FALLING_EDGE : RISING_EDGE; } else { info.clock_port = id_WCLK; - info.edge = bool_or_default(cell->params, id("NEG_CLK_W")) ? FALLING_EDGE : RISING_EDGE; + info.edge = bool_or_default(cell->params, id_NEG_CLK_W) ? FALLING_EDGE : RISING_EDGE; } if (cell->ports.at(port).type == PORT_OUT) { bool has_clktoq = get_cell_delay_internal(cell, info.clock_port, port, info.clockToQ); @@ -1168,7 +1168,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.hold = DelayPair(0); } } else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) { - info.clock_port = this->id("SBCLKI"); + info.clock_port = id_SBCLKI; info.edge = RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { /* Dummy number */ @@ -1233,12 +1233,12 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lcInfo.inputCount++; } else if (cell->type == id_SB_IO) { cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT"; - cell->ioInfo.global = bool_or_default(cell->attrs, this->id("GLOBAL")); - cell->ioInfo.pintype = int_or_default(cell->params, this->id("PIN_TYPE")); - cell->ioInfo.negtrig = bool_or_default(cell->params, this->id("NEG_TRIGGER")); + cell->ioInfo.global = bool_or_default(cell->attrs, id_GLOBAL); + cell->ioInfo.pintype = int_or_default(cell->params, id_PIN_TYPE); + cell->ioInfo.negtrig = bool_or_default(cell->params, id_NEG_TRIGGER); } else if (cell->type == id_SB_GB) { - cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN")); + cell->gbInfo.forPadIn = bool_or_default(cell->attrs, id_FOR_PAD_IN); } } diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index f01afdff6e..3b024d8175 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -120,7 +120,7 @@ bool Arch::isBelLocationValid(BelId bel) const break; // Are we perhaps a PAD INPUT Bel that can be placed here? - if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(getCtx())) + if (pll_cell->attrs[id_BEL_PAD_INPUT] == getBelName(bel).str(getCtx())) return true; // Conflict diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 8caa9dc698..89f8426207 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -391,7 +391,7 @@ void write_asc(const Context *ctx, std::ostream &out) pool sb_io_used_by_pll_pad; for (auto &cell : ctx->cells) { - if (cell.second->type != ctx->id("ICESTORM_PLL")) + if (cell.second->type != id_ICESTORM_PLL) continue; // Collect all locations matching an PLL output port @@ -434,16 +434,16 @@ void write_asc(const Context *ctx, std::ostream &out) std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl; continue; } - if (cell.second->type == ctx->id("ICESTORM_LC")) { + if (cell.second->type == id_ICESTORM_LC) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC]; - unsigned lut_init = get_param_or_def(ctx, cell.second.get(), ctx->id("LUT_INIT")); - bool neg_clk = get_param_or_def(ctx, cell.second.get(), ctx->id("NEG_CLK")); - bool dff_enable = get_param_or_def(ctx, cell.second.get(), ctx->id("DFF_ENABLE")); - bool async_sr = get_param_or_def(ctx, cell.second.get(), ctx->id("ASYNC_SR")); - bool set_noreset = get_param_or_def(ctx, cell.second.get(), ctx->id("SET_NORESET")); - bool carry_enable = get_param_or_def(ctx, cell.second.get(), ctx->id("CARRY_ENABLE")); + unsigned lut_init = get_param_or_def(ctx, cell.second.get(), id_LUT_INIT); + bool neg_clk = get_param_or_def(ctx, cell.second.get(), id_NEG_CLK); + bool dff_enable = get_param_or_def(ctx, cell.second.get(), id_DFF_ENABLE); + bool async_sr = get_param_or_def(ctx, cell.second.get(), id_ASYNC_SR); + bool set_noreset = get_param_or_def(ctx, cell.second.get(), id_SET_NORESET); + bool carry_enable = get_param_or_def(ctx, cell.second.get(), id_CARRY_ENABLE); std::vector lc(20, false); // Discover permutation @@ -502,20 +502,20 @@ void write_asc(const Context *ctx, std::ostream &out) if (dff_enable) set_config(ti, config.at(y).at(x), "NegClk", neg_clk); - bool carry_const = get_param_or_def(ctx, cell.second.get(), ctx->id("CIN_CONST")); - bool carry_set = get_param_or_def(ctx, cell.second.get(), ctx->id("CIN_SET")); + bool carry_const = get_param_or_def(ctx, cell.second.get(), id_CIN_CONST); + bool carry_set = get_param_or_def(ctx, cell.second.get(), id_CIN_SET); if (carry_const) { if (!ctx->force) NPNR_ASSERT(z == 0); set_config(ti, config.at(y).at(x), "CarryInSet", carry_set); } - } else if (cell.second->type == ctx->id("SB_IO")) { + } else if (cell.second->type == id_SB_IO) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; - unsigned pin_type = get_param_or_def(ctx, cell.second.get(), ctx->id("PIN_TYPE")); - bool neg_trigger = get_param_or_def(ctx, cell.second.get(), ctx->id("NEG_TRIGGER")); - bool pullup = get_param_or_def(ctx, cell.second.get(), ctx->id("PULLUP")); + unsigned pin_type = get_param_or_def(ctx, cell.second.get(), id_PIN_TYPE); + bool neg_trigger = get_param_or_def(ctx, cell.second.get(), id_NEG_TRIGGER); + bool pullup = get_param_or_def(ctx, cell.second.get(), id_PULLUP); bool lvds = cell.second->ioInfo.lvds; bool used_by_pll_out = sb_io_used_by_pll_out.count(Loc(x, y, z)) > 0; bool used_by_pll_pad = sb_io_used_by_pll_pad.count(Loc(x, y, z)) > 0; @@ -550,8 +550,8 @@ void write_asc(const Context *ctx, std::ostream &out) if (ctx->args.type == ArchArgs::UP5K || ctx->args.type == ArchArgs::UP3K) { std::string pullup_resistor = "100K"; - if (cell.second->attrs.count(ctx->id("PULLUP_RESISTOR"))) - pullup_resistor = cell.second->attrs.at(ctx->id("PULLUP_RESISTOR")).as_string(); + if (cell.second->attrs.count(id_PULLUP_RESISTOR)) + pullup_resistor = cell.second->attrs.at(id_PULLUP_RESISTOR).as_string(); NPNR_ASSERT(pullup_resistor == "100K" || pullup_resistor == "10K" || pullup_resistor == "6P8K" || pullup_resistor == "3P3K"); if (iez == 0) { @@ -599,7 +599,7 @@ void write_asc(const Context *ctx, std::ostream &out) } } } - } else if (cell.second->type == ctx->id("SB_GB")) { + } else if (cell.second->type == id_SB_GB) { if (cell.second->gbInfo.forPadIn) { Loc gb_loc = ctx->getBelLocation(bel); for (auto &glb : ci.global_network_info) { @@ -608,7 +608,7 @@ void write_asc(const Context *ctx, std::ostream &out) } } } - } else if (cell.second->type == ctx->id("ICESTORM_RAM")) { + } else if (cell.second->type == id_ICESTORM_RAM) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y; const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT]; @@ -616,10 +616,10 @@ void write_asc(const Context *ctx, std::ostream &out) if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) { set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true); } - bool negclk_r = get_param_or_def(ctx, cell.second.get(), ctx->id("NEG_CLK_R")); - bool negclk_w = get_param_or_def(ctx, cell.second.get(), ctx->id("NEG_CLK_W")); - int write_mode = get_param_or_def(ctx, cell.second.get(), ctx->id("WRITE_MODE")); - int read_mode = get_param_or_def(ctx, cell.second.get(), ctx->id("READ_MODE")); + bool negclk_r = get_param_or_def(ctx, cell.second.get(), id_NEG_CLK_R); + bool negclk_w = get_param_or_def(ctx, cell.second.get(), id_NEG_CLK_W); + int write_mode = get_param_or_def(ctx, cell.second.get(), id_WRITE_MODE); + int read_mode = get_param_or_def(ctx, cell.second.get(), id_READ_MODE); set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w); set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r); @@ -627,27 +627,27 @@ void write_asc(const Context *ctx, std::ostream &out) set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2); set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1); set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2); - } else if (cell.second->type == ctx->id("SB_LED_DRV_CUR")) { + } else if (cell.second->type == id_SB_LED_DRV_CUR) { set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "LED_DRV_CUR_EN", true, "IpConfig."); - } else if (cell.second->type == ctx->id("SB_RGB_DRV")) { + } else if (cell.second->type == id_SB_RGB_DRV) { const std::vector> rgb_params = { {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}}; configure_extra_cell(config, ctx, cell.second.get(), rgb_params, true, std::string("IpConfig.")); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGB_DRV_EN", true, "IpConfig."); - } else if (cell.second->type == ctx->id("SB_RGBA_DRV")) { + } else if (cell.second->type == id_SB_RGBA_DRV) { const std::vector> rgba_params = { {"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}}; configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig.")); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig."); - } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC") || - cell.second->type == ctx->id("SB_LEDDA_IP")) { + } else if (cell.second->type == id_SB_WARMBOOT || cell.second->type == id_ICESTORM_LFOSC || + cell.second->type == id_SB_LEDDA_IP) { // No config needed - } else if (cell.second->type == ctx->id("SB_I2C")) { - bool sda_in_dly = !cell.second->attrs.count(ctx->id("SDA_INPUT_DELAYED")) || - cell.second->attrs[ctx->id("SDA_INPUT_DELAYED")].as_bool(); - bool sda_out_dly = !cell.second->attrs.count(ctx->id("SDA_OUTPUT_DELAYED")) || - cell.second->attrs[ctx->id("SDA_OUTPUT_DELAYED")].as_bool(); + } else if (cell.second->type == id_SB_I2C) { + bool sda_in_dly = !cell.second->attrs.count(id_SDA_INPUT_DELAYED) || + cell.second->attrs[id_SDA_INPUT_DELAYED].as_bool(); + bool sda_out_dly = !cell.second->attrs.count(id_SDA_OUTPUT_DELAYED) || + cell.second->attrs[id_SDA_OUTPUT_DELAYED].as_bool(); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SDA_INPUT_DELAYED", sda_in_dly, "IpConfig."); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SDA_OUTPUT_DELAYED", sda_out_dly, @@ -656,7 +656,7 @@ void write_asc(const Context *ctx, std::ostream &out) "IpConfig."); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "I2C_ENABLE_1", true, "IpConfig."); - } else if (cell.second->type == ctx->id("SB_SPI")) { + } else if (cell.second->type == id_SB_SPI) { set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_0", true, "IpConfig."); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_1", true, @@ -665,7 +665,7 @@ void write_asc(const Context *ctx, std::ostream &out) "IpConfig."); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_3", true, "IpConfig."); - } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) { + } else if (cell.second->type == id_ICESTORM_SPRAM) { const BelInfoPOD &beli = ci.bel_data[bel.index]; int x = beli.x, y = beli.y, z = beli.z; NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K || ctx->args.type == ArchArgs::UP3K); @@ -688,7 +688,7 @@ void write_asc(const Context *ctx, std::ostream &out) NPNR_ASSERT(false); } } - } else if (cell.second->type == ctx->id("ICESTORM_DSP")) { + } else if (cell.second->type == id_ICESTORM_DSP) { const std::vector> mac16_params = {{"C_REG", 1}, {"A_REG", 1}, {"B_REG", 1}, @@ -709,13 +709,13 @@ void write_asc(const Context *ctx, std::ostream &out) {"A_SIGNED", 1}, {"B_SIGNED", 1}}; configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig.")); - } else if (cell.second->type == ctx->id("ICESTORM_HFOSC")) { + } else if (cell.second->type == id_ICESTORM_HFOSC) { std::vector> hfosc_params = {{"CLKHF_DIV", 2}}; if (ctx->args.type != ArchArgs::U4K && ctx->args.type != ArchArgs::U1K && ctx->args.type != ArchArgs::U2K) hfosc_params.push_back(std::pair("TRIM_EN", 1)); configure_extra_cell(config, ctx, cell.second.get(), hfosc_params, true, std::string("IpConfig.")); - } else if (cell.second->type == ctx->id("ICESTORM_PLL")) { + } else if (cell.second->type == id_ICESTORM_PLL) { const std::vector> pll_params = {{"DELAY_ADJMODE_FB", 1}, {"DELAY_ADJMODE_REL", 1}, {"DIVF", 7}, @@ -874,7 +874,7 @@ void write_asc(const Context *ctx, std::ostream &out) // Write RAM init data for (auto &cell : ctx->cells) { if (cell.second->bel != BelId()) { - if (cell.second->type == ctx->id("ICESTORM_RAM")) { + if (cell.second->type == id_ICESTORM_RAM) { const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index]; int x = beli.x, y = beli.y; out << ".ram_data " << x << " " << y << std::endl; @@ -1054,7 +1054,7 @@ bool read_asc(Context *ctx, std::istream &in) isUsed |= carry_set; if (isUsed) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("ICESTORM_LC")); + std::unique_ptr created = create_ice_cell(ctx, id_ICESTORM_LC); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK); @@ -1074,7 +1074,7 @@ bool read_asc(Context *ctx, std::istream &in) isUsed |= neg_trigger; if (isUsed) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("SB_IO")); + std::unique_ptr created = create_ice_cell(ctx, id_SB_IO); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK); @@ -1091,35 +1091,35 @@ bool read_asc(Context *ctx, std::istream &in) if (ctx->checkBelAvail(belpin.bel)) { if (ctx->getBelType(belpin.bel) == id_ICESTORM_LC) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("ICESTORM_LC")); + std::unique_ptr created = create_ice_cell(ctx, id_ICESTORM_LC); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); // TODO: Add port mapping to nets } if (ctx->getBelType(belpin.bel) == id_SB_IO) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("SB_IO")); + std::unique_ptr created = create_ice_cell(ctx, id_SB_IO); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); // TODO: Add port mapping to nets } if (ctx->getBelType(belpin.bel) == id_SB_GB) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("SB_GB")); + std::unique_ptr created = create_ice_cell(ctx, id_SB_GB); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); // TODO: Add port mapping to nets } if (ctx->getBelType(belpin.bel) == id_SB_WARMBOOT) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT")); + std::unique_ptr created = create_ice_cell(ctx, id_SB_WARMBOOT); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); // TODO: Add port mapping to nets } if (ctx->getBelType(belpin.bel) == id_ICESTORM_LFOSC) { - std::unique_ptr created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC")); + std::unique_ptr created = create_ice_cell(ctx, id_ICESTORM_LFOSC); IdString name = created->name; ctx->cells[name] = std::move(created); ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK); diff --git a/ice40/cells.cc b/ice40/cells.cc index b97131a62e..a8d3034708 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -33,61 +33,61 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri name.empty() ? ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++)) : ctx->id(name); auto new_cell = std::make_unique(ctx, name_id, type); - if (type == ctx->id("ICESTORM_LC")) { - new_cell->params[ctx->id("LUT_INIT")] = Property(0, 16); - new_cell->params[ctx->id("NEG_CLK")] = Property::State::S0; - new_cell->params[ctx->id("CARRY_ENABLE")] = Property::State::S0; - new_cell->params[ctx->id("DFF_ENABLE")] = Property::State::S0; - new_cell->params[ctx->id("SET_NORESET")] = Property::State::S0; - new_cell->params[ctx->id("ASYNC_SR")] = Property::State::S0; - new_cell->params[ctx->id("CIN_CONST")] = Property::State::S0; - new_cell->params[ctx->id("CIN_SET")] = Property::State::S0; - - new_cell->addInput(ctx->id("I0")); - new_cell->addInput(ctx->id("I1")); - new_cell->addInput(ctx->id("I2")); - new_cell->addInput(ctx->id("I3")); - new_cell->addInput(ctx->id("CIN")); - - new_cell->addInput(ctx->id("CLK")); - new_cell->addInput(ctx->id("CEN")); - new_cell->addInput(ctx->id("SR")); - - new_cell->addOutput(ctx->id("LO")); - new_cell->addOutput(ctx->id("O")); - new_cell->addOutput(ctx->id("COUT")); - } else if (type == ctx->id("SB_IO")) { - new_cell->params[ctx->id("PIN_TYPE")] = Property(0, 6); - new_cell->params[ctx->id("PULLUP")] = Property::State::S0; - new_cell->params[ctx->id("NEG_TRIGGER")] = Property::State::S0; - new_cell->params[ctx->id("IO_STANDARD")] = Property("SB_LVCMOS"); - - new_cell->addInout(ctx->id("PACKAGE_PIN")); - - new_cell->addInput(ctx->id("LATCH_INPUT_VALUE")); - new_cell->addInput(ctx->id("CLOCK_ENABLE")); - new_cell->addInput(ctx->id("INPUT_CLK")); - new_cell->addInput(ctx->id("OUTPUT_CLK")); - - new_cell->addInput(ctx->id("OUTPUT_ENABLE")); - new_cell->addInput(ctx->id("D_OUT_0")); - new_cell->addInput(ctx->id("D_OUT_1")); - - new_cell->addOutput(ctx->id("D_IN_0")); - new_cell->addOutput(ctx->id("D_IN_1")); - } else if (type == ctx->id("ICESTORM_RAM")) { - new_cell->params[ctx->id("NEG_CLK_W")] = Property::State::S0; - new_cell->params[ctx->id("NEG_CLK_R")] = Property::State::S0; - new_cell->params[ctx->id("WRITE_MODE")] = Property::State::S0; - new_cell->params[ctx->id("READ_MODE")] = Property::State::S0; - - new_cell->addInput(ctx->id("RCLK")); - new_cell->addInput(ctx->id("RCLKE")); - new_cell->addInput(ctx->id("RE")); - - new_cell->addInput(ctx->id("WCLK")); - new_cell->addInput(ctx->id("WCLKE")); - new_cell->addInput(ctx->id("WE")); + if (type == id_ICESTORM_LC) { + new_cell->params[id_LUT_INIT] = Property(0, 16); + new_cell->params[id_NEG_CLK] = Property::State::S0; + new_cell->params[id_CARRY_ENABLE] = Property::State::S0; + new_cell->params[id_DFF_ENABLE] = Property::State::S0; + new_cell->params[id_SET_NORESET] = Property::State::S0; + new_cell->params[id_ASYNC_SR] = Property::State::S0; + new_cell->params[id_CIN_CONST] = Property::State::S0; + new_cell->params[id_CIN_SET] = Property::State::S0; + + new_cell->addInput(id_I0); + new_cell->addInput(id_I1); + new_cell->addInput(id_I2); + new_cell->addInput(id_I3); + new_cell->addInput(id_CIN); + + new_cell->addInput(id_CLK); + new_cell->addInput(id_CEN); + new_cell->addInput(id_SR); + + new_cell->addOutput(id_LO); + new_cell->addOutput(id_O); + new_cell->addOutput(id_COUT); + } else if (type == id_SB_IO) { + new_cell->params[id_PIN_TYPE] = Property(0, 6); + new_cell->params[id_PULLUP] = Property::State::S0; + new_cell->params[id_NEG_TRIGGER] = Property::State::S0; + new_cell->params[id_IO_STANDARD] = Property("SB_LVCMOS"); + + new_cell->addInout(id_PACKAGE_PIN); + + new_cell->addInput(id_LATCH_INPUT_VALUE); + new_cell->addInput(id_CLOCK_ENABLE); + new_cell->addInput(id_INPUT_CLK); + new_cell->addInput(id_OUTPUT_CLK); + + new_cell->addInput(id_OUTPUT_ENABLE); + new_cell->addInput(id_D_OUT_0); + new_cell->addInput(id_D_OUT_1); + + new_cell->addOutput(id_D_IN_0); + new_cell->addOutput(id_D_IN_1); + } else if (type == id_ICESTORM_RAM) { + new_cell->params[id_NEG_CLK_W] = Property::State::S0; + new_cell->params[id_NEG_CLK_R] = Property::State::S0; + new_cell->params[id_WRITE_MODE] = Property::State::S0; + new_cell->params[id_READ_MODE] = Property::State::S0; + + new_cell->addInput(id_RCLK); + new_cell->addInput(id_RCLKE); + new_cell->addInput(id_RE); + + new_cell->addInput(id_WCLK); + new_cell->addInput(id_WCLKE); + new_cell->addInput(id_WE); for (int i = 0; i < 16; i++) { new_cell->addInput(ctx->id("WDATA_" + std::to_string(i))); @@ -99,31 +99,31 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri new_cell->addInput(ctx->id("RADDR_" + std::to_string(i))); new_cell->addInput(ctx->id("WADDR_" + std::to_string(i))); } - } else if (type == ctx->id("ICESTORM_LFOSC")) { - new_cell->addInput(ctx->id("CLKLFEN")); - new_cell->addInput(ctx->id("CLKLFPU")); - new_cell->addOutput(ctx->id("CLKLF")); - new_cell->addOutput(ctx->id("CLKLF_FABRIC")); - } else if (type == ctx->id("ICESTORM_HFOSC")) { - new_cell->params[ctx->id("CLKHF_DIV")] = Property("0b00"); - new_cell->params[ctx->id("TRIM_EN")] = Property("0b0"); - - new_cell->addInput(ctx->id("CLKHFEN")); - new_cell->addInput(ctx->id("CLKHFPU")); - new_cell->addOutput(ctx->id("CLKHF")); - new_cell->addOutput(ctx->id("CLKHF_FABRIC")); + } else if (type == id_ICESTORM_LFOSC) { + new_cell->addInput(id_CLKLFEN); + new_cell->addInput(id_CLKLFPU); + new_cell->addOutput(id_CLKLF); + new_cell->addOutput(id_CLKLF_FABRIC); + } else if (type == id_ICESTORM_HFOSC) { + new_cell->params[id_CLKHF_DIV] = Property("0b00"); + new_cell->params[id_TRIM_EN] = Property("0b0"); + + new_cell->addInput(id_CLKHFEN); + new_cell->addInput(id_CLKHFPU); + new_cell->addOutput(id_CLKHF); + new_cell->addOutput(id_CLKHF_FABRIC); for (int i = 0; i < 10; i++) new_cell->addInput(ctx->id("TRIM" + std::to_string(i))); - } else if (type == ctx->id("SB_GB")) { - new_cell->addInput(ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER")); - new_cell->addOutput(ctx->id("GLOBAL_BUFFER_OUTPUT")); - } else if (type == ctx->id("ICESTORM_SPRAM")) { - new_cell->addInput(ctx->id("WREN")); - new_cell->addInput(ctx->id("CHIPSELECT")); - new_cell->addInput(ctx->id("CLOCK")); - new_cell->addInput(ctx->id("STANDBY")); - new_cell->addInput(ctx->id("SLEEP")); - new_cell->addInput(ctx->id("POWEROFF")); + } else if (type == id_SB_GB) { + new_cell->addInput(id_USER_SIGNAL_TO_GLOBAL_BUFFER); + new_cell->addOutput(id_GLOBAL_BUFFER_OUTPUT); + } else if (type == id_ICESTORM_SPRAM) { + new_cell->addInput(id_WREN); + new_cell->addInput(id_CHIPSELECT); + new_cell->addInput(id_CLOCK); + new_cell->addInput(id_STANDBY); + new_cell->addInput(id_SLEEP); + new_cell->addInput(id_POWEROFF); for (int i = 0; i < 16; i++) { new_cell->addInput(ctx->id("DATAIN_" + std::to_string(i))); @@ -135,196 +135,196 @@ std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::stri for (int i = 0; i < 4; i++) { new_cell->addInput(ctx->id("MASKWREN_" + std::to_string(i))); } - } else if (type == ctx->id("ICESTORM_DSP")) { - new_cell->params[ctx->id("NEG_TRIGGER")] = Property::State::S0; - - new_cell->params[ctx->id("C_REG")] = Property::State::S0; - new_cell->params[ctx->id("A_REG")] = Property::State::S0; - new_cell->params[ctx->id("B_REG")] = Property::State::S0; - new_cell->params[ctx->id("D_REG")] = Property::State::S0; - new_cell->params[ctx->id("TOP_8x8_MULT_REG")] = Property::State::S0; - new_cell->params[ctx->id("BOT_8x8_MULT_REG")] = Property::State::S0; - new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG1")] = Property::State::S0; - new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG2")] = Property::State::S0; - - new_cell->params[ctx->id("TOPOUTPUT_SELECT")] = Property(0, 2); - new_cell->params[ctx->id("TOPADDSUB_LOWERINPUT")] = Property(0, 2); - new_cell->params[ctx->id("TOPADDSUB_UPPERINPUT")] = Property::State::S0; - new_cell->params[ctx->id("TOPADDSUB_CARRYSELECT")] = Property(0, 2); - - new_cell->params[ctx->id("BOTOUTPUT_SELECT")] = Property(0, 2); - new_cell->params[ctx->id("BOTADDSUB_LOWERINPUT")] = Property(0, 2); - new_cell->params[ctx->id("BOTADDSUB_UPPERINPUT")] = Property::State::S0; - new_cell->params[ctx->id("BOTADDSUB_CARRYSELECT")] = Property(0, 2); - - new_cell->params[ctx->id("MODE_8x8")] = Property::State::S0; - new_cell->params[ctx->id("A_SIGNED")] = Property::State::S0; - new_cell->params[ctx->id("B_SIGNED")] = Property::State::S0; - - new_cell->addInput(ctx->id("CLK")); - new_cell->addInput(ctx->id("CE")); + } else if (type == id_ICESTORM_DSP) { + new_cell->params[id_NEG_TRIGGER] = Property::State::S0; + + new_cell->params[id_C_REG] = Property::State::S0; + new_cell->params[id_A_REG] = Property::State::S0; + new_cell->params[id_B_REG] = Property::State::S0; + new_cell->params[id_D_REG] = Property::State::S0; + new_cell->params[id_TOP_8x8_MULT_REG] = Property::State::S0; + new_cell->params[id_BOT_8x8_MULT_REG] = Property::State::S0; + new_cell->params[id_PIPELINE_16x16_MULT_REG1] = Property::State::S0; + new_cell->params[id_PIPELINE_16x16_MULT_REG2] = Property::State::S0; + + new_cell->params[id_TOPOUTPUT_SELECT] = Property(0, 2); + new_cell->params[id_TOPADDSUB_LOWERINPUT] = Property(0, 2); + new_cell->params[id_TOPADDSUB_UPPERINPUT] = Property::State::S0; + new_cell->params[id_TOPADDSUB_CARRYSELECT] = Property(0, 2); + + new_cell->params[id_BOTOUTPUT_SELECT] = Property(0, 2); + new_cell->params[id_BOTADDSUB_LOWERINPUT] = Property(0, 2); + new_cell->params[id_BOTADDSUB_UPPERINPUT] = Property::State::S0; + new_cell->params[id_BOTADDSUB_CARRYSELECT] = Property(0, 2); + + new_cell->params[id_MODE_8x8] = Property::State::S0; + new_cell->params[id_A_SIGNED] = Property::State::S0; + new_cell->params[id_B_SIGNED] = Property::State::S0; + + new_cell->addInput(id_CLK); + new_cell->addInput(id_CE); for (int i = 0; i < 16; i++) { new_cell->addInput(ctx->id("C_" + std::to_string(i))); new_cell->addInput(ctx->id("A_" + std::to_string(i))); new_cell->addInput(ctx->id("B_" + std::to_string(i))); new_cell->addInput(ctx->id("D_" + std::to_string(i))); } - new_cell->addInput(ctx->id("AHOLD")); - new_cell->addInput(ctx->id("BHOLD")); - new_cell->addInput(ctx->id("CHOLD")); - new_cell->addInput(ctx->id("DHOLD")); + new_cell->addInput(id_AHOLD); + new_cell->addInput(id_BHOLD); + new_cell->addInput(id_CHOLD); + new_cell->addInput(id_DHOLD); - new_cell->addInput(ctx->id("IRSTTOP")); - new_cell->addInput(ctx->id("IRSTBOT")); - new_cell->addInput(ctx->id("ORSTTOP")); - new_cell->addInput(ctx->id("ORSTBOT")); + new_cell->addInput(id_IRSTTOP); + new_cell->addInput(id_IRSTBOT); + new_cell->addInput(id_ORSTTOP); + new_cell->addInput(id_ORSTBOT); - new_cell->addInput(ctx->id("OLOADTOP")); - new_cell->addInput(ctx->id("OLOADBOT")); + new_cell->addInput(id_OLOADTOP); + new_cell->addInput(id_OLOADBOT); - new_cell->addInput(ctx->id("ADDSUBTOP")); - new_cell->addInput(ctx->id("ADDSUBBOT")); + new_cell->addInput(id_ADDSUBTOP); + new_cell->addInput(id_ADDSUBBOT); - new_cell->addInput(ctx->id("OHOLDTOP")); - new_cell->addInput(ctx->id("OHOLDBOT")); + new_cell->addInput(id_OHOLDTOP); + new_cell->addInput(id_OHOLDBOT); - new_cell->addInput(ctx->id("CI")); - new_cell->addInput(ctx->id("ACCUMCI")); - new_cell->addInput(ctx->id("SIGNEXTIN")); + new_cell->addInput(id_CI); + new_cell->addInput(id_ACCUMCI); + new_cell->addInput(id_SIGNEXTIN); for (int i = 0; i < 32; i++) { new_cell->addOutput(ctx->id("O_" + std::to_string(i))); } - new_cell->addOutput(ctx->id("CO")); - new_cell->addOutput(ctx->id("ACCUMCO")); - new_cell->addOutput(ctx->id("SIGNEXTOUT")); + new_cell->addOutput(id_CO); + new_cell->addOutput(id_ACCUMCO); + new_cell->addOutput(id_SIGNEXTOUT); - } else if (type == ctx->id("ICESTORM_PLL")) { - new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = Property::State::S0; - new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = Property::State::S0; + } else if (type == id_ICESTORM_PLL) { + new_cell->params[id_DELAY_ADJMODE_FB] = Property::State::S0; + new_cell->params[id_DELAY_ADJMODE_REL] = Property::State::S0; - new_cell->params[ctx->id("DIVF")] = Property(0, 7); - new_cell->params[ctx->id("DIVQ")] = Property(0, 3); - new_cell->params[ctx->id("DIVR")] = Property(0, 4); + new_cell->params[id_DIVF] = Property(0, 7); + new_cell->params[id_DIVQ] = Property(0, 3); + new_cell->params[id_DIVR] = Property(0, 4); - new_cell->params[ctx->id("FDA_FEEDBACK")] = Property(0, 4); - new_cell->params[ctx->id("FDA_RELATIVE")] = Property(0, 4); - new_cell->params[ctx->id("FEEDBACK_PATH")] = Property(1, 3); - new_cell->params[ctx->id("FILTER_RANGE")] = Property(0, 3); + new_cell->params[id_FDA_FEEDBACK] = Property(0, 4); + new_cell->params[id_FDA_RELATIVE] = Property(0, 4); + new_cell->params[id_FEEDBACK_PATH] = Property(1, 3); + new_cell->params[id_FILTER_RANGE] = Property(0, 3); - new_cell->params[ctx->id("PLLOUT_SELECT_A")] = Property(0, 2); - new_cell->params[ctx->id("PLLOUT_SELECT_B")] = Property(0, 2); + new_cell->params[id_PLLOUT_SELECT_A] = Property(0, 2); + new_cell->params[id_PLLOUT_SELECT_B] = Property(0, 2); - new_cell->params[ctx->id("PLLTYPE")] = Property(0, 3); - new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = Property::State::S0; - new_cell->params[ctx->id("TEST_MODE")] = Property::State::S0; + new_cell->params[id_PLLTYPE] = Property(0, 3); + new_cell->params[id_SHIFTREG_DIVMODE] = Property::State::S0; + new_cell->params[id_TEST_MODE] = Property::State::S0; - new_cell->addInput(ctx->id("BYPASS")); + new_cell->addInput(id_BYPASS); for (int i = 0; i < 8; i++) new_cell->addInput(ctx->id("DYNAMICDELAY_" + std::to_string(i))); - new_cell->addInput(ctx->id("EXTFEEDBACK")); - new_cell->addInput(ctx->id("LATCHINPUTVALUE")); - new_cell->addInput(ctx->id("REFERENCECLK")); - new_cell->addInput(ctx->id("RESETB")); - - new_cell->addInput(ctx->id("SCLK")); - new_cell->addInput(ctx->id("SDI")); - new_cell->addOutput(ctx->id("SDO")); - - new_cell->addOutput(ctx->id("LOCK")); - new_cell->addOutput(ctx->id("PLLOUT_A")); - new_cell->addOutput(ctx->id("PLLOUT_B")); - new_cell->addOutput(ctx->id("PLLOUT_A_GLOBAL")); - new_cell->addOutput(ctx->id("PLLOUT_B_GLOBAL")); - } else if (type == ctx->id("SB_RGBA_DRV")) { - new_cell->params[ctx->id("CURRENT_MODE")] = std::string("0b0"); - new_cell->params[ctx->id("RGB0_CURRENT")] = std::string("0b000000"); - new_cell->params[ctx->id("RGB1_CURRENT")] = std::string("0b000000"); - new_cell->params[ctx->id("RGB2_CURRENT")] = std::string("0b000000"); - - new_cell->addInput(ctx->id("CURREN")); - new_cell->addInput(ctx->id("RGBLEDEN")); - new_cell->addInput(ctx->id("RGB0PWM")); - new_cell->addInput(ctx->id("RGB1PWM")); - new_cell->addInput(ctx->id("RGB2PWM")); - new_cell->addOutput(ctx->id("RGB0")); - new_cell->addOutput(ctx->id("RGB1")); - new_cell->addOutput(ctx->id("RGB2")); - } else if (type == ctx->id("SB_LED_DRV_CUR")) { - new_cell->addInput(ctx->id("EN")); - new_cell->addOutput(ctx->id("LEDPU")); - } else if (type == ctx->id("SB_RGB_DRV")) { - new_cell->params[ctx->id("RGB0_CURRENT")] = std::string("0b000000"); - new_cell->params[ctx->id("RGB1_CURRENT")] = std::string("0b000000"); - new_cell->params[ctx->id("RGB2_CURRENT")] = std::string("0b000000"); - - new_cell->addInput(ctx->id("RGBPU")); - new_cell->addInput(ctx->id("RGBLEDEN")); - new_cell->addInput(ctx->id("RGB0PWM")); - new_cell->addInput(ctx->id("RGB1PWM")); - new_cell->addInput(ctx->id("RGB2PWM")); - new_cell->addOutput(ctx->id("RGB0")); - new_cell->addOutput(ctx->id("RGB1")); - new_cell->addOutput(ctx->id("RGB2")); - } else if (type == ctx->id("SB_LEDDA_IP")) { - new_cell->addInput(ctx->id("LEDDCS")); - new_cell->addInput(ctx->id("LEDDCLK")); + new_cell->addInput(id_EXTFEEDBACK); + new_cell->addInput(id_LATCHINPUTVALUE); + new_cell->addInput(id_REFERENCECLK); + new_cell->addInput(id_RESETB); + + new_cell->addInput(id_SCLK); + new_cell->addInput(id_SDI); + new_cell->addOutput(id_SDO); + + new_cell->addOutput(id_LOCK); + new_cell->addOutput(id_PLLOUT_A); + new_cell->addOutput(id_PLLOUT_B); + new_cell->addOutput(id_PLLOUT_A_GLOBAL); + new_cell->addOutput(id_PLLOUT_B_GLOBAL); + } else if (type == id_SB_RGBA_DRV) { + new_cell->params[id_CURRENT_MODE] = std::string("0b0"); + new_cell->params[id_RGB0_CURRENT] = std::string("0b000000"); + new_cell->params[id_RGB1_CURRENT] = std::string("0b000000"); + new_cell->params[id_RGB2_CURRENT] = std::string("0b000000"); + + new_cell->addInput(id_CURREN); + new_cell->addInput(id_RGBLEDEN); + new_cell->addInput(id_RGB0PWM); + new_cell->addInput(id_RGB1PWM); + new_cell->addInput(id_RGB2PWM); + new_cell->addOutput(id_RGB0); + new_cell->addOutput(id_RGB1); + new_cell->addOutput(id_RGB2); + } else if (type == id_SB_LED_DRV_CUR) { + new_cell->addInput(id_EN); + new_cell->addOutput(id_LEDPU); + } else if (type == id_SB_RGB_DRV) { + new_cell->params[id_RGB0_CURRENT] = std::string("0b000000"); + new_cell->params[id_RGB1_CURRENT] = std::string("0b000000"); + new_cell->params[id_RGB2_CURRENT] = std::string("0b000000"); + + new_cell->addInput(id_RGBPU); + new_cell->addInput(id_RGBLEDEN); + new_cell->addInput(id_RGB0PWM); + new_cell->addInput(id_RGB1PWM); + new_cell->addInput(id_RGB2PWM); + new_cell->addOutput(id_RGB0); + new_cell->addOutput(id_RGB1); + new_cell->addOutput(id_RGB2); + } else if (type == id_SB_LEDDA_IP) { + new_cell->addInput(id_LEDDCS); + new_cell->addInput(id_LEDDCLK); for (int i = 0; i < 8; i++) new_cell->addInput(ctx->id("LEDDDAT" + std::to_string(i))); for (int i = 0; i < 3; i++) new_cell->addInput(ctx->id("LEDDADDR" + std::to_string(i))); - new_cell->addInput(ctx->id("LEDDDEN")); - new_cell->addInput(ctx->id("LEDDEXE")); - new_cell->addInput(ctx->id("LEDDRST")); // doesn't actually exist, for icecube code compatibility - // only - new_cell->addOutput(ctx->id("PWMOUT0")); - new_cell->addOutput(ctx->id("PWMOUT1")); - new_cell->addOutput(ctx->id("PWMOUT2")); - new_cell->addOutput(ctx->id("LEDDON")); - } else if (type == ctx->id("SB_I2C")) { - new_cell->params[ctx->id("I2C_SLAVE_INIT_ADDR")] = std::string("0b1111100001"); - new_cell->params[ctx->id("BUS_ADDR74")] = std::string("0b0001"); + new_cell->addInput(id_LEDDDEN); + new_cell->addInput(id_LEDDEXE); + new_cell->addInput(id_LEDDRST); // doesn't actually exist, for icecube code compatibility + // only + new_cell->addOutput(id_PWMOUT0); + new_cell->addOutput(id_PWMOUT1); + new_cell->addOutput(id_PWMOUT2); + new_cell->addOutput(id_LEDDON); + } else if (type == id_SB_I2C) { + new_cell->params[id_I2C_SLAVE_INIT_ADDR] = std::string("0b1111100001"); + new_cell->params[id_BUS_ADDR74] = std::string("0b0001"); for (int i = 0; i < 8; i++) { new_cell->addInput(ctx->id("SBADRI" + std::to_string(i))); new_cell->addInput(ctx->id("SBDATI" + std::to_string(i))); new_cell->addOutput(ctx->id("SBDATO" + std::to_string(i))); } - new_cell->addInput(ctx->id("SBCLKI")); - new_cell->addInput(ctx->id("SBRWI")); - new_cell->addInput(ctx->id("SBSTBI")); - new_cell->addInput(ctx->id("SCLI")); - new_cell->addInput(ctx->id("SDAI")); - new_cell->addOutput(ctx->id("SBACKO")); - new_cell->addOutput(ctx->id("I2CIRQ")); - new_cell->addOutput(ctx->id("I2CWKUP")); - new_cell->addOutput(ctx->id("SCLO")); - new_cell->addOutput(ctx->id("SCLOE")); - new_cell->addOutput(ctx->id("SDAO")); - new_cell->addOutput(ctx->id("SDAOE")); - } else if (type == ctx->id("SB_SPI")) { - new_cell->params[ctx->id("BUS_ADDR74")] = std::string("0b0000"); + new_cell->addInput(id_SBCLKI); + new_cell->addInput(id_SBRWI); + new_cell->addInput(id_SBSTBI); + new_cell->addInput(id_SCLI); + new_cell->addInput(id_SDAI); + new_cell->addOutput(id_SBACKO); + new_cell->addOutput(id_I2CIRQ); + new_cell->addOutput(id_I2CWKUP); + new_cell->addOutput(id_SCLO); + new_cell->addOutput(id_SCLOE); + new_cell->addOutput(id_SDAO); + new_cell->addOutput(id_SDAOE); + } else if (type == id_SB_SPI) { + new_cell->params[id_BUS_ADDR74] = std::string("0b0000"); for (int i = 0; i < 8; i++) { new_cell->addInput(ctx->id("SBADRI" + std::to_string(i))); new_cell->addInput(ctx->id("SBDATI" + std::to_string(i))); new_cell->addOutput(ctx->id("SBDATO" + std::to_string(i))); } - new_cell->addInput(ctx->id("SBCLKI")); - new_cell->addInput(ctx->id("SBRWI")); - new_cell->addInput(ctx->id("SBSTBI")); - new_cell->addInput(ctx->id("MI")); - new_cell->addInput(ctx->id("SI")); - new_cell->addInput(ctx->id("SCKI")); - new_cell->addInput(ctx->id("SCSNI")); - new_cell->addOutput(ctx->id("SBACKO")); - new_cell->addOutput(ctx->id("SPIIRQ")); - new_cell->addOutput(ctx->id("SPIWKUP")); - new_cell->addOutput(ctx->id("SO")); - new_cell->addOutput(ctx->id("SOE")); - new_cell->addOutput(ctx->id("MO")); - new_cell->addOutput(ctx->id("MOE")); - new_cell->addOutput(ctx->id("SCKO")); - new_cell->addOutput(ctx->id("SCKOE")); + new_cell->addInput(id_SBCLKI); + new_cell->addInput(id_SBRWI); + new_cell->addInput(id_SBSTBI); + new_cell->addInput(id_MI); + new_cell->addInput(id_SI); + new_cell->addInput(id_SCKI); + new_cell->addInput(id_SCSNI); + new_cell->addOutput(id_SBACKO); + new_cell->addOutput(id_SPIIRQ); + new_cell->addOutput(id_SPIWKUP); + new_cell->addOutput(id_SO); + new_cell->addOutput(id_SOE); + new_cell->addOutput(id_MO); + new_cell->addOutput(id_MOE); + new_cell->addOutput(id_SCKO); + new_cell->addOutput(id_SCKOE); for (int i = 0; i < 4; i++) { new_cell->addOutput(ctx->id("MCSNO" + std::to_string(i))); new_cell->addOutput(ctx->id("MCSNOE" + std::to_string(i))); @@ -339,14 +339,14 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) { if (lc->hierpath == IdString()) lc->hierpath = lut->hierpath; - lc->params[ctx->id("LUT_INIT")] = lut->params[ctx->id("LUT_INIT")].extract(0, 16, Property::State::S0); - replace_port(lut, ctx->id("I0"), lc, ctx->id("I0")); - replace_port(lut, ctx->id("I1"), lc, ctx->id("I1")); - replace_port(lut, ctx->id("I2"), lc, ctx->id("I2")); - replace_port(lut, ctx->id("I3"), lc, ctx->id("I3")); + lc->params[id_LUT_INIT] = lut->params[id_LUT_INIT].extract(0, 16, Property::State::S0); + replace_port(lut, id_I0, lc, id_I0); + replace_port(lut, id_I1, lc, id_I1); + replace_port(lut, id_I2, lc, id_I2); + replace_port(lut, id_I3, lc, id_I3); if (no_dff) { - replace_port(lut, ctx->id("O"), lc, ctx->id("O")); - lc->params[ctx->id("DFF_ENABLE")] = Property::State::S0; + replace_port(lut, id_O, lc, id_O); + lc->params[id_DFF_ENABLE] = Property::State::S0; } } @@ -354,20 +354,20 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l { if (lc->hierpath == IdString()) lc->hierpath = dff->hierpath; - lc->params[ctx->id("DFF_ENABLE")] = Property::State::S1; + lc->params[id_DFF_ENABLE] = Property::State::S1; std::string config = dff->type.str(ctx).substr(6); auto citer = config.begin(); - replace_port(dff, ctx->id("C"), lc, ctx->id("CLK")); + replace_port(dff, id_C, lc, id_CLK); if (citer != config.end() && *citer == 'N') { - lc->params[ctx->id("NEG_CLK")] = Property::State::S1; + lc->params[id_NEG_CLK] = Property::State::S1; ++citer; } else { - lc->params[ctx->id("NEG_CLK")] = Property::State::S0; + lc->params[id_NEG_CLK] = Property::State::S0; } if (citer != config.end() && *citer == 'E') { - replace_port(dff, ctx->id("E"), lc, ctx->id("CEN")); + replace_port(dff, id_E, lc, id_CEN); ++citer; } @@ -375,60 +375,60 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l if ((config.end() - citer) >= 2) { char c = *(citer++); NPNR_ASSERT(c == 'S'); - lc->params[ctx->id("ASYNC_SR")] = Property::State::S0; + lc->params[id_ASYNC_SR] = Property::State::S0; } else { - lc->params[ctx->id("ASYNC_SR")] = Property::State::S1; + lc->params[id_ASYNC_SR] = Property::State::S1; } if (*citer == 'S') { citer++; - replace_port(dff, ctx->id("S"), lc, ctx->id("SR")); - lc->params[ctx->id("SET_NORESET")] = Property::State::S1; + replace_port(dff, id_S, lc, id_SR); + lc->params[id_SET_NORESET] = Property::State::S1; } else { NPNR_ASSERT(*citer == 'R'); citer++; - replace_port(dff, ctx->id("R"), lc, ctx->id("SR")); - lc->params[ctx->id("SET_NORESET")] = Property::State::S0; + replace_port(dff, id_R, lc, id_SR); + lc->params[id_SET_NORESET] = Property::State::S0; } } NPNR_ASSERT(citer == config.end()); if (pass_thru_lut) { - lc->params[ctx->id("LUT_INIT")] = Property(2, 16); - replace_port(dff, ctx->id("D"), lc, ctx->id("I0")); + lc->params[id_LUT_INIT] = Property(2, 16); + replace_port(dff, id_D, lc, id_I0); } - replace_port(dff, ctx->id("Q"), lc, ctx->id("O")); + replace_port(dff, id_Q, lc, id_O); } void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &todelete_cells) { if (nxio->type == ctx->id("$nextpnr_ibuf")) { - sbio->params[ctx->id("PIN_TYPE")] = 1; - auto pu_attr = nxio->attrs.find(ctx->id("PULLUP")); + sbio->params[id_PIN_TYPE] = 1; + auto pu_attr = nxio->attrs.find(id_PULLUP); if (pu_attr != nxio->attrs.end()) - sbio->params[ctx->id("PULLUP")] = pu_attr->second; - replace_port(nxio, ctx->id("O"), sbio, ctx->id("D_IN_0")); + sbio->params[id_PULLUP] = pu_attr->second; + replace_port(nxio, id_O, sbio, id_D_IN_0); } else if (nxio->type == ctx->id("$nextpnr_obuf")) { - sbio->params[ctx->id("PIN_TYPE")] = 25; - replace_port(nxio, ctx->id("I"), sbio, ctx->id("D_OUT_0")); + sbio->params[id_PIN_TYPE] = 25; + replace_port(nxio, id_I, sbio, id_D_OUT_0); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below - NetInfo *i = get_net_or_empty(nxio, ctx->id("I")); + NetInfo *i = get_net_or_empty(nxio, id_I); if (i == nullptr || i->driver.cell == nullptr) - sbio->params[ctx->id("PIN_TYPE")] = 1; + sbio->params[id_PIN_TYPE] = 1; else - sbio->params[ctx->id("PIN_TYPE")] = 25; - auto pu_attr = nxio->attrs.find(ctx->id("PULLUP")); + sbio->params[id_PIN_TYPE] = 25; + auto pu_attr = nxio->attrs.find(id_PULLUP); if (pu_attr != nxio->attrs.end()) - sbio->params[ctx->id("PULLUP")] = pu_attr->second; - replace_port(nxio, ctx->id("I"), sbio, ctx->id("D_OUT_0")); - replace_port(nxio, ctx->id("O"), sbio, ctx->id("D_IN_0")); + sbio->params[id_PULLUP] = pu_attr->second; + replace_port(nxio, id_I, sbio, id_D_OUT_0); + replace_port(nxio, id_O, sbio, id_D_IN_0); } else { NPNR_ASSERT(false); } - NetInfo *donet = sbio->ports.at(ctx->id("D_OUT_0")).net, *dinet = sbio->ports.at(ctx->id("D_IN_0")).net; + NetInfo *donet = sbio->ports.at(id_D_OUT_0).net, *dinet = sbio->ports.at(id_D_IN_0).net; // Rename I/O nets to avoid conflicts if (donet != nullptr && donet->name == nxio->name) @@ -451,17 +451,17 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to NPNR_ASSERT(!ctx->nets.count(tn_netname)); ctx->net_aliases.erase(tn_netname); NetInfo *toplevel_net = ctx->createNet(tn_netname); - connect_port(ctx, toplevel_net, sbio, ctx->id("PACKAGE_PIN")); + connect_port(ctx, toplevel_net, sbio, id_PACKAGE_PIN); ctx->ports[nxio->name].net = toplevel_net; } CellInfo *tbuf = net_driven_by( ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); }, - ctx->id("Y")); + id_Y); if (tbuf) { - sbio->params[ctx->id("PIN_TYPE")] = 41; - replace_port(tbuf, ctx->id("A"), sbio, ctx->id("D_OUT_0")); - replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE")); + sbio->params[id_PIN_TYPE] = 41; + replace_port(tbuf, id_A, sbio, id_D_OUT_0); + replace_port(tbuf, id_E, sbio, id_OUTPUT_ENABLE); if (donet->users.size() > 1) { for (auto user : donet->users) @@ -477,15 +477,15 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell) { - if (cell->type == ctx->id("SB_PLL40_PAD")) + if (cell->type == id_SB_PLL40_PAD) return 2; - if (cell->type == ctx->id("SB_PLL40_2_PAD")) + if (cell->type == id_SB_PLL40_2_PAD) return 4; - if (cell->type == ctx->id("SB_PLL40_2F_PAD")) + if (cell->type == id_SB_PLL40_2F_PAD) return 6; - if (cell->type == ctx->id("SB_PLL40_CORE")) + if (cell->type == id_SB_PLL40_CORE) return 3; - if (cell->type == ctx->id("SB_PLL40_2F_CORE")) + if (cell->type == id_SB_PLL40_2F_CORE) return 7; NPNR_ASSERT(0); } @@ -495,15 +495,14 @@ bool is_clock_port(const BaseCtx *ctx, const PortRef &port) if (port.cell == nullptr) return false; if (is_ff(ctx, port.cell)) - return port.port == ctx->id("C"); - if (port.cell->type == ctx->id("ICESTORM_LC")) - return port.port == ctx->id("CLK"); - if (is_ram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_RAM")) - return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK") || port.port == ctx->id("RCLKN") || - port.port == ctx->id("WCLKN"); - if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP")) - return port.port == ctx->id("CLK"); - if (is_sb_spram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_SPRAM")) + return port.port == id_C; + if (port.cell->type == id_ICESTORM_LC) + return port.port == id_CLK; + if (is_ram(ctx, port.cell) || port.cell->type == id_ICESTORM_RAM) + return port.port == id_RCLK || port.port == id_WCLK || port.port == id_RCLKN || port.port == id_WCLKN; + if (is_sb_mac16(ctx, port.cell) || port.cell->type == id_ICESTORM_DSP) + return port.port == id_CLK; + if (is_sb_spram(ctx, port.cell) || port.cell->type == id_ICESTORM_SPRAM) return port.port == id_CLOCK; if (is_sb_io(ctx, port.cell)) return port.port == id_INPUT_CLK || port.port == id_OUTPUT_CLK; @@ -515,12 +514,11 @@ bool is_reset_port(const BaseCtx *ctx, const PortRef &port) if (port.cell == nullptr) return false; if (is_ff(ctx, port.cell)) - return port.port == ctx->id("R") || port.port == ctx->id("S"); - if (port.cell->type == ctx->id("ICESTORM_LC")) - return port.port == ctx->id("SR"); - if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP")) - return port.port == ctx->id("IRSTTOP") || port.port == ctx->id("IRSTBOT") || port.port == ctx->id("ORSTTOP") || - port.port == ctx->id("ORSTBOT"); + return port.port == id_R || port.port == id_S; + if (port.cell->type == id_ICESTORM_LC) + return port.port == id_SR; + if (is_sb_mac16(ctx, port.cell) || port.cell->type == id_ICESTORM_DSP) + return port.port == id_IRSTTOP || port.port == id_IRSTBOT || port.port == id_ORSTTOP || port.port == id_ORSTBOT; return false; } @@ -529,12 +527,12 @@ bool is_enable_port(const BaseCtx *ctx, const PortRef &port) if (port.cell == nullptr) return false; if (is_ff(ctx, port.cell)) - return port.port == ctx->id("E"); - if (port.cell->type == ctx->id("ICESTORM_LC")) - return port.port == ctx->id("CEN"); + return port.port == id_E; + if (port.cell->type == id_ICESTORM_LC) + return port.port == id_CEN; // FIXME - // if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP")) - // return port.port == ctx->id("CE"); + // if (is_sb_mac16(ctx, port.cell) || port.cell->type == id_ICESTORM_DSP) + // return port.port == id_CE; return false; } diff --git a/ice40/cells.h b/ice40/cells.h index 30fdd63167..724500d66c 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -30,90 +30,80 @@ NEXTPNR_NAMESPACE_BEGIN std::unique_ptr create_ice_cell(Context *ctx, IdString type, std::string name = ""); // Return true if a cell is a LUT -inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LUT4"); } +inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_LUT4; } // Return true if a cell is a flipflop inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("SB_DFF") || cell->type == ctx->id("SB_DFFE") || cell->type == ctx->id("SB_DFFSR") || - cell->type == ctx->id("SB_DFFR") || cell->type == ctx->id("SB_DFFSS") || cell->type == ctx->id("SB_DFFS") || - cell->type == ctx->id("SB_DFFESR") || cell->type == ctx->id("SB_DFFER") || - cell->type == ctx->id("SB_DFFESS") || cell->type == ctx->id("SB_DFFES") || - cell->type == ctx->id("SB_DFFN") || cell->type == ctx->id("SB_DFFNE") || - cell->type == ctx->id("SB_DFFNSR") || cell->type == ctx->id("SB_DFFNR") || - cell->type == ctx->id("SB_DFFNSS") || cell->type == ctx->id("SB_DFFNS") || - cell->type == ctx->id("SB_DFFNESR") || cell->type == ctx->id("SB_DFFNER") || - cell->type == ctx->id("SB_DFFNESS") || cell->type == ctx->id("SB_DFFNES"); + return cell->type == id_SB_DFF || cell->type == id_SB_DFFE || cell->type == id_SB_DFFSR || + cell->type == id_SB_DFFR || cell->type == id_SB_DFFSS || cell->type == id_SB_DFFS || + cell->type == id_SB_DFFESR || cell->type == id_SB_DFFER || cell->type == id_SB_DFFESS || + cell->type == id_SB_DFFES || cell->type == id_SB_DFFN || cell->type == id_SB_DFFNE || + cell->type == id_SB_DFFNSR || cell->type == id_SB_DFFNR || cell->type == id_SB_DFFNSS || + cell->type == id_SB_DFFNS || cell->type == id_SB_DFFNESR || cell->type == id_SB_DFFNER || + cell->type == id_SB_DFFNESS || cell->type == id_SB_DFFNES; } -inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_CARRY"); } +inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_CARRY; } -inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("ICESTORM_LC"); } +inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_ICESTORM_LC; } // Return true if a cell is a SB_IO -inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); } +inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_IO; } // Return true if a cell is a SB_GB_IO -inline bool is_sb_gb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB_IO"); } +inline bool is_sb_gb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_GB_IO; } // Return true if a cell is a global buffer -inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB"); } +inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_GB; } // Return true if a cell is a RAM inline bool is_ram(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("SB_RAM40_4K") || cell->type == ctx->id("SB_RAM40_4KNR") || - cell->type == ctx->id("SB_RAM40_4KNW") || cell->type == ctx->id("SB_RAM40_4KNRNW"); + return cell->type == id_SB_RAM40_4K || cell->type == id_SB_RAM40_4KNR || cell->type == id_SB_RAM40_4KNW || + cell->type == id_SB_RAM40_4KNRNW; } -inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LFOSC"); } +inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_LFOSC; } -inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_HFOSC"); } +inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_HFOSC; } -inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); } +inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_SPRAM256KA; } -inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); } +inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_MAC16; } -inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); } +inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_RGBA_DRV; } -inline bool is_sb_rgb_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGB_DRV"); } +inline bool is_sb_rgb_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_RGB_DRV; } -inline bool is_sb_led_drv_cur(const BaseCtx *ctx, const CellInfo *cell) -{ - return cell->type == ctx->id("SB_LED_DRV_CUR"); -} +inline bool is_sb_led_drv_cur(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_LED_DRV_CUR; } -inline bool is_sb_ledda_ip(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LEDDA_IP"); } +inline bool is_sb_ledda_ip(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_LEDDA_IP; } -inline bool is_sb_i2c(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_I2C"); } +inline bool is_sb_i2c(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_I2C; } -inline bool is_sb_spi(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPI"); } +inline bool is_sb_spi(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_SPI; } inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") || - cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") || - cell->type == ctx->id("SB_PLL40_2F_CORE"); + return cell->type == id_SB_PLL40_PAD || cell->type == id_SB_PLL40_2_PAD || cell->type == id_SB_PLL40_2F_PAD || + cell->type == id_SB_PLL40_CORE || cell->type == id_SB_PLL40_2F_CORE; } inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") || - cell->type == ctx->id("SB_PLL40_2F_PAD") || - (cell->type == ctx->id("ICESTORM_PLL") && - (cell->attrs.at(ctx->id("TYPE")).as_string() == "SB_PLL40_PAD" || - cell->attrs.at(ctx->id("TYPE")).as_string() == "SB_PLL40_2_PAD" || - cell->attrs.at(ctx->id("TYPE")).as_string() == "SB_PLL40_2F_PAD")); + return cell->type == id_SB_PLL40_PAD || cell->type == id_SB_PLL40_2_PAD || cell->type == id_SB_PLL40_2F_PAD || + (cell->type == id_ICESTORM_PLL && (cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_PAD" || + cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2_PAD" || + cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2F_PAD")); } inline bool is_sb_pll40_dual(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == ctx->id("SB_PLL40_2_PAD") || cell->type == ctx->id("SB_PLL40_2F_PAD") || - cell->type == ctx->id("SB_PLL40_2F_CORE") || - (cell->type == ctx->id("ICESTORM_PLL") && - (cell->attrs.at(ctx->id("TYPE")).as_string() == "SB_PLL40_2_PAD" || - cell->attrs.at(ctx->id("TYPE")).as_string() == "SB_PLL40_2F_PAD" || - cell->attrs.at(ctx->id("TYPE")).as_string() == "SB_PLL40_2F_CORE")); + return cell->type == id_SB_PLL40_2_PAD || cell->type == id_SB_PLL40_2F_PAD || cell->type == id_SB_PLL40_2F_CORE || + (cell->type == id_ICESTORM_PLL && (cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2_PAD" || + cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2F_PAD" || + cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2F_CORE")); } uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell); diff --git a/ice40/chains.cc b/ice40/chains.cc index fa4ce413cf..e6a37044fa 100644 --- a/ice40/chains.cc +++ b/ice40/chains.cc @@ -53,9 +53,9 @@ class ChainConstrainer tile.clear(); chains.emplace_back(); start_of_chain = false; - if (cell->ports.at(ctx->id("CIN")).net) { + if (cell->ports.at(id_CIN).net) { // CIN is not constant and not part of a chain. Must feed in from fabric - CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN"))); + CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(id_CIN)); chains.back().cells.push_back(feedin); tile.push_back(feedin); ++feedio_lcs; @@ -66,18 +66,18 @@ class ChainConstrainer bool split_chain = (!ctx->logic_cells_compatible(tile.data(), tile.size())) || (int(chains.back().cells.size()) > max_length); if (split_chain) { - CellInfo *passout = make_carry_pass_out((*(curr_cell - 1))->ports.at(ctx->id("COUT"))); + CellInfo *passout = make_carry_pass_out((*(curr_cell - 1))->ports.at(id_COUT)); tile.pop_back(); chains.back().cells.back() = passout; start_of_chain = true; } else { - NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net; + NetInfo *carry_net = cell->ports.at(id_COUT).net; bool at_end = (curr_cell == carryc.cells.end() - 1); if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) { if (carry_net->users.size() > 2 || - (net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) != - net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) || - (at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) { + (net_only_drives(ctx, carry_net, is_lc, id_I3, false) != + net_only_drives(ctx, carry_net, is_lc, id_CIN, false)) || + (at_end && !net_only_drives(ctx, carry_net, is_lc, id_I3, true))) { if (ctx->debug) log_info(" inserting feed-%s\n", at_end ? "out" : "out-in"); CellInfo *passout; @@ -89,10 +89,10 @@ class ChainConstrainer tile.pop_back(); if (split_chain_next) start_of_chain = true; - passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")), + passout = make_carry_pass_out(cell->ports.at(id_COUT), split_chain_next ? nullptr : *(curr_cell + 1)); } else { - passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")), nullptr); + passout = make_carry_pass_out(cell->ports.at(id_COUT), nullptr); } chains.back().cells.push_back(passout); @@ -110,9 +110,9 @@ class ChainConstrainer CellInfo *make_carry_pass_out(PortInfo &cout_port, CellInfo *cin_cell = nullptr) { NPNR_ASSERT(cout_port.net != nullptr); - std::unique_ptr lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC")); - lc->params[ctx->id("LUT_INIT")] = Property(65280, 16); // 0xff00: O = I3 - lc->params[ctx->id("CARRY_ENABLE")] = Property::State::S1; + std::unique_ptr lc = create_ice_cell(ctx, id_ICESTORM_LC); + lc->params[id_LUT_INIT] = Property(65280, 16); // 0xff00: O = I3 + lc->params[id_CARRY_ENABLE] = Property::State::S1; lc->ports.at(id_O).net = cout_port.net; NetInfo *co_i3_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$I3")); co_i3_net->driver = cout_port.net->driver; @@ -177,11 +177,11 @@ class ChainConstrainer CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port) { NPNR_ASSERT(cin_port.net != nullptr); - std::unique_ptr lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC")); - lc->params[ctx->id("CARRY_ENABLE")] = Property::State::S1; - lc->params[ctx->id("CIN_CONST")] = Property::State::S1; - lc->params[ctx->id("CIN_SET")] = Property::State::S1; - lc->ports.at(ctx->id("I1")).net = cin_port.net; + std::unique_ptr lc = create_ice_cell(ctx, id_ICESTORM_LC); + lc->params[id_CARRY_ENABLE] = Property::State::S1; + lc->params[id_CIN_CONST] = Property::State::S1; + lc->params[id_CIN_SET] = Property::State::S1; + lc->ports.at(id_I1).net = cin_port.net; cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(), [cin_cell, cin_port](const PortRef &usr) { return usr.cell == cin_cell && usr.port == cin_port.name; @@ -189,16 +189,16 @@ class ChainConstrainer PortRef i1_ref; i1_ref.cell = lc.get(); - i1_ref.port = ctx->id("I1"); - lc->ports.at(ctx->id("I1")).net->users.push_back(i1_ref); + i1_ref.port = id_I1; + lc->ports.at(id_I1).net->users.push_back(i1_ref); NetInfo *out_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$O")); PortRef drv_ref; - drv_ref.port = ctx->id("COUT"); + drv_ref.port = id_COUT; drv_ref.cell = lc.get(); out_net->driver = drv_ref; - lc->ports.at(ctx->id("COUT")).net = out_net; + lc->ports.at(id_COUT).net = out_net; PortRef usr_ref; usr_ref.port = cin_port.name; @@ -219,22 +219,19 @@ class ChainConstrainer [](const Context *ctx, const CellInfo *cell) { - CellInfo *carry_prev = - net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT")); + CellInfo *carry_prev = net_driven_by(ctx, cell->ports.at(id_CIN).net, is_lc, id_COUT); if (carry_prev != nullptr) return carry_prev; - CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc, ctx->id("COUT")); + CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(id_I3).net, is_lc, id_COUT); if (i3_prev != nullptr) return i3_prev; return (CellInfo *)nullptr; }, [](const Context *ctx, const CellInfo *cell) { - CellInfo *carry_next = - net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("CIN"), false); + CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(id_COUT).net, is_lc, id_CIN, false); if (carry_next != nullptr) return carry_next; - CellInfo *i3_next = - net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"), false); + CellInfo *i3_next = net_only_drives(ctx, cell->ports.at(id_COUT).net, is_lc, id_I3, false); if (i3_next != nullptr) return i3_next; return (CellInfo *)nullptr; @@ -249,7 +246,7 @@ class ChainConstrainer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (chained.find(cell.first) == chained.end() && is_lc(ctx, ci) && - bool_or_default(ci->params, ctx->id("CARRY_ENABLE"))) { + bool_or_default(ci->params, id_CARRY_ENABLE)) { CellChain sChain; sChain.cells.push_back(ci); chained.insert(cell.first); diff --git a/ice40/constids.inc b/ice40/constids.inc index 6aa5c4c0c2..8bad9e2547 100644 --- a/ice40/constids.inc +++ b/ice40/constids.inc @@ -448,4 +448,168 @@ X(SB_RGB_DRV) X(DFF_ENABLE) X(CARRY_ENABLE) X(NEG_CLK) -X(IO_STANDARD) \ No newline at end of file +X(IO_STANDARD) +X(A) +X(ASYNC_SR) +X(A_REG) +X(A_SIGNED) +X(BEL) +X(BEL_PAD_INPUT) +X(BOTADDSUB_CARRYSELECT) +X(BOTADDSUB_LOWERINPUT) +X(BOTADDSUB_UPPERINPUT) +X(BOTOUTPUT_SELECT) +X(BOT_8x8_MULT_REG) +X(BUS_ADDR74) +X(B_REG) +X(B_SIGNED) +X(C) +X(CARRY_IN_MUX) +X(CIN_CONST) +X(CIN_SET) +X(CLKHF_DIV) +X(CURRENT_MODE) +X(C_REG) +X(D) +X(DELAY_ADJMODE_FB) +X(DELAY_ADJMODE_REL) +X(DELAY_ADJUSTMENT_MODE_FEEDBACK) +X(DELAY_ADJUSTMENT_MODE_RELATIVE) +X(DIVF) +X(DIVQ) +X(DIVR) +X(D_REG) +X(E) +X(FDA_FEEDBACK) +X(FDA_RELATIVE) +X(FEEDBACK_PATH) +X(FILTER_RANGE) +X(FOR_PAD_IN) +X(GLB2LOCAL) +X(GLB_NETWK) +X(GLOBAL) +X(GND) +X(GRID_X) +X(GRID_Y) +X(GRID_Z) +X(I) +X(I2C_SLAVE_INIT_ADDR) +X(INDEX) +X(LEDDRST) +X(LOCAL) +X(LUTFF_COUT) +X(LUTFF_GLOBAL) +X(LUTFF_IN) +X(LUTFF_IN_LUT) +X(LUTFF_LOUT) +X(LUTFF_OUT) +X(LUT_INIT) +X(MODE_8x8) +X(NEG_CLK_R) +X(NEG_CLK_W) +X(NEG_TRIGGER) +X(PACKAGEPIN) +X(PIN_TYPE) +X(PIPELINE_16x16_MULT_REG1) +X(PIPELINE_16x16_MULT_REG2) +X(PLLOUTCORE) +X(PLLOUTCOREA) +X(PLLOUTCOREB) +X(PLLOUTGLOBAL) +X(PLLOUTGLOBALA) +X(PLLOUTGLOBALB) +X(PLLOUT_SELECT) +X(PLLOUT_SELECT_A) +X(PLLOUT_SELECT_B) +X(PLLOUT_SELECT_PORTA) +X(PLLOUT_SELECT_PORTB) +X(PLLTYPE) +X(PULLUP) +X(PULLUP_RESISTOR) +X(Q) +X(R) +X(RCLKN) +X(READ_MODE) +X(RGB0_CURRENT) +X(RGB1_CURRENT) +X(RGB2_CURRENT) +X(ROUTE_THROUGH_FABRIC) +X(S) +X(SB_CARRY) +X(SB_DFF) +X(SB_DFFE) +X(SB_DFFER) +X(SB_DFFES) +X(SB_DFFESR) +X(SB_DFFESS) +X(SB_DFFN) +X(SB_DFFNE) +X(SB_DFFNER) +X(SB_DFFNES) +X(SB_DFFNESR) +X(SB_DFFNESS) +X(SB_DFFNR) +X(SB_DFFNS) +X(SB_DFFNSR) +X(SB_DFFNSS) +X(SB_DFFR) +X(SB_DFFS) +X(SB_DFFSR) +X(SB_DFFSS) +X(SB_GB_IO) +X(SB_HFOSC) +X(SB_LFOSC) +X(SB_LUT4) +X(SB_MAC16) +X(SB_PLL40_2F_CORE) +X(SB_PLL40_2F_PAD) +X(SB_PLL40_2_PAD) +X(SB_PLL40_CORE) +X(SB_PLL40_PAD) +X(SB_RAM40_4K) +X(SB_RAM40_4KNR) +X(SB_RAM40_4KNRNW) +X(SB_RAM40_4KNW) +X(SB_SPRAM256KA) +X(SDA_INPUT_DELAYED) +X(SDA_OUTPUT_DELAYED) +X(SET_NORESET) +X(SHIFTREG_DIVMODE) +X(SHIFTREG_DIV_MODE) +X(SP12_H) +X(SP12_V) +X(SP4_H) +X(SP4_V) +X(TEST_MODE) +X(TOPADDSUB_CARRYSELECT) +X(TOPADDSUB_LOWERINPUT) +X(TOPADDSUB_UPPERINPUT) +X(TOPOUTPUT_SELECT) +X(TOP_8x8_MULT_REG) +X(TRIM_EN) +X(TYPE) +X(VCC) +X(WCLKN) +X(WRITE_MODE) +X(Y) +X(hx1k) +X(hx4k) +X(hx8k) +X(lp1k) +X(lp384) +X(lp4k) +X(lp8k) +X(no_promote_globals) +X(opt_timing) +X(pack) +X(pcf_allow_unconstrained) +X(place) +X(placer) +X(promote_logic) +X(route) +X(router) +X(u1k) +X(u2k) +X(u4k) +X(up3k) +X(up5k) diff --git a/ice40/main.cc b/ice40/main.cc index e480a6cede..224b027577 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -265,13 +265,13 @@ std::unique_ptr Ice40CommandHandler::createContext(dictsettings[ctx->id("arch.package")] = ctx->archArgs().package; if (vm.count("promote-logic")) - ctx->settings[ctx->id("promote_logic")] = Property::State::S1; + ctx->settings[id_promote_logic] = Property::State::S1; if (vm.count("no-promote-globals")) - ctx->settings[ctx->id("no_promote_globals")] = Property::State::S1; + ctx->settings[id_no_promote_globals] = Property::State::S1; if (vm.count("opt-timing")) - ctx->settings[ctx->id("opt_timing")] = Property::State::S1; + ctx->settings[id_opt_timing] = Property::State::S1; if (vm.count("pcf-allow-unconstrained")) - ctx->settings[ctx->id("pcf_allow_unconstrained")] = Property::State::S1; + ctx->settings[id_pcf_allow_unconstrained] = Property::State::S1; return ctx; } diff --git a/ice40/pack.cc b/ice40/pack.cc index fcbdf2bdad..0220d4fe76 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -41,7 +41,7 @@ static void pack_lut_lutffs(Context *ctx) if (ctx->verbose) log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); if (is_lut(ctx, ci)) { - std::unique_ptr packed = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "_LC"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_LC, ci->name.str(ctx) + "_LC"); for (auto &attr : ci->attrs) packed->attrs[attr.first] = attr.second; packed_cells.insert(ci->name); @@ -49,14 +49,14 @@ static void pack_lut_lutffs(Context *ctx) log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx)); // See if we can pack into a DFF // TODO: LUT cascade - NetInfo *o = ci->ports.at(ctx->id("O")).net; - CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true); - auto lut_bel = ci->attrs.find(ctx->id("BEL")); + NetInfo *o = ci->ports.at(id_O).net; + CellInfo *dff = net_only_drives(ctx, o, is_ff, id_D, true); + auto lut_bel = ci->attrs.find(id_BEL); bool packed_dff = false; if (dff) { if (ctx->verbose) log_info("found attached dff %s\n", dff->name.c_str(ctx)); - auto dff_bel = dff->attrs.find(ctx->id("BEL")); + auto dff_bel = dff->attrs.find(id_BEL); if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { // Locations don't match, can't pack } else { @@ -65,10 +65,10 @@ static void pack_lut_lutffs(Context *ctx) ++lut_and_ff; ctx->nets.erase(o->name); if (dff_bel != dff->attrs.end()) - packed->attrs[ctx->id("BEL")] = dff_bel->second; + packed->attrs[id_BEL] = dff_bel->second; for (const auto &attr : dff->attrs) { // BEL is dealt with specially - if (attr.first != ctx->id("BEL")) + if (attr.first != id_BEL) packed->attrs[attr.first] = attr.second; } packed_cells.insert(dff->name); @@ -106,8 +106,7 @@ static void pack_nonlut_ffs(Context *ctx) for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_ff(ctx, ci)) { - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "_DFFLC"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_LC, ci->name.str(ctx) + "_DFFLC"); for (auto &attr : ci->attrs) packed->attrs[attr.first] = attr.second; if (ctx->verbose) @@ -155,25 +154,25 @@ static void pack_carries(Context *ctx) CellInfo *carry_ci_lc; bool ci_value; - bool ci_const = net_is_constant(ctx, ci->ports.at(ctx->id("CI")).net, ci_value); + bool ci_const = net_is_constant(ctx, ci->ports.at(id_CI).net, ci_value); if (ci_const) { carry_ci_lc = nullptr; } else { - carry_ci_lc = net_only_drives(ctx, ci->ports.at(ctx->id("CI")).net, is_lc, ctx->id("I3"), false); + carry_ci_lc = net_only_drives(ctx, ci->ports.at(id_CI).net, is_lc, id_I3, false); } std::set i0_matches, i1_matches; - NetInfo *i0_net = ci->ports.at(ctx->id("I0")).net; - NetInfo *i1_net = ci->ports.at(ctx->id("I1")).net; + NetInfo *i0_net = ci->ports.at(id_I0).net; + NetInfo *i1_net = ci->ports.at(id_I1).net; // Find logic cells connected to both I0 and I1 if (i0_net) { for (auto usr : i0_net->users) { - if (is_lc(ctx, usr.cell) && usr.port == ctx->id("I1")) { + if (is_lc(ctx, usr.cell) && usr.port == id_I1) { if (ctx->cells.find(usr.cell->name) != ctx->cells.end() && exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) { // This clause stops us double-packing cells i0_matches.insert(usr.cell->name); - if (!i1_net && !usr.cell->ports.at(ctx->id("I2")).net) { + if (!i1_net && !usr.cell->ports.at(id_I2).net) { // I1 is don't care when disconnected, duplicate I0 i1_matches.insert(usr.cell->name); } @@ -183,12 +182,12 @@ static void pack_carries(Context *ctx) } if (i1_net) { for (auto usr : i1_net->users) { - if (is_lc(ctx, usr.cell) && usr.port == ctx->id("I2")) { + if (is_lc(ctx, usr.cell) && usr.port == id_I2) { if (ctx->cells.find(usr.cell->name) != ctx->cells.end() && exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) { // This clause stops us double-packing cells i1_matches.insert(usr.cell->name); - if (!i0_net && !usr.cell->ports.at(ctx->id("I1")).net) { + if (!i0_net && !usr.cell->ports.at(id_I1).net) { // I0 is don't care when disconnected, duplicate I1 i0_matches.insert(usr.cell->name); } @@ -208,53 +207,53 @@ static void pack_carries(Context *ctx) } else { // No LC to pack into matching I0/I1, insert a new one std::unique_ptr created_lc = - create_ice_cell(ctx, ctx->id("ICESTORM_LC"), cell.first.str(ctx) + "$CARRY"); + create_ice_cell(ctx, id_ICESTORM_LC, cell.first.str(ctx) + "$CARRY"); carry_lc = created_lc.get(); - created_lc->ports.at(ctx->id("I1")).net = i0_net; + created_lc->ports.at(id_I1).net = i0_net; if (i0_net) { PortRef pr; pr.cell = created_lc.get(); - pr.port = ctx->id("I1"); + pr.port = id_I1; i0_net->users.push_back(pr); } - created_lc->ports.at(ctx->id("I2")).net = i1_net; + created_lc->ports.at(id_I2).net = i1_net; if (i1_net) { PortRef pr; pr.cell = created_lc.get(); - pr.port = ctx->id("I2"); + pr.port = id_I2; i1_net->users.push_back(pr); } new_cells.push_back(std::move(created_lc)); ++carry_only; } - carry_lc->params[ctx->id("CARRY_ENABLE")] = Property::State::S1; - replace_port(ci, ctx->id("CI"), carry_lc, ctx->id("CIN")); - replace_port(ci, ctx->id("CO"), carry_lc, ctx->id("COUT")); + carry_lc->params[id_CARRY_ENABLE] = Property::State::S1; + replace_port(ci, id_CI, carry_lc, id_CIN); + replace_port(ci, id_CO, carry_lc, id_COUT); if (i0_net) { auto &i0_usrs = i0_net->users; i0_usrs.erase(std::remove_if(i0_usrs.begin(), i0_usrs.end(), [ci, ctx](const PortRef &pr) { - return pr.cell == ci && pr.port == ctx->id("I0"); + return pr.cell == ci && pr.port == id_I0; })); } if (i1_net) { auto &i1_usrs = i1_net->users; i1_usrs.erase(std::remove_if(i1_usrs.begin(), i1_usrs.end(), [ci, ctx](const PortRef &pr) { - return pr.cell == ci && pr.port == ctx->id("I1"); + return pr.cell == ci && pr.port == id_I1; })); } // Check for constant driver on CIN - if (carry_lc->ports.at(ctx->id("CIN")).net != nullptr) { - IdString cin_net = carry_lc->ports.at(ctx->id("CIN")).net->name; + if (carry_lc->ports.at(id_CIN).net != nullptr) { + IdString cin_net = carry_lc->ports.at(id_CIN).net->name; if (cin_net == ctx->id("$PACKER_GND_NET") || cin_net == ctx->id("$PACKER_VCC_NET")) { - carry_lc->params[ctx->id("CIN_CONST")] = Property::State::S1; - carry_lc->params[ctx->id("CIN_SET")] = + carry_lc->params[id_CIN_CONST] = Property::State::S1; + carry_lc->params[id_CIN_SET] = cin_net == ctx->id("$PACKER_VCC_NET") ? Property::State::S1 : Property::State::S0; - carry_lc->ports.at(ctx->id("CIN")).net = nullptr; + carry_lc->ports.at(id_CIN).net = nullptr; auto &cin_users = ctx->nets.at(cin_net)->users; cin_users.erase( std::remove_if(cin_users.begin(), cin_users.end(), [carry_lc, ctx](const PortRef &pr) { - return pr.cell == carry_lc && pr.port == ctx->id("CIN"); + return pr.cell == carry_lc && pr.port == id_CIN; })); } } @@ -281,18 +280,15 @@ static void pack_ram(Context *ctx) for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_ram(ctx, ci)) { - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_RAM"), ci->name.str(ctx) + "_RAM"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_RAM, ci->name.str(ctx) + "_RAM"); packed_cells.insert(ci->name); for (auto attr : ci->attrs) packed->attrs[attr.first] = attr.second; for (auto param : ci->params) packed->params[param.first] = param.second; - packed->params[ctx->id("NEG_CLK_W")] = - Property(ci->type == ctx->id("SB_RAM40_4KNW") || ci->type == ctx->id("SB_RAM40_4KNRNW"), 1); - packed->params[ctx->id("NEG_CLK_R")] = - Property(ci->type == ctx->id("SB_RAM40_4KNR") || ci->type == ctx->id("SB_RAM40_4KNRNW"), 1); - packed->type = ctx->id("ICESTORM_RAM"); + packed->params[id_NEG_CLK_W] = Property(ci->type == id_SB_RAM40_4KNW || ci->type == id_SB_RAM40_4KNRNW, 1); + packed->params[id_NEG_CLK_R] = Property(ci->type == id_SB_RAM40_4KNR || ci->type == id_SB_RAM40_4KNRNW, 1); + packed->type = id_ICESTORM_RAM; for (auto port : ci->ports) { PortInfo &pi = port.second; std::string newname = pi.name.str(ctx); @@ -300,9 +296,9 @@ static void pack_ram(Context *ctx) if (bpos != std::string::npos) { newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - if (pi.name == ctx->id("RCLKN")) + if (pi.name == id_RCLKN) newname = "RCLK"; - else if (pi.name == ctx->id("WCLKN")) + else if (pi.name == id_WCLKN) newname = "WCLK"; replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } @@ -330,13 +326,13 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') && !constval) { uc->ports[user.port].net = nullptr; - } else if ((is_sb_mac16(ctx, uc) || uc->type == ctx->id("ICESTORM_DSP")) && - (user.port != ctx->id("CLK") && - ((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) { + } else if ((is_sb_mac16(ctx, uc) || uc->type == id_ICESTORM_DSP) && + (user.port != id_CLK && + ((constval && user.port == id_CE) || (!constval && user.port != id_CE)))) { uc->ports[user.port].net = nullptr; - } else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") && - user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") && - user.port != ctx->id("WCLKE")) { + } else if (is_ram(ctx, uc) && !constval && user.port != id_RCLK && user.port != id_RCLKN && + user.port != id_WCLK && user.port != id_WCLKN && user.port != id_RCLKE && + user.port != id_WCLKE) { uc->ports[user.port].net = nullptr; } else { uc->ports[user.port].net = constnet; @@ -352,24 +348,24 @@ static void pack_constants(Context *ctx) { log_info("Packing constants..\n"); - std::unique_ptr gnd_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_GND"); - gnd_cell->params[ctx->id("LUT_INIT")] = Property(0, 16); + std::unique_ptr gnd_cell = create_ice_cell(ctx, id_ICESTORM_LC, "$PACKER_GND"); + gnd_cell->params[id_LUT_INIT] = Property(0, 16); auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); - gnd_net->driver.port = ctx->id("O"); - gnd_cell->ports.at(ctx->id("O")).net = gnd_net.get(); + gnd_net->driver.port = id_O; + gnd_cell->ports.at(id_O).net = gnd_net.get(); NetInfo *gnd_net_info = gnd_net.get(); if (ctx->nets.find(ctx->id("$PACKER_GND_NET")) != ctx->nets.end()) { gnd_net_info = ctx->nets.find(ctx->id("$PACKER_GND_NET"))->second.get(); } - std::unique_ptr vcc_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_VCC"); - vcc_cell->params[ctx->id("LUT_INIT")] = Property(1, 16); + std::unique_ptr vcc_cell = create_ice_cell(ctx, id_ICESTORM_LC, "$PACKER_VCC"); + vcc_cell->params[id_LUT_INIT] = Property(1, 16); auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); - vcc_net->driver.port = ctx->id("O"); - vcc_cell->ports.at(ctx->id("O")).net = vcc_net.get(); + vcc_net->driver.port = id_O; + vcc_cell->ports.at(id_O).net = vcc_net.get(); NetInfo *vcc_net_info = vcc_net.get(); if (ctx->nets.find(ctx->id("$PACKER_VCC_NET")) != ctx->nets.end()) { @@ -382,13 +378,13 @@ static void pack_constants(Context *ctx) for (auto &net : ctx->nets) { NetInfo *ni = net.second.get(); - if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { + if (ni->driver.cell != nullptr && ni->driver.cell->type == id_GND) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, gnd_net_info, false); gnd_used = true; dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); - } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) { + } else if (ni->driver.cell != nullptr && ni->driver.cell->type == id_VCC) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, vcc_net_info, true); dead_nets.push_back(net.first); @@ -434,16 +430,16 @@ static std::unique_ptr create_padin_gbuf(Context *ctx, CellInfo *cell, std::string gbuf_name) { // Find the matching SB_GB BEL connected to the same global network - if (!cell->attrs.count(ctx->id("BEL"))) + if (!cell->attrs.count(id_BEL)) log_error("Unconstrained SB_GB_IO %s is not supported.\n", ctx->nameOf(cell)); - BelId bel = ctx->getBelByNameStr(cell->attrs[ctx->id("BEL")].as_string()); + BelId bel = ctx->getBelByNameStr(cell->attrs[id_BEL].as_string()); BelId gb_bel = find_padin_gbuf(ctx, bel, port_name); NPNR_ASSERT(gb_bel != BelId()); // Create a SB_GB Cell and lock it there - std::unique_ptr gb = create_ice_cell(ctx, ctx->id("SB_GB"), gbuf_name); - gb->attrs[ctx->id("FOR_PAD_IN")] = Property::State::S1; - gb->attrs[ctx->id("BEL")] = ctx->getBelName(gb_bel).str(ctx); + std::unique_ptr gb = create_ice_cell(ctx, id_SB_GB, gbuf_name); + gb->attrs[id_FOR_PAD_IN] = Property::State::S1; + gb->attrs[id_BEL] = ctx->getBelName(gb_bel).str(ctx); // Reconnect the net to that port for easier identification it's a global net replace_port(cell, port_name, gb.get(), id_GLOBAL_BUFFER_OUTPUT); @@ -475,11 +471,11 @@ static void pack_io(Context *ctx) if (is_nextpnr_iob(ctx, ci)) { CellInfo *sb = nullptr, *rgb = nullptr; if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { - sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); + sb = net_only_drives(ctx, ci->ports.at(id_O).net, is_ice_iob, id_PACKAGE_PIN, true, ci); } else if (ci->type == ctx->id("$nextpnr_obuf")) { - NetInfo *net = ci->ports.at(ctx->id("I")).net; - sb = net_only_drives(ctx, net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci); + NetInfo *net = ci->ports.at(id_I).net; + sb = net_only_drives(ctx, net, is_ice_iob, id_PACKAGE_PIN, true, ci); if (net && net->driver.cell && (is_sb_rgba_drv(ctx, net->driver.cell) || is_sb_rgb_drv(ctx, net->driver.cell))) rgb = net->driver.cell; @@ -488,7 +484,7 @@ static void pack_io(Context *ctx) // Trivial case, SB_IO used. Just destroy the iobuf log_info("%s feeds SB_IO %s, removing %s %s.\n", ci->name.c_str(ctx), sb->name.c_str(ctx), ci->type.c_str(ctx), ci->name.c_str(ctx)); - NetInfo *net = sb->ports.at(ctx->id("PACKAGE_PIN")).net; + NetInfo *net = sb->ports.at(id_PACKAGE_PIN).net; if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) && net->users.size() > 1) || (ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr))) @@ -518,13 +514,12 @@ static void pack_io(Context *ctx) } else if (rgb != nullptr) { log_info("%s use by SB_RGBA_DRV/SB_RGB_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx)); - disconnect_port(ctx, ci, ctx->id("I")); + disconnect_port(ctx, ci, id_I); packed_cells.insert(ci->name); continue; } else { // Create a SB_IO buffer - std::unique_ptr ice_cell = - create_ice_cell(ctx, ctx->id("SB_IO"), ci->name.str(ctx) + "$sb_io"); + std::unique_ptr ice_cell = create_ice_cell(ctx, id_SB_IO, ci->name.str(ctx) + "$sb_io"); nxio_to_sb(ctx, ci, ice_cell.get(), packed_cells); new_cells.push_back(std::move(ice_cell)); sb = new_cells.back().get(); @@ -535,7 +530,7 @@ static void pack_io(Context *ctx) for (auto &attr : ci->attrs) sb->attrs[attr.first] = attr.second; } else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) { - NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net; + NetInfo *net = ci->ports.at(id_PACKAGE_PIN).net; if ((net != nullptr) && ((net->users.size() > 2) || (net->driver.cell != nullptr && net->driver.cell->type == ctx->id("$nextpnr_obuf") && net->users.size() > 1))) @@ -552,8 +547,8 @@ static void pack_io(Context *ctx) new_cells.push_back(std::move(gb)); // Make it a normal SB_IO with global marker - ci->type = ctx->id("SB_IO"); - ci->attrs[ctx->id("GLOBAL")] = Property::State::S1; + ci->type = id_SB_IO; + ci->attrs[id_GLOBAL] = Property::State::S1; } else if (is_sb_io(ctx, ci)) { // Disconnect unused inputs NetInfo *net_in0 = ci->ports.count(id_D_IN_0) ? ci->ports[id_D_IN_0].net : nullptr; @@ -595,18 +590,18 @@ static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen is_cen ? " [cen]" : "", is_logic ? " [logic]" : "", fanout); std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk")); - std::unique_ptr gb = create_ice_cell(ctx, ctx->id("SB_GB"), "$gbuf_" + glb_name); - gb->ports[ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER")].net = net; + std::unique_ptr gb = create_ice_cell(ctx, id_SB_GB, "$gbuf_" + glb_name); + gb->ports[id_USER_SIGNAL_TO_GLOBAL_BUFFER].net = net; PortRef pr; pr.cell = gb.get(); - pr.port = ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER"); + pr.port = id_USER_SIGNAL_TO_GLOBAL_BUFFER; net->users.push_back(pr); pr.cell = gb.get(); - pr.port = ctx->id("GLOBAL_BUFFER_OUTPUT"); + pr.port = id_GLOBAL_BUFFER_OUTPUT; NetInfo *glbnet = ctx->createNet(ctx->id(glb_name)); glbnet->driver = pr; - gb->ports[ctx->id("GLOBAL_BUFFER_OUTPUT")].net = glbnet; + gb->ports[id_GLOBAL_BUFFER_OUTPUT].net = glbnet; std::vector keep_users; for (auto user : net->users) { if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) || @@ -664,9 +659,9 @@ static void promote_globals(Context *ctx) --gbs_available; /* And possibly limits what we can promote */ - if (cell.second->attrs.find(ctx->id("BEL")) != cell.second->attrs.end()) { + if (cell.second->attrs.find(id_BEL) != cell.second->attrs.end()) { /* If the SB_GB is locked, doesn't matter what it drives */ - BelId bel = ctx->getBelByNameStr(cell.second->attrs[ctx->id("BEL")].as_string()); + BelId bel = ctx->getBelByNameStr(cell.second->attrs[id_BEL].as_string()); int glb_id = ctx->get_driven_glb_netwk(bel); if ((glb_id % 2) == 0) resets_available--; @@ -708,7 +703,7 @@ static void promote_globals(Context *ctx) if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh && (global_logic->second > global_cen->second || prom_cens >= cens_available) && (global_logic->second > global_reset->second || prom_resets >= resets_available) && - bool_or_default(ctx->settings, ctx->id("promote_logic"), false)) { + bool_or_default(ctx->settings, id_promote_logic, false)) { NetInfo *logicnet = ctx->nets[global_logic->first].get(); insert_global(ctx, logicnet, false, false, true, global_logic->second); ++prom_globals; @@ -784,11 +779,11 @@ static void place_plls(Context *ctx) continue; // If it's constrained already, add to already used list - if (ci->attrs.count(ctx->id("BEL"))) { - BelId bel_constrain = ctx->getBelByNameStr(ci->attrs[ctx->id("BEL")].as_string()); + if (ci->attrs.count(id_BEL)) { + BelId bel_constrain = ctx->getBelByNameStr(ci->attrs[id_BEL].as_string()); if (pll_all_bels.count(bel_constrain) == 0) log_error("PLL '%s' is constrained to invalid BEL '%s'\n", ci->name.c_str(ctx), - ci->attrs[ctx->id("BEL")].as_string().c_str()); + ci->attrs[id_BEL].as_string().c_str()); pll_used_bels[bel_constrain] = ci; } @@ -802,10 +797,10 @@ static void place_plls(Context *ctx) continue; // Check PACKAGEPIN connection - if (!ci->ports.count(ctx->id("PACKAGEPIN"))) + if (!ci->ports.count(id_PACKAGEPIN)) log_error("PLL '%s' is of PAD type but doesn't have a PACKAGEPIN port\n", ci->name.c_str(ctx)); - NetInfo *ni = ci->ports.at(ctx->id("PACKAGEPIN")).net; + NetInfo *ni = ci->ports.at(id_PACKAGEPIN).net; if (ni == nullptr || ni->driver.cell == nullptr) log_error("PLL '%s' is of PAD type but doesn't have a valid PACKAGEPIN connection\n", ci->name.c_str(ctx)); @@ -816,11 +811,11 @@ static void place_plls(Context *ctx) ci->name.c_str(ctx), io_cell->type.c_str(ctx)); if (ni->users.size() != 1) log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx), ni->name.c_str(ctx)); - if (!io_cell->attrs.count(ctx->id("BEL"))) + if (!io_cell->attrs.count(id_BEL)) log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx), io_cell->name.c_str(ctx)); - BelId io_bel = ctx->getBelByNameStr(io_cell->attrs.at(ctx->id("BEL")).as_string()); + BelId io_bel = ctx->getBelByNameStr(io_cell->attrs.at(id_BEL).as_string()); BelId found_bel; // Find the PLL BEL that would suit that connection @@ -843,16 +838,16 @@ static void place_plls(Context *ctx) } // Is it user constrained ? - if (ci->attrs.count(ctx->id("BEL"))) { + if (ci->attrs.count(id_BEL)) { // Yes. Check it actually matches ! - BelId bel_constrain = ctx->getBelByNameStr(ci->attrs[ctx->id("BEL")].as_string()); + BelId bel_constrain = ctx->getBelByNameStr(ci->attrs[id_BEL].as_string()); if (bel_constrain != found_bel) log_error("PLL '%s' is user constrained to %s but can only be placed in %s based on its PACKAGEPIN " "connection\n", ci->name.c_str(ctx), ctx->nameOfBel(bel_constrain), ctx->nameOfBel(found_bel)); } else { // No, we can constrain it ourselves - ci->attrs[ctx->id("BEL")] = ctx->getBelName(found_bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(found_bel).str(ctx); pll_used_bels[found_bel] = ci; } @@ -867,15 +862,15 @@ static void place_plls(Context *ctx) continue; // Only consider bound IO that are used as inputs - if (!io_ci->attrs.count(ctx->id("BEL"))) + if (!io_ci->attrs.count(id_BEL)) continue; if ((!io_ci->ports.count(id_D_IN_0) || (io_ci->ports[id_D_IN_0].net == nullptr)) && (!io_ci->ports.count(id_D_IN_1) || (io_ci->ports[id_D_IN_1].net == nullptr)) && - !bool_or_default(io_ci->attrs, ctx->id("GLOBAL"))) + !bool_or_default(io_ci->attrs, id_GLOBAL)) continue; // Check all placed PLL (either forced by user, or forced by PACKAGEPIN) - BelId io_bel = ctx->getBelByNameStr(io_ci->attrs[ctx->id("BEL")].as_string()); + BelId io_bel = ctx->getBelByNameStr(io_ci->attrs[id_BEL].as_string()); for (auto placed_pll : pll_used_bels) { BelPin pll_io_a, pll_io_b; @@ -905,11 +900,11 @@ static void place_plls(Context *ctx) continue; // Only consider the bound ones - if (!gb_ci->attrs.count(ctx->id("BEL"))) + if (!gb_ci->attrs.count(id_BEL)) continue; // Check all placed PLL (either forced by user, or forced by PACKAGEPIN) - BelId gb_bel = ctx->getBelByNameStr(gb_ci->attrs[ctx->id("BEL")].as_string()); + BelId gb_bel = ctx->getBelByNameStr(gb_ci->attrs[id_BEL].as_string()); for (auto placed_pll : pll_used_bels) { CellInfo *ci = placed_pll.second; @@ -967,8 +962,8 @@ static void place_plls(Context *ctx) // Could this be a PAD PLL ? bool could_be_pad = false; BelId pad_bel; - if (ni->users.size() == 1 && is_sb_io(ctx, ni->driver.cell) && ni->driver.cell->attrs.count(ctx->id("BEL"))) - pad_bel = ctx->getBelByNameStr(ni->driver.cell->attrs[ctx->id("BEL")].as_string()); + if (ni->users.size() == 1 && is_sb_io(ctx, ni->driver.cell) && ni->driver.cell->attrs.count(id_BEL)) + pad_bel = ctx->getBelByNameStr(ni->driver.cell->attrs[id_BEL].as_string()); // Find a BEL for it BelId found_bel; @@ -1002,7 +997,7 @@ static void place_plls(Context *ctx) if (could_be_pad) log_info(" (given its connections, this PLL could have been a PAD PLL)\n"); - ci->attrs[ctx->id("BEL")] = ctx->getBelName(found_bel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(found_bel).str(ctx); pll_used_bels[found_bel] = ci; } } @@ -1017,20 +1012,20 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString NPNR_ASSERT(port.net != nullptr); // Create pass-through LUT. - std::unique_ptr pt = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), - ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through"); - pt->params[ctx->id("LUT_INIT")] = Property(65280, 16); // output is always I3 + std::unique_ptr pt = + create_ice_cell(ctx, id_ICESTORM_LC, ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through"); + pt->params[id_LUT_INIT] = Property(65280, 16); // output is always I3 // Create LUT output net. NetInfo *out_net = ctx->createNet(ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net")); out_net->driver.cell = pt.get(); - out_net->driver.port = ctx->id("O"); - pt->ports.at(ctx->id("O")).net = out_net; + out_net->driver.port = id_O; + pt->ports.at(id_O).net = out_net; // New users of the original cell's port std::vector new_users; for (const auto &user : port.net->users) { - if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) { + if (onlyNonLUTs && user.cell->type == id_ICESTORM_LC) { new_users.push_back(user); continue; } @@ -1046,9 +1041,9 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString // Add LUT to new users. PortRef pr; pr.cell = pt.get(); - pr.port = ctx->id("I3"); + pr.port = id_I3; new_users.push_back(pr); - pt->ports.at(ctx->id("I3")).net = port.net; + pt->ports.at(id_I3).net = port.net; // Replace users of the original net. port.net->users = new_users; @@ -1065,7 +1060,7 @@ static BelId cell_place_unique(Context *ctx, CellInfo *ci) if (ctx->is_bel_locked(bel)) continue; IdStringList bel_name = ctx->getBelName(bel); - ci->attrs[ctx->id("BEL")] = bel_name.str(ctx); + ci->attrs[id_BEL] = bel_name.str(ctx); log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), ctx->nameOfBel(bel)); return bel; } @@ -1124,7 +1119,7 @@ static void pack_special(Context *ctx) /* Force placement (no choices anyway) */ cell_place_unique(ctx, ci); - NetInfo *ledpu_net = ci->ports.at(ctx->id("LEDPU")).net; + NetInfo *ledpu_net = ci->ports.at(id_LEDPU).net; for (auto &user : ledpu_net->users) { if (!is_sb_rgb_drv(ctx, user.cell)) { log_error("SB_LED_DRV_CUR LEDPU port can only be connected to SB_RGB_DRV!\n"); @@ -1133,44 +1128,42 @@ static void pack_special(Context *ctx) user.cell->ports.at(user.port).net = nullptr; } } - ci->ports.erase(ctx->id("LEDPU")); + ci->ports.erase(id_LEDPU); ctx->nets.erase(ledpu_net->name); } } for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_sb_lfosc(ctx, ci)) { - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"), ci->name.str(ctx) + "_OSC"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_LFOSC, ci->name.str(ctx) + "_OSC"); packed_cells.insert(ci->name); cell_place_unique(ctx, packed.get()); - replace_port(ci, ctx->id("CLKLFEN"), packed.get(), ctx->id("CLKLFEN")); - replace_port(ci, ctx->id("CLKLFPU"), packed.get(), ctx->id("CLKLFPU")); - if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) { - replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF_FABRIC")); - set_period(ctx, packed.get(), ctx->id("CLKLF_FABRIC"), 100000000); // 10kHz + replace_port(ci, id_CLKLFEN, packed.get(), id_CLKLFEN); + replace_port(ci, id_CLKLFPU, packed.get(), id_CLKLFPU); + if (bool_or_default(ci->attrs, id_ROUTE_THROUGH_FABRIC)) { + replace_port(ci, id_CLKLF, packed.get(), id_CLKLF_FABRIC); + set_period(ctx, packed.get(), id_CLKLF_FABRIC, 100000000); // 10kHz } else { - replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF")); + replace_port(ci, id_CLKLF, packed.get(), id_CLKLF); std::unique_ptr gb = - create_padin_gbuf(ctx, packed.get(), ctx->id("CLKLF"), "$gbuf_" + ci->name.str(ctx) + "_lfosc"); + create_padin_gbuf(ctx, packed.get(), id_CLKLF, "$gbuf_" + ci->name.str(ctx) + "_lfosc"); set_period(ctx, gb.get(), id_GLOBAL_BUFFER_OUTPUT, 100000000); // 10kHz new_cells.push_back(std::move(gb)); } new_cells.push_back(std::move(packed)); } else if (is_sb_hfosc(ctx, ci)) { - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_HFOSC, ci->name.str(ctx) + "_OSC"); packed_cells.insert(ci->name); cell_place_unique(ctx, packed.get()); - packed->params[ctx->id("TRIM_EN")] = str_or_default(ci->params, ctx->id("TRIM_EN"), "0b0"); - packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00"); - replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN")); - replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU")); + packed->params[id_TRIM_EN] = str_or_default(ci->params, id_TRIM_EN, "0b0"); + packed->params[id_CLKHF_DIV] = str_or_default(ci->params, id_CLKHF_DIV, "0b00"); + replace_port(ci, id_CLKHFEN, packed.get(), id_CLKHFEN); + replace_port(ci, id_CLKHFPU, packed.get(), id_CLKHFPU); for (int i = 0; i < 10; i++) { auto port = ctx->id("TRIM" + std::to_string(i)); replace_port(ci, port, packed.get(), port); } - std::string div = packed->params[ctx->id("CLKHF_DIV")].as_string(); + std::string div = packed->params[id_CLKHF_DIV].as_string(); int frequency; if (div == "0b00") frequency = 48; @@ -1182,20 +1175,19 @@ static void pack_special(Context *ctx) frequency = 6; else log_error("Invalid HFOSC divider value '%s' - expecting 0b00, 0b01, 0b10 or 0b11\n", div.c_str()); - if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) { - replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC")); - set_period(ctx, packed.get(), ctx->id("CLKHF_FABRIC"), 1000000 / frequency); + if (bool_or_default(ci->attrs, id_ROUTE_THROUGH_FABRIC)) { + replace_port(ci, id_CLKHF, packed.get(), id_CLKHF_FABRIC); + set_period(ctx, packed.get(), id_CLKHF_FABRIC, 1000000 / frequency); } else { - replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF")); + replace_port(ci, id_CLKHF, packed.get(), id_CLKHF); std::unique_ptr gb = - create_padin_gbuf(ctx, packed.get(), ctx->id("CLKHF"), "$gbuf_" + ci->name.str(ctx) + "_hfosc"); + create_padin_gbuf(ctx, packed.get(), id_CLKHF, "$gbuf_" + ci->name.str(ctx) + "_hfosc"); set_period(ctx, gb.get(), id_GLOBAL_BUFFER_OUTPUT, 1000000 / frequency); new_cells.push_back(std::move(gb)); } new_cells.push_back(std::move(packed)); } else if (is_sb_spram(ctx, ci)) { - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_SPRAM"), ci->name.str(ctx) + "_RAM"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_SPRAM, ci->name.str(ctx) + "_RAM"); packed_cells.insert(ci->name); for (auto attr : ci->attrs) packed->attrs[attr.first] = attr.second; @@ -1210,8 +1202,7 @@ static void pack_special(Context *ctx) } new_cells.push_back(std::move(packed)); } else if (is_sb_mac16(ctx, ci)) { - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_DSP"), ci->name.str(ctx) + "_DSP"); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_DSP, ci->name.str(ctx) + "_DSP"); packed_cells.insert(ci->name); for (auto attr : ci->attrs) packed->attrs[attr.first] = attr.second; @@ -1241,7 +1232,7 @@ static void pack_special(Context *ctx) if (net == nullptr) continue; - if ((pi.name != ctx->id("RGB0")) && (pi.name != ctx->id("RGB1")) && (pi.name != ctx->id("RGB2"))) + if ((pi.name != id_RGB0) && (pi.name != id_RGB1) && (pi.name != id_RGB2)) continue; if (net->users.size() > 0) @@ -1253,10 +1244,10 @@ static void pack_special(Context *ctx) if (is_sb_rgb_drv(ctx, ci) && !ci->ledInfo.ledCurConnected) log_error("Port RGBPU of SB_RGB_DRV should be driven by port LEDPU of SB_LED_DRV_CUR!\n"); - ci->ports.erase(ctx->id("RGBPU")); - ci->ports.erase(ctx->id("RGB0")); - ci->ports.erase(ctx->id("RGB1")); - ci->ports.erase(ctx->id("RGB2")); + ci->ports.erase(id_RGBPU); + ci->ports.erase(id_RGB0); + ci->ports.erase(id_RGB1); + ci->ports.erase(id_RGB2); } else if (is_sb_ledda_ip(ctx, ci)) { /* Force placement (no choices anyway) */ cell_place_unique(ctx, ci); @@ -1268,7 +1259,7 @@ static void pack_special(Context *ctx) {std::make_tuple(id_SB_I2C, "0b0011"), Loc(25, 31, 0)}, }; std::string bus_addr74 = - str_or_default(ci->params, ctx->id("BUS_ADDR74"), is_sb_i2c(ctx, ci) ? "0b0001" : "0b0000"); + str_or_default(ci->params, 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)); @@ -1278,7 +1269,7 @@ static void pack_special(Context *ctx) log_error("Unable to find placement for cell '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); IdStringList bel_name = ctx->getBelName(bel); - ci->attrs[ctx->id("BEL")] = bel_name.str(ctx); + ci->attrs[id_BEL] = bel_name.str(ctx); log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), ctx->nameOfBel(bel)); } } @@ -1303,15 +1294,14 @@ void pack_plls(Context *ctx) bool is_pad = is_sb_pll40_pad(ctx, ci); bool is_core = !is_pad; - std::unique_ptr packed = - create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL"); - packed->attrs[ctx->id("TYPE")] = ci->type.str(ctx); + std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_PLL, ci->name.str(ctx) + "_PLL"); + packed->attrs[id_TYPE] = ci->type.str(ctx); packed_cells.insert(ci->name); if (!is_sb_pll40_dual(ctx, ci)) { // Remove second output, so a buffer isn't created for it, for these // cell types with only one output - packed->ports.erase(ctx->id("PLLOUT_B")); - packed->ports.erase(ctx->id("PLLOUT_B_GLOBAL")); + packed->ports.erase(id_PLLOUT_B); + packed->ports.erase(id_PLLOUT_B_GLOBAL); } for (auto attr : ci->attrs) packed->attrs[attr.first] = attr.second; @@ -1319,9 +1309,9 @@ void pack_plls(Context *ctx) packed->params[param.first] = param.second; const std::map pos_map_name = { - {ctx->id("PLLOUT_SELECT"), ctx->id("PLLOUT_SELECT_A")}, - {ctx->id("PLLOUT_SELECT_PORTA"), ctx->id("PLLOUT_SELECT_A")}, - {ctx->id("PLLOUT_SELECT_PORTB"), ctx->id("PLLOUT_SELECT_B")}, + {id_PLLOUT_SELECT, id_PLLOUT_SELECT_A}, + {id_PLLOUT_SELECT_PORTA, id_PLLOUT_SELECT_A}, + {id_PLLOUT_SELECT_PORTB, id_PLLOUT_SELECT_B}, }; const std::map pos_map_val = { {"GENCLK", 0}, @@ -1336,9 +1326,8 @@ void pack_plls(Context *ctx) packed->params[pos_map_name.at(param.first)] = pos_map_val.at(param.second.as_string()); } const std::map> delmodes = { - {ctx->id("DELAY_ADJUSTMENT_MODE_FEEDBACK"), {ctx->id("DELAY_ADJMODE_FB"), ctx->id("FDA_FEEDBACK")}}, - {ctx->id("DELAY_ADJUSTMENT_MODE_RELATIVE"), - {ctx->id("DELAY_ADJMODE_REL"), ctx->id("FDA_RELATIVE")}}, + {id_DELAY_ADJUSTMENT_MODE_FEEDBACK, {id_DELAY_ADJMODE_FB, id_FDA_FEEDBACK}}, + {id_DELAY_ADJUSTMENT_MODE_RELATIVE, {id_DELAY_ADJMODE_REL, id_FDA_RELATIVE}}, }; for (auto delmode : delmodes) { if (ci->params.count(delmode.first)) { @@ -1352,9 +1341,9 @@ void pack_plls(Context *ctx) log_error("Invalid PLL %s selection '%s'\n", delmode.first.c_str(ctx), value.c_str()); } } - auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")].is_string - ? packed->params[ctx->id("FEEDBACK_PATH")].as_string() - : std::to_string(packed->params[ctx->id("FEEDBACK_PATH")].as_int64()); + auto feedback_path = packed->params[id_FEEDBACK_PATH].is_string + ? packed->params[id_FEEDBACK_PATH].as_string() + : std::to_string(packed->params[id_FEEDBACK_PATH].as_int64()); std::string fbp_value = feedback_path == "DELAY" ? "0" : feedback_path == "SIMPLE" ? "1" : feedback_path == "PHASE_AND_DELAY" ? "2" @@ -1363,8 +1352,8 @@ void pack_plls(Context *ctx) if (!std::all_of(fbp_value.begin(), fbp_value.end(), isdigit)) log_error("PLL '%s' has unsupported FEEDBACK_PATH value '%s'\n", ci->name.c_str(ctx), feedback_path.c_str()); - packed->params[ctx->id("FEEDBACK_PATH")] = Property(std::stoi(fbp_value), 3); - packed->params[ctx->id("PLLTYPE")] = sb_pll40_type(ctx, ci); + packed->params[id_FEEDBACK_PATH] = Property(std::stoi(fbp_value), 3); + packed->params[id_PLLTYPE] = sb_pll40_type(ctx, ci); NetInfo *pad_packagepin_net = nullptr; @@ -1379,16 +1368,16 @@ void pack_plls(Context *ctx) newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - if (pi.name == ctx->id("PLLOUTCOREA") || pi.name == ctx->id("PLLOUTCORE")) + if (pi.name == id_PLLOUTCOREA || pi.name == id_PLLOUTCORE) newname = "PLLOUT_A"; - if (pi.name == ctx->id("PLLOUTCOREB")) + if (pi.name == id_PLLOUTCOREB) newname = "PLLOUT_B"; - if (pi.name == ctx->id("PLLOUTGLOBALA") || pi.name == ctx->id("PLLOUTGLOBAL")) + if (pi.name == id_PLLOUTGLOBALA || pi.name == id_PLLOUTGLOBAL) newname = "PLLOUT_A_GLOBAL"; - if (pi.name == ctx->id("PLLOUTGLOBALB")) + if (pi.name == id_PLLOUTGLOBALB) newname = "PLLOUT_B_GLOBAL"; - if (pi.name == ctx->id("PACKAGEPIN")) { + if (pi.name == id_PACKAGEPIN) { if (!is_pad) { log_error("PLL '%s' has a PACKAGEPIN but is not a PAD PLL\n", ci->name.c_str(ctx)); } else { @@ -1399,7 +1388,7 @@ void pack_plls(Context *ctx) continue; } } - if (pi.name == ctx->id("REFERENCECLK")) { + if (pi.name == id_REFERENCECLK) { if (!is_core) log_error("PLL '%s' has a REFERENCECLK but is not a CORE PLL\n", ci->name.c_str(ctx)); got_input_constr = get_period(ctx, ci, pi.name, input_constr); @@ -1429,7 +1418,7 @@ void pack_plls(Context *ctx) log_info(" Input frequency of PLL '%s' is constrained to %.1f MHz\n", ctx->nameOf(ci), MHz(ctx, input_constr)); // Input divider (DIVR) - input_constr *= (int_or_default(packed->params, ctx->id("DIVR"), 0) + 1); + input_constr *= (int_or_default(packed->params, id_DIVR, 0) + 1); delay_t vco_constr = 0; delay_t outa_constr = 0, outb_constr = 0; int sr_div = 4; @@ -1452,7 +1441,7 @@ void pack_plls(Context *ctx) }; // Lookup shiftreg divider - int sr_div_mode = int_or_default(packed->params, ctx->id("SHIFTREG_DIV_MODE"), 0); + int sr_div_mode = int_or_default(packed->params, id_SHIFTREG_DIV_MODE, 0); switch (sr_div_mode) { case 0: sr_div = 4; @@ -1470,8 +1459,8 @@ void pack_plls(Context *ctx) } } // Determine dividers in VCO path - vco_constr = input_constr / (int_or_default(packed->params, ctx->id("DIVF"), 0) + 1); - divq = 1 << (int_or_default(packed->params, ctx->id("DIVQ"), 0)); + vco_constr = input_constr / (int_or_default(packed->params, id_DIVF, 0) + 1); + divq = 1 << (int_or_default(packed->params, id_DIVQ, 0)); if (fbp_value != "1") // anything other than SIMPLE - feedback after DIVQ vco_constr /= divq; if (fbp_value == "6") { // EXTERNAL @@ -1484,19 +1473,19 @@ void pack_plls(Context *ctx) } log_info(" VCO frequency of PLL '%s' is constrained to %.1f MHz\n", ctx->nameOf(ci), MHz(ctx, vco_constr)); - if (ci->type == ctx->id("SB_PLL40_2_PAD")) + if (ci->type == id_SB_PLL40_2_PAD) outa_constr = input_constr; // 2_PAD variant passes through input to OUTPUT A else - outa_constr = process_output(ctx->id("PLLOUT_SELECT_A")); - outb_constr = process_output(ctx->id("PLLOUT_SELECT_B")); - set_period(ctx, packed.get(), ctx->id("PLLOUT_A"), outa_constr); - set_period(ctx, packed.get(), ctx->id("PLLOUT_A_GLOBAL"), outa_constr); - set_period(ctx, packed.get(), ctx->id("PLLOUT_B"), outb_constr); - set_period(ctx, packed.get(), ctx->id("PLLOUT_B_GLOBAL"), outb_constr); + outa_constr = process_output(id_PLLOUT_SELECT_A); + outb_constr = process_output(id_PLLOUT_SELECT_B); + set_period(ctx, packed.get(), id_PLLOUT_A, outa_constr); + set_period(ctx, packed.get(), id_PLLOUT_A_GLOBAL, outa_constr); + set_period(ctx, packed.get(), id_PLLOUT_B, outb_constr); + set_period(ctx, packed.get(), id_PLLOUT_B_GLOBAL, outb_constr); } constr_fail: // PLL must have been placed already in place_plls() - BelId pll_bel = ctx->getBelByNameStr(packed->attrs[ctx->id("BEL")].as_string()); + BelId pll_bel = ctx->getBelByNameStr(packed->attrs[id_BEL].as_string()); NPNR_ASSERT(pll_bel != BelId()); // Deal with PAD PLL peculiarities @@ -1505,10 +1494,10 @@ void pack_plls(Context *ctx) auto pll_packagepin_driver = pad_packagepin_net->driver; NPNR_ASSERT(pll_packagepin_driver.cell != nullptr); auto packagepin_cell = pll_packagepin_driver.cell; - auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL")); + auto packagepin_bel_name = packagepin_cell->attrs.find(id_BEL); // Set an attribute about this PLL's PAD SB_IO. - packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second; + packed->attrs[id_BEL_PAD_INPUT] = packagepin_bel_name->second; // Disconnect PACKAGEPIN (it's a physical HW link) for (auto user : pad_packagepin_net->users) @@ -1522,7 +1511,7 @@ void pack_plls(Context *ctx) // In practice, this means the LOCK signal can only directly reach LUT // inputs. // If we have a net connected to LOCK, make sure it only drives LUTs. - auto port = packed->ports[ctx->id("LOCK")]; + auto port = packed->ports[id_LOCK]; if (port.net != nullptr) { log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx)); bool found_lut = false; @@ -1531,8 +1520,8 @@ void pack_plls(Context *ctx) unsigned int lut_count = 0; for (const auto &user : port.net->users) { NPNR_ASSERT(user.cell != nullptr); - if (user.cell->type == ctx->id("ICESTORM_LC")) { - if (bool_or_default(user.cell->params, ctx->id("CARRY_ENABLE"), false)) { + if (user.cell->type == id_ICESTORM_LC) { + if (bool_or_default(user.cell->params, id_CARRY_ENABLE, false)) { found_carry = true; all_luts = false; } else { @@ -1569,14 +1558,14 @@ void pack_plls(Context *ctx) int z = 0; for (const auto &user : port.net->users) { NPNR_ASSERT(user.cell != nullptr); - NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC")); + NPNR_ASSERT(user.cell->type == id_ICESTORM_LC); // TODO(q3k): handle when the Bel might be already the // target of another constraint. NPNR_ASSERT(z < 8); auto target_bel = ctx->getBelByLocation(Loc(x, y, z++)); auto target_bel_name = ctx->getBelName(target_bel).str(ctx); - user.cell->attrs[ctx->id("BEL")] = target_bel_name; + user.cell->attrs[id_BEL] = target_bel_name; log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str()); } } @@ -1586,9 +1575,9 @@ void pack_plls(Context *ctx) PortInfo &pi = port.second; bool is_b_port; - if (pi.name == ctx->id("PLLOUT_A_GLOBAL")) + if (pi.name == id_PLLOUT_A_GLOBAL) is_b_port = false; - else if (pi.name == ctx->id("PLLOUT_B_GLOBAL")) + else if (pi.name == id_PLLOUT_B_GLOBAL) is_b_port = true; else continue; @@ -1635,13 +1624,13 @@ bool Arch::pack() place_plls(ctx); pack_special(ctx); pack_plls(ctx); - if (!bool_or_default(ctx->settings, ctx->id("no_promote_globals"), false)) + if (!bool_or_default(ctx->settings, id_no_promote_globals, false)) promote_globals(ctx); ctx->assignArchInfo(); constrain_chains(ctx); ctx->fixupHierarchy(); ctx->assignArchInfo(); - ctx->settings[ctx->id("pack")] = 1; + ctx->settings[id_pack] = 1; archInfoToAttributes(); log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; diff --git a/ice40/pcf.cc b/ice40/pcf.cc index 1003c037cf..d03820da5d 100644 --- a/ice40/pcf.cc +++ b/ice40/pcf.cc @@ -57,9 +57,9 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in) if (setting == "-pullup") { const auto &value = words.at(++args_end); if (value == "yes" || value == "1") - extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP"), Property::State::S1)); + extra_attrs.emplace_back(std::make_pair(id_PULLUP, Property::State::S1)); else if (value == "no" || value == "0") - extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP"), Property::State::S0)); + extra_attrs.emplace_back(std::make_pair(id_PULLUP, Property::State::S0)); else log_error("Invalid value '%s' for -pullup (on line %d)\n", value.c_str(), lineno); } else if (setting == "-pullup_resistor") { @@ -68,7 +68,7 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in) log_error("Pullup resistance can only be set on UP5K/UP3K (on line %d)\n", lineno); if (value != "3P3K" && value != "6P8K" && value != "10K" && value != "100K") log_error("Invalid value '%s' for -pullup_resistor (on line %d)\n", value.c_str(), lineno); - extra_attrs.emplace_back(std::make_pair(ctx->id("PULLUP_RESISTOR"), value)); + extra_attrs.emplace_back(std::make_pair(id_PULLUP_RESISTOR, value)); } else if (setting == "-nowarn") { nowarn = true; } else if (setting == "--warn-no-port") { @@ -92,11 +92,11 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in) BelId pin_bel = ctx->get_package_pin_bel(pin); if (pin_bel == BelId()) log_error("package does not have a pin named '%s' (on line %d)\n", pin.c_str(), lineno); - if (fnd_cell->second->attrs.count(ctx->id("BEL"))) + if (fnd_cell->second->attrs.count(id_BEL)) log_error("duplicate pin constraint on '%s' (on line %d)\n", cell.c_str(), lineno); - fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx); + fnd_cell->second->attrs[id_BEL] = ctx->getBelName(pin_bel).str(ctx); log_info("constrained '%s' to bel '%s'\n", cell.c_str(), - fnd_cell->second->attrs[ctx->id("BEL")].as_string().c_str()); + fnd_cell->second->attrs[id_BEL].as_string().c_str()); for (const auto &attr : extra_attrs) fnd_cell->second->attrs[attr.first] = attr.second; } @@ -112,8 +112,8 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in) CellInfo *ci = cell.second.get(); if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { - if (!ci->attrs.count(ctx->id("BEL"))) { - if (bool_or_default(ctx->settings, ctx->id("pcf_allow_unconstrained"))) + if (!ci->attrs.count(id_BEL)) { + if (bool_or_default(ctx->settings, id_pcf_allow_unconstrained)) log_warning("IO '%s' is unconstrained in PCF and will be automatically placed\n", cell.first.c_str(ctx)); else diff --git a/machxo2/arch.cc b/machxo2/arch.cc index 6c0e48ced5..5fcdeaf759 100644 --- a/machxo2/arch.cc +++ b/machxo2/arch.cc @@ -171,17 +171,17 @@ std::string Arch::get_full_chip_name() const IdString Arch::archArgsToId(ArchArgs args) const { if (args.type == ArchArgs::LCMXO2_256HC) { - return id("lcmxo2_256hc"); + return id_lcmxo2_256hc; } else if (args.type == ArchArgs::LCMXO2_640HC) { - return id("lcmxo2_640hc"); + return id_lcmxo2_640hc; } else if (args.type == ArchArgs::LCMXO2_1200HC) { - return id("lcmxo2_1200hc"); + return id_lcmxo2_1200hc; } else if (args.type == ArchArgs::LCMXO2_2000HC) { - return id("lcmxo2_2000hc"); + return id_lcmxo2_2000hc; } else if (args.type == ArchArgs::LCMXO2_4000HC) { - return id("lcmxo2_4000hc"); + return id_lcmxo2_4000hc; } else if (args.type == ArchArgs::LCMXO2_7000HC) { - return id("lcmxo2_7000hc"); + return id_lcmxo2_7000hc; } return IdString(); @@ -414,17 +414,17 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bool Arch::place() { - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + std::string placer = str_or_default(settings, id_placer, defaultPlacer); if (placer == "sa") { bool retVal = placer1(getCtx(), Placer1Cfg(getCtx())); - getCtx()->settings[getCtx()->id("place")] = 1; + getCtx()->settings[id_place] = 1; archInfoToAttributes(); return retVal; } else if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); cfg.ioBufTypes.insert(id_FACADE_IO); bool retVal = placer_heap(getCtx(), cfg); - getCtx()->settings[getCtx()->id("place")] = 1; + getCtx()->settings[id_place] = 1; archInfoToAttributes(); return retVal; } else { @@ -434,7 +434,7 @@ bool Arch::place() bool Arch::route() { - std::string router = str_or_default(settings, id("router"), defaultRouter); + std::string router = str_or_default(settings, id_router, defaultRouter); bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); @@ -444,7 +444,7 @@ bool Arch::route() } else { log_error("MachXO2 architecture does not support router '%s'\n", router.c_str()); } - getCtx()->settings[getCtx()->id("route")] = 1; + getCtx()->settings[id_route] = 1; archInfoToAttributes(); return result; } diff --git a/machxo2/arch.h b/machxo2/arch.h index 29d8c3ffcc..347f4edec3 100644 --- a/machxo2/arch.h +++ b/machxo2/arch.h @@ -435,7 +435,7 @@ struct Arch : BaseArch // Extra helper std::string get_full_chip_name() const; - IdString archId() const override { return id("machxo2"); } + IdString archId() const override { return id_machxo2; } ArchArgs archArgs() const override { return args; } IdString archArgsToId(ArchArgs args) const override; diff --git a/machxo2/bitstream.cc b/machxo2/bitstream.cc index f624d91b61..9a711cbe45 100644 --- a/machxo2/bitstream.cc +++ b/machxo2/bitstream.cc @@ -235,36 +235,34 @@ void write_bitstream(Context *ctx, std::string text_config_file) int int_index = slice[5] - 'A'; NPNR_ASSERT(int_index >= 0 && int_index < 4); - int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); - int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); + int lut0_init = int_or_default(ci->params, id_LUT0_INITVAL); + int lut1_init = int_or_default(ci->params, id_LUT1_INITVAL); cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16)); cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16)); - cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC")); - cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); + cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, id_MODE, "LOGIC")); + cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED")); cc.tiles[tname].add_enum("LSR" + std::to_string(int_index) + ".SRMODE", - str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); - cc.tiles[tname].add_enum(slice + ".CEMUX", intstr_or_default(ci->params, ctx->id("CEMUX"), "1")); + str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); + cc.tiles[tname].add_enum(slice + ".CEMUX", intstr_or_default(ci->params, id_CEMUX, "1")); cc.tiles[tname].add_enum("CLK" + std::to_string(int_index) + ".CLKMUX", - intstr_or_default(ci->params, ctx->id("CLKMUX"), "0")); + intstr_or_default(ci->params, id_CLKMUX, "0")); cc.tiles[tname].add_enum("LSR" + std::to_string(int_index) + ".LSRMUX", - str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + str_or_default(ci->params, id_LSRMUX, "LSR")); cc.tiles[tname].add_enum("LSR" + std::to_string(int_index) + ".LSRONMUX", - intstr_or_default(ci->params, ctx->id("LSRONMUX"), "LSRMUX")); - cc.tiles[tname].add_enum(slice + ".REGMODE", str_or_default(ci->params, ctx->id("REGMODE"), "FF")); - cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, ctx->id("REG0_SD"), "0")); - cc.tiles[tname].add_enum(slice + ".REG1.SD", intstr_or_default(ci->params, ctx->id("REG1_SD"), "0")); - cc.tiles[tname].add_enum(slice + ".REG0.REGSET", - str_or_default(ci->params, ctx->id("REG0_REGSET"), "RESET")); - cc.tiles[tname].add_enum(slice + ".REG1.REGSET", - str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET")); - } else if (ci->type == ctx->id("FACADE_IO")) { + intstr_or_default(ci->params, id_LSRONMUX, "LSRMUX")); + cc.tiles[tname].add_enum(slice + ".REGMODE", str_or_default(ci->params, id_REGMODE, "FF")); + cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, id_REG0_SD, "0")); + cc.tiles[tname].add_enum(slice + ".REG1.SD", intstr_or_default(ci->params, id_REG1_SD, "0")); + cc.tiles[tname].add_enum(slice + ".REG0.REGSET", str_or_default(ci->params, id_REG0_REGSET, "RESET")); + cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, id_REG1_REGSET, "RESET")); + } else if (ci->type == id_FACADE_IO) { std::string pio = ctx->tile_info(bel)->bel_data[bel.index].name.get(); - std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); - std::string dir = str_or_default(ci->params, ctx->id("DIR"), "INPUT"); + std::string iotype = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); + std::string dir = str_or_default(ci->params, id_DIR, "INPUT"); std::string pic_tile = get_pic_tile(ctx, bel); cc.tiles[pic_tile].add_enum(pio + ".BASE_TYPE", dir + "_" + iotype); - } else if (ci->type == ctx->id("OSCH")) { - std::string freq = str_or_default(ci->params, ctx->id("NOM_FREQ"), "2.08"); + } else if (ci->type == id_OSCH) { + std::string freq = str_or_default(ci->params, id_NOM_FREQ, "2.08"); cc.tiles[ctx->get_tile_by_type("CFG1")].add_enum("OSCH.MODE", "OSCH"); cc.tiles[ctx->get_tile_by_type("CFG1")].add_enum("OSCH.NOM_FREQ", freq); } diff --git a/machxo2/cells.cc b/machxo2/cells.cc index bbe3f2d636..1c4f753be9 100644 --- a/machxo2/cells.cc +++ b/machxo2/cells.cc @@ -103,12 +103,12 @@ std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std:: new_cell->addOutput(id_WADO3); } else if (type == id_FACADE_IO) { new_cell->params[id_DIR] = std::string("INPUT"); - new_cell->attrs[ctx->id("IO_TYPE")] = std::string("LVCMOS33"); + new_cell->attrs[id_IO_TYPE] = std::string("LVCMOS33"); - new_cell->addInout(ctx->id("PAD")); - new_cell->addInput(ctx->id("I")); - new_cell->addInput(ctx->id("EN")); - new_cell->addOutput(ctx->id("O")); + new_cell->addInout(id_PAD); + new_cell->addInput(id_I); + new_cell->addInput(id_EN); + new_cell->addOutput(id_O); } else if (type == id_LUT4) { new_cell->params[id_INIT] = Property(0, 16); @@ -126,7 +126,7 @@ std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std:: void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) { - lc->params[ctx->id("LUT0_INITVAL")] = lut->params[ctx->id("INIT")]; + lc->params[id_LUT0_INITVAL] = lut->params[id_INIT]; for (std::string i : {"A", "B", "C", "D"}) { IdString lut_port = ctx->id(i); @@ -134,34 +134,34 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) replace_port(lut, lut_port, lc, lc_port); } - replace_port(lut, ctx->id("Z"), lc, ctx->id("F0")); + replace_port(lut, id_Z, lc, id_F0); } void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type) { // FIXME: This will have to change once we support FFs with reset value of 1. - lc->params[ctx->id("REG0_REGSET")] = std::string("RESET"); + lc->params[id_REG0_REGSET] = std::string("RESET"); - replace_port(dff, ctx->id("CLK"), lc, ctx->id("CLK")); - replace_port(dff, ctx->id("LSR"), lc, ctx->id("LSR")); - replace_port(dff, ctx->id("Q"), lc, ctx->id("Q0")); + replace_port(dff, id_CLK, lc, id_CLK); + replace_port(dff, id_LSR, lc, id_LSR); + replace_port(dff, id_Q, lc, id_Q0); if (lut_type == LutType::PassThru) { // If a register's DI port is fed by a constant, options for placing are // limited. Use the LUT to get around this. // LUT output will go to F0, which will feed back to DI0 input. - lc->params[ctx->id("LUT0_INITVAL")] = Property(0xAAAA, 16); - replace_port(dff, ctx->id("DI"), lc, ctx->id("A0")); - connect_ports(ctx, lc, ctx->id("F0"), lc, ctx->id("DI0")); + lc->params[id_LUT0_INITVAL] = Property(0xAAAA, 16); + replace_port(dff, id_DI, lc, id_A0); + connect_ports(ctx, lc, id_F0, lc, id_DI0); } else if (lut_type == LutType::None) { // If there is no LUT, use the M0 input because DI0 requires // going through the LUTs. - lc->params[ctx->id("REG0_SD")] = std::string("0"); - replace_port(dff, ctx->id("DI"), lc, ctx->id("M0")); + lc->params[id_REG0_SD] = std::string("0"); + replace_port(dff, id_DI, lc, id_M0); } else { // Otherwise, there's a LUT being used in the slice and mapping DI to // DI0 input is fine. - replace_port(dff, ctx->id("DI"), lc, ctx->id("DI0")); + replace_port(dff, id_DI, lc, id_DI0); } } diff --git a/machxo2/cells.h b/machxo2/cells.h index 409b68b118..7a9bab9267 100644 --- a/machxo2/cells.h +++ b/machxo2/cells.h @@ -40,12 +40,12 @@ enum class LutType std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std::string name = ""); // Return true if a cell is a LUT -inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("LUT4"); } +inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_LUT4; } // Return true if a cell is a flipflop -inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("FACADE_FF"); } +inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_FACADE_FF; } -inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("FACADE_SLICE"); } +inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_FACADE_SLICE; } // Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports // as needed. Set no_dff if a DFF is not being used, so that the output diff --git a/machxo2/constids.inc b/machxo2/constids.inc index b2ff51ae59..b216f8b4d1 100644 --- a/machxo2/constids.inc +++ b/machxo2/constids.inc @@ -116,3 +116,22 @@ X(CLK0) X(CLK1) X(SEL) X(DCMOUT) + +X(BEL) +X(GND) +X(IO_TYPE) +X(LOC) +X(NOM_FREQ) +X(VCC) +X(lcmxo2_1200hc) +X(lcmxo2_2000hc) +X(lcmxo2_256hc) +X(lcmxo2_4000hc) +X(lcmxo2_640hc) +X(lcmxo2_7000hc) +X(machxo2) +X(pack) +X(place) +X(placer) +X(route) +X(router) diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 0c2c945922..de6078656a 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -51,20 +51,20 @@ static void pack_lut_lutffs(Context *ctx) // LUT4 drives more than one FF. NetInfo *o = ci->ports.at(id_Z).net; CellInfo *dff = net_only_drives(ctx, o, is_ff, id_DI, false); - auto lut_bel = ci->attrs.find(ctx->id("BEL")); + auto lut_bel = ci->attrs.find(id_BEL); bool packed_dff = false; if (dff) { if (ctx->verbose) log_info("found attached dff %s\n", dff->name.c_str(ctx)); - auto dff_bel = dff->attrs.find(ctx->id("BEL")); + auto dff_bel = dff->attrs.find(id_BEL); if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { // Locations don't match, can't pack } else { lut_to_lc(ctx, ci, packed.get(), false); dff_to_lc(ctx, dff, packed.get(), LutType::Normal); if (dff_bel != dff->attrs.end()) - packed->attrs[ctx->id("BEL")] = dff_bel->second; + packed->attrs[id_BEL] = dff_bel->second; packed_cells.insert(dff->name); if (ctx->verbose) log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx)); @@ -104,12 +104,12 @@ static void pack_remaining_ffs(Context *ctx) for (auto &attr : ci->attrs) packed->attrs[attr.first] = attr.second; - auto dff_bel = ci->attrs.find(ctx->id("BEL")); + auto dff_bel = ci->attrs.find(id_BEL); dff_to_lc(ctx, ci, packed.get(), LutType::None); if (dff_bel != ci->attrs.end()) - packed->attrs[ctx->id("BEL")] = dff_bel->second; + packed->attrs[id_BEL] = dff_bel->second; packed_cells.insert(ci->name); if (ctx->verbose) log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx)); @@ -198,12 +198,12 @@ static void pack_constants(Context *ctx) for (auto &net : ctx->nets) { NetInfo *ni = net.second.get(); - if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) { + if (ni->driver.cell != nullptr && ni->driver.cell->type == id_GND) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, gnd_net, false); dead_nets.push_back(net.first); ctx->cells.erase(drv_cell); - } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) { + } else if (ni->driver.cell != nullptr && ni->driver.cell->type == id_VCC) { IdString drv_cell = ni->driver.cell->name; set_net_constant(ctx, ni, vcc_net, true); dead_nets.push_back(net.first); @@ -294,8 +294,8 @@ static void pack_io(Context *ctx) // attribute already on a FACADE_IO is an error. Attributes on // the pin attached to the PAD of FACADE_IO are ignored by this // packing phase. - auto loc_attr_cell = ci->attrs.find(ctx->id("LOC")); - auto bel_attr_cell = ci->attrs.find(ctx->id("BEL")); + auto loc_attr_cell = ci->attrs.find(id_LOC); + auto bel_attr_cell = ci->attrs.find(id_BEL); if (loc_attr_cell != ci->attrs.end()) { if (bel_attr_cell != ci->attrs.end()) { @@ -312,7 +312,7 @@ static void pack_io(Context *ctx) } else { log_info("pin '%s' constrained to Bel '%s'.\n", ci->name.c_str(ctx), ctx->nameOfBel(pinBel)); } - ci->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx); + ci->attrs[id_BEL] = ctx->getBelName(pinBel).str(ctx); } } } @@ -332,7 +332,7 @@ bool Arch::pack() pack_io(ctx); pack_lut_lutffs(ctx); pack_remaining_ffs(ctx); - ctx->settings[ctx->id("pack")] = 1; + ctx->settings[id_pack] = 1; ctx->assignArchInfo(); log_info("Checksum: 0x%08x\n", ctx->checksum()); return true; diff --git a/mistral/arch.cc b/mistral/arch.cc index c50e30f49b..e79a391038 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -446,7 +446,7 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const bool Arch::place() { - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + std::string placer = str_or_default(settings, id_placer, defaultPlacer); if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); @@ -468,7 +468,7 @@ bool Arch::place() log_error("Mistral architecture does not support placer '%s'\n", placer.c_str()); } - getCtx()->attrs[getCtx()->id("step")] = std::string("place"); + getCtx()->attrs[id_step] = std::string("place"); archInfoToAttributes(); return true; } @@ -479,7 +479,7 @@ bool Arch::route() lab_pre_route(); - std::string router = str_or_default(settings, id("router"), defaultRouter); + std::string router = str_or_default(settings, id_router, defaultRouter); bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); @@ -489,7 +489,7 @@ bool Arch::route() } else { log_error("Mistral architecture does not support router '%s'\n", router.c_str()); } - getCtx()->attrs[getCtx()->id("step")] = std::string("route"); + getCtx()->attrs[id_step] = std::string("route"); archInfoToAttributes(); return result; } diff --git a/mistral/constids.inc b/mistral/constids.inc index 2aa2c15739..d241c8ef2d 100644 --- a/mistral/constids.inc +++ b/mistral/constids.inc @@ -97,3 +97,11 @@ X(WE_INV) X(cyclonev_oscillator) X(cyclonev_hps_interface_mpu_general_purpose) + +X(clkout) +X(clkout1) +X(compress_rbf) +X(oscena) +X(placer) +X(router) +X(step) diff --git a/mistral/globals.cc b/mistral/globals.cc index d41b2afe62..1ba709191e 100644 --- a/mistral/globals.cc +++ b/mistral/globals.cc @@ -58,9 +58,9 @@ void Arch::create_hps_mpu_general_purpose(int x, int y) void Arch::create_control(int x, int y) { BelId oscillator_bel = add_bel(x, y, id_cyclonev_oscillator, id_cyclonev_oscillator); - add_bel_pin(oscillator_bel, id("oscena"), PORT_IN, get_port(CycloneV::CTRL, x, y, -1, CycloneV::OSC_ENA, -1)); - add_bel_pin(oscillator_bel, id("clkout"), PORT_OUT, get_port(CycloneV::CTRL, x, y, -1, CycloneV::CLK_OUT, -1)); - add_bel_pin(oscillator_bel, id("clkout1"), PORT_OUT, get_port(CycloneV::CTRL, x, y, -1, CycloneV::CLK_OUT1, -1)); + add_bel_pin(oscillator_bel, id_oscena, PORT_IN, get_port(CycloneV::CTRL, x, y, -1, CycloneV::OSC_ENA, -1)); + add_bel_pin(oscillator_bel, id_clkout, PORT_OUT, get_port(CycloneV::CTRL, x, y, -1, CycloneV::CLK_OUT, -1)); + add_bel_pin(oscillator_bel, id_clkout1, PORT_OUT, get_port(CycloneV::CTRL, x, y, -1, CycloneV::CLK_OUT1, -1)); } NEXTPNR_NAMESPACE_END diff --git a/mistral/main.cc b/mistral/main.cc index bd9ef59158..34bf759ee5 100644 --- a/mistral/main.cc +++ b/mistral/main.cc @@ -79,7 +79,7 @@ std::unique_ptr MistralCommandHandler::createContext(dict(); auto ctx = std::unique_ptr(new Context(chipArgs)); if (vm.count("compress-rbf")) - ctx->settings[ctx->id("compress_rbf")] = Property::State::S1; + ctx->settings[id_compress_rbf] = Property::State::S1; return ctx; } diff --git a/nexus/arch.cc b/nexus/arch.cc index d553200fe0..cb8cacf8d0 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -186,8 +186,8 @@ Arch::Arch(ArchArgs args) : args(args) disabled_pips.insert(pip); } // TODO: find a better solution to disable these - WireId dcs_out = getWireByName( - IdStringList(std::array{x_ids.at(37), y_ids.at(10), id("JDCSOUT_DCS_DCSIP")})); + WireId dcs_out = + getWireByName(IdStringList(std::array{x_ids.at(37), y_ids.at(10), id_JDCSOUT_DCS_DCSIP})); for (auto dcs_pip : getPipsUphill(dcs_out)) disabled_pips.insert(dcs_pip); NPNR_ASSERT(disabled_pips.size() == 6); @@ -300,13 +300,13 @@ std::vector> Arch::getBelAttrs(BelId bel) const { std::vector> ret; - ret.emplace_back(id("INDEX"), stringf("%d", bel.index)); + ret.emplace_back(id_INDEX, stringf("%d", bel.index)); - ret.emplace_back(id("GRID_X"), stringf("%d", bel.tile % chip_info->width)); - ret.emplace_back(id("GRID_Y"), stringf("%d", bel.tile / chip_info->width)); - ret.emplace_back(id("BEL_Z"), stringf("%d", bel_data(bel).z)); + ret.emplace_back(id_GRID_X, stringf("%d", bel.tile % chip_info->width)); + ret.emplace_back(id_GRID_Y, stringf("%d", bel.tile / chip_info->width)); + ret.emplace_back(id_BEL_Z, stringf("%d", bel_data(bel).z)); - ret.emplace_back(id("BEL_TYPE"), nameOf(getBelType(bel))); + ret.emplace_back(id_BEL_TYPE, nameOf(getBelType(bel))); return ret; } @@ -337,11 +337,11 @@ std::vector> Arch::getWireAttrs(WireId wire) co { std::vector> ret; - ret.emplace_back(id("INDEX"), stringf("%d", wire.index)); + ret.emplace_back(id_INDEX, stringf("%d", wire.index)); - ret.emplace_back(id("GRID_X"), stringf("%d", wire.tile % chip_info->width)); - ret.emplace_back(id("GRID_Y"), stringf("%d", wire.tile / chip_info->width)); - ret.emplace_back(id("FLAGS"), stringf("%u", wire_data(wire).flags)); + ret.emplace_back(id_GRID_X, stringf("%d", wire.tile % chip_info->width)); + ret.emplace_back(id_GRID_Y, stringf("%d", wire.tile / chip_info->width)); + ret.emplace_back(id_FLAGS, stringf("%u", wire_data(wire).flags)); return ret; } @@ -388,13 +388,13 @@ std::vector> Arch::getPipAttrs(PipId pip) const { std::vector> ret; - ret.emplace_back(id("INDEX"), stringf("%d", pip.index)); + ret.emplace_back(id_INDEX, stringf("%d", pip.index)); - ret.emplace_back(id("GRID_X"), stringf("%d", pip.tile % chip_info->width)); - ret.emplace_back(id("GRID_Y"), stringf("%d", pip.tile / chip_info->width)); + ret.emplace_back(id_GRID_X, stringf("%d", pip.tile % chip_info->width)); + ret.emplace_back(id_GRID_Y, stringf("%d", pip.tile / chip_info->width)); - ret.emplace_back(id("FROM_TILE_WIRE"), nameOf(IdString(loc_data(pip).wires[pip_data(pip).from_wire].name))); - ret.emplace_back(id("TO_TILE_WIRE"), nameOf(IdString(loc_data(pip).wires[pip_data(pip).to_wire].name))); + ret.emplace_back(id_FROM_TILE_WIRE, nameOf(IdString(loc_data(pip).wires[pip_data(pip).from_wire].name))); + ret.emplace_back(id_TO_TILE_WIRE, nameOf(IdString(loc_data(pip).wires[pip_data(pip).to_wire].name))); return ret; } @@ -665,7 +665,7 @@ bool Arch::place() if (getCtx()->settings.count(getCtx()->id("estimate-delay-mult"))) estimate_delay_mult = getCtx()->setting("estimate-delay-mult"); - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); + std::string placer = str_or_default(settings, id_placer, defaultPlacer); if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); @@ -689,7 +689,7 @@ bool Arch::place() post_place_opt(); - getCtx()->attrs[getCtx()->id("step")] = std::string("place"); + getCtx()->attrs[id_step] = std::string("place"); archInfoToAttributes(); return true; } @@ -740,7 +740,7 @@ bool Arch::route() route_globals(); - std::string router = str_or_default(settings, id("router"), defaultRouter); + std::string router = str_or_default(settings, id_router, defaultRouter); bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); @@ -752,7 +752,7 @@ bool Arch::route() } else { log_error("Nexus architecture does not support router '%s'\n", router.c_str()); } - getCtx()->attrs[getCtx()->id("step")] = std::string("route"); + getCtx()->attrs[id_step] = std::string("route"); archInfoToAttributes(); return result; } diff --git a/nexus/constids.inc b/nexus/constids.inc index ca6bed5a23..11726d3b1e 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -531,3 +531,24 @@ X(Q1) X(SCLK) X(LOCAL_VCC) + +X(BEL_TYPE) +X(BEL_Z) +X(CEOUTMUX) +X(CLAMP) +X(FLAGS) +X(FROM_TILE_WIRE) +X(GLITCHFILTER) +X(GRID_X) +X(GRID_Y) +X(INDEX) +X(JDCSOUT_DCS_DCSIP) +X(PULLMODE) +X(TO_TILE_WIRE) +X(carry_lutff_ratio) +X(no_pack_lutff) +X(no_post_place_opt) +X(placer) +X(router) +X(step) +X(syn_useioff) diff --git a/nexus/main.cc b/nexus/main.cc index 88dddfe598..bca6661f66 100644 --- a/nexus/main.cc +++ b/nexus/main.cc @@ -79,15 +79,15 @@ std::unique_ptr NexusCommandHandler::createContext(dict(); auto ctx = std::unique_ptr(new Context(chipArgs)); if (vm.count("no-post-place-opt")) - ctx->settings[ctx->id("no_post_place_opt")] = Property::State::S1; + ctx->settings[id_no_post_place_opt] = Property::State::S1; if (vm.count("no-pack-lutff")) - ctx->settings[ctx->id("no_pack_lutff")] = Property::State::S1; + ctx->settings[id_no_pack_lutff] = Property::State::S1; if (vm.count("carry-lutff-ratio")) { float ratio = vm["carry-lutff-ratio"].as(); if (ratio < 0.0f || ratio > 1.0f) { log_error("Carry LUT+FF packing ration must be between 0.0 and 1.0"); } - ctx->settings[ctx->id("carry_lutff_ratio")] = ratio; + ctx->settings[id_carry_lutff_ratio] = ratio; } if (vm.count("estimate-delay-mult")) ctx->settings[ctx->id("estimate-delay-mult")] = vm["estimate-delay-mult"].as(); diff --git a/nexus/pack.cc b/nexus/pack.cc index 81a729b187..13522c78c7 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1132,7 +1132,7 @@ struct NexusPacker CellInfo *ci = cell.second.get(); if (ci->type != id_LRAM_CORE) continue; - if (str_or_default(ci->params, ctx->id("ECC_BYTE_SEL"), "BYTE_EN") == "BYTE_EN") + if (str_or_default(ci->params, id_ECC_BYTE_SEL, "BYTE_EN") == "BYTE_EN") continue; for (int i = 0; i < 0x80; i++) { // FIXME: document ECC and remove this DRC @@ -1986,7 +1986,7 @@ struct NexusPacker if (ci->type == id_DCC) { copy_constraint(ci, id_CLKI, id_CLKO, 1); } else if (ci->type == id_OSC_CORE) { - int div = int_or_default(ci->params, ctx->id("HF_CLK_DIV"), 128); + int div = int_or_default(ci->params, id_HF_CLK_DIV, 128); const float tol = 1.07f; // OSCA has +/-7% frequency tolerance, assume the worst case. set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1) / tol)); set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10) / tol)); @@ -2177,26 +2177,26 @@ struct NexusPacker // We have IDDR+ODDR if (isODDR && isIDDR) { - if (!iob->attrs.count(ctx->id("GLITCHFILTER"))) { - iob->attrs[ctx->id("GLITCHFILTER")] = std::string("ON"); + if (!iob->attrs.count(id_GLITCHFILTER)) { + iob->attrs[id_GLITCHFILTER] = std::string("ON"); } - if (!iob->attrs.count(ctx->id("CLAMP"))) { - iob->attrs[ctx->id("CLAMP")] = std::string("ON"); + if (!iob->attrs.count(id_CLAMP)) { + iob->attrs[id_CLAMP] = std::string("ON"); } - if (!iob->attrs.count(ctx->id("PULLMODE"))) { - iob->attrs[ctx->id("PULLMODE")] = std::string("DOWN"); + if (!iob->attrs.count(id_PULLMODE)) { + iob->attrs[id_PULLMODE] = std::string("DOWN"); } } // We have ODDR only else if (isODDR && !isIDDR) { - if (!iob->attrs.count(ctx->id("GLITCHFILTER"))) { - iob->attrs[ctx->id("GLITCHFILTER")] = std::string("OFF"); + if (!iob->attrs.count(id_GLITCHFILTER)) { + iob->attrs[id_GLITCHFILTER] = std::string("OFF"); } - if (!iob->attrs.count(ctx->id("CLAMP"))) { - iob->attrs[ctx->id("CLAMP")] = std::string("OFF"); + if (!iob->attrs.count(id_CLAMP)) { + iob->attrs[id_CLAMP] = std::string("OFF"); } - if (!iob->attrs.count(ctx->id("PULLMODE"))) { - iob->attrs[ctx->id("PULLMODE")] = std::string("NONE"); + if (!iob->attrs.count(id_PULLMODE)) { + iob->attrs[id_PULLMODE] = std::string("NONE"); } } @@ -2237,8 +2237,8 @@ struct NexusPacker // Check if the net wants to use the T flip-flop in // IOLOGIC bool syn_useioff = false; - if (iob_t->attrs.count(ctx->id("syn_useioff"))) { - syn_useioff = iob_t->attrs.at(ctx->id("syn_useioff")).as_bool(); + if (iob_t->attrs.count(id_syn_useioff)) { + syn_useioff = iob_t->attrs.at(id_syn_useioff).as_bool(); } // Check if the T input is driven by a flip-flop. Store @@ -2281,7 +2281,7 @@ struct NexusPacker iol->params[id_REGSET] = ff->params.at(id_REGSET); // Enable the TSREG - iol->params[ctx->id("CEOUTMUX")] = std::string("1"); + iol->params[id_CEOUTMUX] = std::string("1"); iol->params[ctx->id("TSREG.REGSET")] = std::string("SET"); iol->params[ctx->id("IDDRX1_ODDRX1.TRISTATE")] = std::string("ENABLED"); } @@ -2328,7 +2328,7 @@ struct NexusPacker log_info("Inferring LUT+FF pairs...\n"); float carry_ratio = 1.0f; - if (ctx->settings.find(ctx->id("carry_lutff_ratio")) != ctx->settings.end()) { + if (ctx->settings.find(id_carry_lutff_ratio) != ctx->settings.end()) { carry_ratio = ctx->setting("carry_lutff_ratio"); } @@ -2487,7 +2487,7 @@ struct NexusPacker pack_ip(); handle_iologic(); - if (!bool_or_default(ctx->settings, ctx->id("no_pack_lutff"))) { + if (!bool_or_default(ctx->settings, id_no_pack_lutff)) { pack_lutffs(); } @@ -2500,7 +2500,7 @@ struct NexusPacker bool Arch::pack() { (NexusPacker(getCtx()))(); - attrs[id("step")] = std::string("pack"); + attrs[id_step] = std::string("pack"); archInfoToAttributes(); assignArchInfo(); return true; diff --git a/nexus/post_place.cc b/nexus/post_place.cc index eece4008ea..f42c0f1892 100644 --- a/nexus/post_place.cc +++ b/nexus/post_place.cc @@ -148,7 +148,7 @@ struct NexusPostPlaceOpt void Arch::post_place_opt() { - if (bool_or_default(settings, id("no_post_place_opt"))) + if (bool_or_default(settings, id_no_post_place_opt)) return; log_info("Running post-place optimisations...\n"); NexusPostPlaceOpt opt(getCtx()); From 6a32aca4ac8705b637943c236cedd2f36422fb21 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 18 Feb 2022 10:52:37 +0000 Subject: [PATCH 069/712] refactor: New member functions to replace design_utils Signed-off-by: gatecat --- common/basectx.cc | 15 +- common/basectx.h | 1 + common/design_utils.cc | 143 ------------ common/design_utils.h | 26 --- common/nextpnr_types.cc | 133 +++++++++++ common/nextpnr_types.h | 21 ++ ecp5/arch_place.cc | 4 +- ecp5/bitstream.cc | 28 ++- ecp5/cells.cc | 139 +++++------ ecp5/globals.cc | 6 +- ecp5/pack.cc | 299 ++++++++++++------------ fpga_interchange/arch_pack_clusters.cc | 2 +- fpga_interchange/macros.cc | 6 +- fpga_interchange/site_router.cc | 2 +- frontend/frontend_base.h | 18 +- generic/arch.cc | 2 +- generic/cells.cc | 22 +- generic/pack.cc | 2 +- generic/viaduct/example/example.cc | 6 +- generic/viaduct_helpers.cc | 8 +- gowin/arch.cc | 6 +- gowin/cells.cc | 38 +-- gowin/pack.cc | 66 +++--- ice40/arch.cc | 14 +- ice40/cells.cc | 47 ++-- ice40/pack.cc | 36 +-- machxo2/cells.cc | 18 +- machxo2/pack.cc | 2 +- mistral/bitstream.cc | 9 +- mistral/lab.cc | 46 ++-- mistral/pack.cc | 42 ++-- nexus/fasm.cc | 8 +- nexus/pack.cc | 310 ++++++++++++------------- nexus/post_place.cc | 8 +- 34 files changed, 765 insertions(+), 768 deletions(-) diff --git a/common/basectx.cc b/common/basectx.cc index b55cd072ab..83a2deea7b 100644 --- a/common/basectx.cc +++ b/common/basectx.cc @@ -22,7 +22,6 @@ #include #include "context.h" -#include "design_utils.h" #include "log.h" NEXTPNR_NAMESPACE_BEGIN @@ -223,13 +222,23 @@ void BaseCtx::connectPort(IdString net, IdString cell, IdString port) { NetInfo *net_info = getNetByAlias(net); CellInfo *cell_info = cells.at(cell).get(); - connect_port(getCtx(), net_info, cell_info, port); + cell_info->connectPort(port, net_info); } void BaseCtx::disconnectPort(IdString cell, IdString port) { CellInfo *cell_info = cells.at(cell).get(); - disconnect_port(getCtx(), cell_info, port); + cell_info->disconnectPort(port); +} + +void BaseCtx::renameNet(IdString old_name, IdString new_name) +{ + NetInfo *net = nets.at(old_name).get(); + NPNR_ASSERT(!nets.count(new_name)); + nets[new_name]; + std::swap(nets.at(net->name), nets.at(new_name)); + nets.erase(net->name); + net->name = new_name; } void BaseCtx::ripupNet(IdString name) diff --git a/common/basectx.h b/common/basectx.h index 507f29cde9..21d6d63a9b 100644 --- a/common/basectx.h +++ b/common/basectx.h @@ -226,6 +226,7 @@ struct BaseCtx void disconnectPort(IdString cell, IdString port); void ripupNet(IdString name); void lockNetRouting(IdString name); + void renameNet(IdString old_name, IdString new_name); CellInfo *createCell(IdString name, IdString type); void copyBelPorts(IdString cell, BelId bel); diff --git a/common/design_utils.cc b/common/design_utils.cc index 9432b6cd71..f52cc30485 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -25,42 +25,6 @@ #include "util.h" NEXTPNR_NAMESPACE_BEGIN -void replace_port(CellInfo *old_cell, IdString old_name, CellInfo *rep_cell, IdString rep_name) -{ - if (!old_cell->ports.count(old_name)) - return; - PortInfo &old = old_cell->ports.at(old_name); - - // Create port on the replacement cell if it doesn't already exist - if (!rep_cell->ports.count(rep_name)) { - rep_cell->ports[rep_name].name = rep_name; - rep_cell->ports[rep_name].type = old.type; - } - - PortInfo &rep = rep_cell->ports.at(rep_name); - NPNR_ASSERT(old.type == rep.type); - - rep.net = old.net; - old.net = nullptr; - if (rep.type == PORT_OUT) { - if (rep.net != nullptr) { - rep.net->driver.cell = rep_cell; - rep.net->driver.port = rep_name; - } - } else if (rep.type == PORT_IN) { - if (rep.net != nullptr) { - for (PortRef &load : rep.net->users) { - if (load.cell == old_cell && load.port == old_name) { - load.cell = rep_cell; - load.port = rep_name; - } - } - } - } else { - NPNR_ASSERT(false); - } -} - // Print utilisation of a design void print_utilisation(const Context *ctx) { @@ -85,111 +49,4 @@ void print_utilisation(const Context *ctx) log_break(); } -// Connect a net to a port -void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name) -{ - if (net == nullptr) - return; - PortInfo &port = cell->ports.at(port_name); - NPNR_ASSERT(port.net == nullptr); - port.net = net; - if (port.type == PORT_OUT) { - NPNR_ASSERT(net->driver.cell == nullptr); - net->driver.cell = cell; - net->driver.port = port_name; - } else if (port.type == PORT_IN || port.type == PORT_INOUT) { - PortRef user; - user.cell = cell; - user.port = port_name; - net->users.push_back(user); - } else { - NPNR_ASSERT_FALSE("invalid port type for connect_port"); - } -} - -void disconnect_port(const Context *ctx, CellInfo *cell, IdString port_name) -{ - if (!cell->ports.count(port_name)) - return; - PortInfo &port = cell->ports.at(port_name); - if (port.net != nullptr) { - port.net->users.erase(std::remove_if(port.net->users.begin(), port.net->users.end(), - [cell, port_name](const PortRef &user) { - return user.cell == cell && user.port == port_name; - }), - port.net->users.end()); - if (port.net->driver.cell == cell && port.net->driver.port == port_name) - port.net->driver.cell = nullptr; - port.net = nullptr; - } -} - -void connect_ports(Context *ctx, CellInfo *cell1, IdString port1_name, CellInfo *cell2, IdString port2_name) -{ - PortInfo &port1 = cell1->ports.at(port1_name); - if (port1.net == nullptr) { - // No net on port1; need to create one - NetInfo *p1net = ctx->createNet(ctx->id(cell1->name.str(ctx) + "$conn$" + port1_name.str(ctx))); - connect_port(ctx, p1net, cell1, port1_name); - } - connect_port(ctx, port1.net, cell2, port2_name); -} - -void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_name) -{ - if (!cell->ports.count(old_name)) - return; - PortInfo pi = cell->ports.at(old_name); - if (pi.net != nullptr) { - if (pi.net->driver.cell == cell && pi.net->driver.port == old_name) - pi.net->driver.port = new_name; - for (auto &usr : pi.net->users) - if (usr.cell == cell && usr.port == old_name) - usr.port = new_name; - } - cell->ports.erase(old_name); - pi.name = new_name; - cell->ports[new_name] = pi; -} - -void rename_net(Context *ctx, NetInfo *net, IdString new_name) -{ - if (net == nullptr) - return; - NPNR_ASSERT(!ctx->nets.count(new_name)); - ctx->nets[new_name]; - std::swap(ctx->nets.at(net->name), ctx->nets.at(new_name)); - ctx->nets.erase(net->name); - net->name = new_name; -} - -void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, - CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width) -{ - for (int i = 0; i < width; i++) { - IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); - IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); - replace_port(old_cell, old_port, new_cell, new_port); - } -} - -void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *new_cell, IdString new_name) -{ - if (!old_cell->ports.count(old_name)) - return; - new_cell->ports[new_name].name = new_name; - new_cell->ports[new_name].type = old_cell->ports.at(old_name).type; - connect_port(ctx, old_cell->ports.at(old_name).net, new_cell, new_name); -} - -void copy_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, - CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width) -{ - for (int i = 0; i < width; i++) { - IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); - IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); - copy_port(ctx, old_cell, old_port, new_cell, new_port); - } -} - NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index 82c9ac4530..63cb71d7b3 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -89,34 +89,8 @@ inline bool port_used(CellInfo *cell, IdString port_name) return port_fnd != cell->ports.end() && port_fnd->second.net != nullptr; } -// Connect a net to a port -void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name); - -// Disconnect a net from a port -void disconnect_port(const Context *ctx, CellInfo *cell, IdString port_name); - -// Connect two ports together -void connect_ports(Context *ctx, CellInfo *cell1, IdString port1_name, CellInfo *cell2, IdString port2_name); - -// Rename a port if it exists on a cell -void rename_port(Context *ctx, CellInfo *cell, IdString old_name, IdString new_name); - -// Rename a net without invalidating pointers to it -void rename_net(Context *ctx, NetInfo *net, IdString new_name); - void print_utilisation(const Context *ctx); -// Disconnect a bus of nets (if connected) from old, and connect it to the new ports -void replace_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, - CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); - -// Copy a bus of nets (if connected) from old, and connect it to the new ports -void copy_bus(Context *ctx, CellInfo *old_cell, IdString old_name, int old_offset, bool old_brackets, - CellInfo *new_cell, IdString new_name, int new_offset, bool new_brackets, int width); - -// Copy a port from one cell to another -void copy_port(Context *ctx, CellInfo *old_cell, IdString old_name, CellInfo *new_cell, IdString new_name); - NEXTPNR_NAMESPACE_END #endif diff --git a/common/nextpnr_types.cc b/common/nextpnr_types.cc index 3deed46fe5..c89a007102 100644 --- a/common/nextpnr_types.cc +++ b/common/nextpnr_types.cc @@ -18,6 +18,8 @@ */ #include "nextpnr_types.h" +#include "context.h" +#include "log.h" #include "nextpnr_namespaces.h" @@ -49,4 +51,135 @@ bool CellInfo::testRegion(BelId bel) const return region == nullptr || !region->constr_bels || region->bels.count(bel); } +void CellInfo::connectPort(IdString port_name, NetInfo *net) +{ + if (net == nullptr) + return; + PortInfo &port = ports.at(port_name); + NPNR_ASSERT(port.net == nullptr); + port.net = net; + if (port.type == PORT_OUT) { + NPNR_ASSERT(net->driver.cell == nullptr); + net->driver.cell = this; + net->driver.port = port_name; + } else if (port.type == PORT_IN || port.type == PORT_INOUT) { + PortRef user; + user.cell = this; + user.port = port_name; + net->users.push_back(user); + } else { + NPNR_ASSERT_FALSE("invalid port type for connect_port"); + } +} + +void CellInfo::disconnectPort(IdString port_name) +{ + if (!ports.count(port_name)) + return; + PortInfo &port = ports.at(port_name); + if (port.net != nullptr) { + port.net->users.erase(std::remove_if(port.net->users.begin(), port.net->users.end(), + [this, port_name](const PortRef &user) { + return user.cell == this && user.port == port_name; + }), + port.net->users.end()); + if (port.net->driver.cell == this && port.net->driver.port == port_name) + port.net->driver.cell = nullptr; + port.net = nullptr; + } +} + +void CellInfo::connectPorts(IdString port, CellInfo *other, IdString other_port) +{ + PortInfo &port1 = ports.at(port); + if (port1.net == nullptr) { + // No net on port1; need to create one + NetInfo *p1net = ctx->createNet(ctx->id(name.str(ctx) + "$conn$" + port.str(ctx))); + connectPort(port, p1net); + } + other->connectPort(other_port, port1.net); +} + +void CellInfo::movePortTo(IdString port, CellInfo *other, IdString other_port) +{ + if (!ports.count(port)) + return; + PortInfo &old = ports.at(port); + + // Create port on the replacement cell if it doesn't already exist + if (!other->ports.count(other_port)) { + other->ports[other_port].name = other_port; + other->ports[other_port].type = old.type; + } + + PortInfo &rep = other->ports.at(other_port); + NPNR_ASSERT(old.type == rep.type); + + rep.net = old.net; + old.net = nullptr; + if (rep.type == PORT_OUT) { + if (rep.net != nullptr) { + rep.net->driver.cell = other; + rep.net->driver.port = other_port; + } + } else if (rep.type == PORT_IN) { + if (rep.net != nullptr) { + for (PortRef &load : rep.net->users) { + if (load.cell == this && load.port == port) { + load.cell = other; + load.port = other_port; + } + } + } + } else { + NPNR_ASSERT(false); + } +} + +void CellInfo::renamePort(IdString old_name, IdString new_name) +{ + if (!ports.count(old_name)) + return; + PortInfo pi = ports.at(old_name); + if (pi.net != nullptr) { + if (pi.net->driver.cell == this && pi.net->driver.port == old_name) + pi.net->driver.port = new_name; + for (auto &usr : pi.net->users) + if (usr.cell == this && usr.port == old_name) + usr.port = new_name; + } + ports.erase(old_name); + pi.name = new_name; + ports[new_name] = pi; +} + +void CellInfo::movePortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, + IdString new_name, int new_offset, bool new_brackets, int width) +{ + for (int i = 0; i < width; i++) { + IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); + IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + movePortTo(old_port, new_cell, new_port); + } +} + +void CellInfo::copyPortTo(IdString port, CellInfo *other, IdString other_port) +{ + if (!ports.count(port)) + return; + other->ports[other_port].name = other_port; + other->ports[other_port].type = ports.at(port).type; + other->connectPort(other_port, ports.at(port).net); +} + +void CellInfo::copyPortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, + IdString new_name, int new_offset, bool new_brackets, int width) +{ + for (int i = 0; i < width; i++) { + IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); + IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + copyPortTo(old_port, new_cell, new_port); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 6debd2b8df..cf93a0714d 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -187,6 +187,27 @@ struct CellInfo : ArchCellInfo void unsetAttr(IdString name); // check whether a bel complies with the cell's region constraint bool testRegion(BelId bel) const; + + NetInfo *getPort(IdString name) + { + auto found = ports.find(name); + return (found == ports.end()) ? nullptr : found->second.net; + } + const NetInfo *getPort(IdString name) const + { + auto found = ports.find(name); + return (found == ports.end()) ? nullptr : found->second.net; + } + void connectPort(IdString port, NetInfo *net); + void disconnectPort(IdString port); + void connectPorts(IdString port, CellInfo *other, IdString other_port); + void movePortTo(IdString port, CellInfo *other, IdString other_port); + void renamePort(IdString old_name, IdString new_name); + void movePortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, + int new_offset, bool new_brackets, int width); + void copyPortTo(IdString port, CellInfo *other, IdString other_port); + void copyPortBusTo(IdString old_name, int old_offset, bool old_brackets, CellInfo *new_cell, IdString new_name, + int new_offset, bool new_brackets, int width); }; enum TimingPortClass diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index a1f8aa1f04..b1849ee672 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -124,10 +124,10 @@ void Arch::permute_luts() for (int i = 0; i < 4; i++) { IdString p = port_names.at(i); // log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first); - disconnect_port(getCtx(), ci, p); + ci->disconnectPort(p); ci->ports.at(p).net = nullptr; if (orig_nets.at(inputs.at(i).second) != nullptr) { - connect_port(getCtx(), orig_nets.at(inputs.at(i).second), ci, p); + ci->connectPort(p, orig_nets.at(inputs.at(i).second)); ci->params[id(p.str(this) + "MUX")] = p.str(this); } else { ci->params[id(p.str(this) + "MUX")] = std::string("1"); diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 11a855ec48..a23e4cd2e5 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -1043,7 +1043,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex if (trimux_tsreg != "PADDT") cc.tiles[pic_tile].add_enum(pio + ".TRIMUX_TSREG", trimux_tsreg); } else if (ci->type == id_DCCA) { - const NetInfo *cen = get_net_or_empty(ci, id_CE); + const NetInfo *cen = ci->getPort(id_CE); if (cen != nullptr) { std::string belname = ctx->loc_info(bel)->bel_data[bel.index].name.get(); Loc loc = ctx->getBelLocation(bel); @@ -1347,13 +1347,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex tg.config.add_enum("CLKOS_TRIM_DELAY", intstr_or_default(ci->params, id_CLKOS_TRIM_DELAY, "0")); tg.config.add_enum("OUTDIVIDER_MUXA", str_or_default(ci->params, id_OUTDIVIDER_MUXA, - get_net_or_empty(ci, id_CLKOP) ? "DIVA" : "REFCLK")); + ci->getPort(id_CLKOP) ? "DIVA" : "REFCLK")); tg.config.add_enum("OUTDIVIDER_MUXB", str_or_default(ci->params, id_OUTDIVIDER_MUXB, - get_net_or_empty(ci, id_CLKOP) ? "DIVB" : "REFCLK")); + ci->getPort(id_CLKOP) ? "DIVB" : "REFCLK")); tg.config.add_enum("OUTDIVIDER_MUXC", str_or_default(ci->params, id_OUTDIVIDER_MUXC, - get_net_or_empty(ci, id_CLKOP) ? "DIVC" : "REFCLK")); + ci->getPort(id_CLKOP) ? "DIVC" : "REFCLK")); tg.config.add_enum("OUTDIVIDER_MUXD", str_or_default(ci->params, id_OUTDIVIDER_MUXD, - get_net_or_empty(ci, id_CLKOP) ? "DIVD" : "REFCLK")); + ci->getPort(id_CLKOP) ? "DIVD" : "REFCLK")); tg.config.add_word("PLL_LOCK_MODE", int_to_bitvector(int_or_default(ci->params, id_PLL_LOCK_MODE, 0), 3)); @@ -1404,7 +1404,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex else cc.tiles[pic_tile].add_enum(prim + "." + param.first.str(ctx), param.second.as_string()); } - if (get_net_or_empty(ci, id_LOADN) != nullptr) { + if (ci->getPort(id_LOADN) != nullptr) { cc.tiles[pic_tile].add_enum(prim + ".LOADNMUX", "LOADN"); } } else if (ci->type == id_DCUA) { @@ -1481,14 +1481,12 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex lo_del_value = (256 - lo_del_value) & 0xFF; tg.config.add_word("DQS.DQS_LI_DEL_VAL", int_to_bitvector(li_del_value, 8)); tg.config.add_word("DQS.DQS_LO_DEL_VAL", int_to_bitvector(lo_del_value, 8)); - tg.config.add_enum("DQS.WRLOADN_USED", get_net_or_empty(ci, id_WRLOADN) != nullptr ? "YES" : "NO"); - tg.config.add_enum("DQS.RDLOADN_USED", get_net_or_empty(ci, id_RDLOADN) != nullptr ? "YES" : "NO"); - tg.config.add_enum("DQS.PAUSE_USED", get_net_or_empty(ci, id_PAUSE) != nullptr ? "YES" : "NO"); + tg.config.add_enum("DQS.WRLOADN_USED", ci->getPort(id_WRLOADN) != nullptr ? "YES" : "NO"); + tg.config.add_enum("DQS.RDLOADN_USED", ci->getPort(id_RDLOADN) != nullptr ? "YES" : "NO"); + tg.config.add_enum("DQS.PAUSE_USED", ci->getPort(id_PAUSE) != nullptr ? "YES" : "NO"); tg.config.add_enum("DQS.READ_USED", - (get_net_or_empty(ci, id_READ0) != nullptr || get_net_or_empty(ci, id_READ1) != nullptr) - ? "YES" - : "NO"); - tg.config.add_enum("DQS.DDRDEL", get_net_or_empty(ci, id_DDRDEL) != nullptr ? "DDRDEL" : "0"); + (ci->getPort(id_READ0) != nullptr || ci->getPort(id_READ1) != nullptr) ? "YES" : "NO"); + tg.config.add_enum("DQS.DDRDEL", ci->getPort(id_DDRDEL) != nullptr ? "DDRDEL" : "0"); tg.config.add_enum("DQS.GSR", str_or_default(ci->params, id_GSR, "DISABLED")); cc.tilegroups.push_back(tg); } else if (ci->type == id_ECLKSYNCB) { @@ -1496,14 +1494,14 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex bool r = loc.x > 5; std::string eclksync = ctx->loc_info(bel)->bel_data[bel.index].name.get(); std::string tile = ctx->get_tile_by_type(std::string("ECLK_") + (r ? "R" : "L")); - if (get_net_or_empty(ci, id_STOP) != nullptr) + if (ci->getPort(id_STOP) != nullptr) cc.tiles[tile].add_enum(eclksync + ".MODE", "ECLKSYNCB"); } else if (ci->type == id_ECLKBRIDGECS) { Loc loc = ctx->getBelLocation(ci->bel); bool r = loc.x > 5; std::string eclkb = ctx->loc_info(bel)->bel_data[bel.index].name.get(); std::string tile = ctx->get_tile_by_type(std::string("ECLK_") + (r ? "R" : "L")); - if (get_net_or_empty(ci, id_STOP) != nullptr) + if (ci->getPort(id_STOP) != nullptr) cc.tiles[tile].add_enum(eclkb + ".MODE", "ECLKBRIDGECS"); } else if (ci->type == id_DDRDLL) { Loc loc = ctx->getBelLocation(ci->bel); diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 6d88af7529..a5d484ff23 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -217,7 +217,7 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }), ffnet->users.end()); } else { - replace_port(ff, ff_port, lc, lc_port); + ff->movePortTo(ff_port, lc, lc_port); } } @@ -242,21 +242,21 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive if (ff->ports.find(id_CE) != ff->ports.end()) replace_port_safe(has_ff, ff, id_CE, lc, id_CE); - replace_port(ff, id_Q, lc, ctx->id("Q" + std::to_string(index))); - if (get_net_or_empty(ff, id_M) != nullptr) { + ff->movePortTo(id_Q, lc, ctx->id("Q" + std::to_string(index))); + if (ff->getPort(id_M) != nullptr) { // PRLD FFs that use both M and DI NPNR_ASSERT(!driven_by_lut); // As M is used; must route DI through a new LUT lc->params[ctx->id(reg + "_SD")] = std::string("1"); lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16); - replace_port(ff, id_DI, lc, ctx->id("D" + std::to_string(index))); - replace_port(ff, id_M, lc, ctx->id("M" + std::to_string(index))); - connect_ports(ctx, lc, ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index))); + ff->movePortTo(id_DI, lc, ctx->id("D" + std::to_string(index))); + ff->movePortTo(id_M, lc, ctx->id("M" + std::to_string(index))); + lc->connectPorts(ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index))); } else { if (driven_by_lut) { - replace_port(ff, id_DI, lc, ctx->id("DI" + std::to_string(index))); + ff->movePortTo(id_DI, lc, ctx->id("DI" + std::to_string(index))); } else { - replace_port(ff, id_DI, lc, ctx->id("M" + std::to_string(index))); + ff->movePortTo(id_DI, lc, ctx->id("M" + std::to_string(index))); } } } @@ -267,11 +267,11 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) lc->hierpath = lut->hierpath; lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = get_or_default(lut->params, id_INIT, Property(0, 16)); - replace_port(lut, id_A, lc, ctx->id("A" + std::to_string(index))); - replace_port(lut, id_B, lc, ctx->id("B" + std::to_string(index))); - replace_port(lut, id_C, lc, ctx->id("C" + std::to_string(index))); - replace_port(lut, id_D, lc, ctx->id("D" + std::to_string(index))); - replace_port(lut, id_Z, lc, ctx->id("F" + std::to_string(index))); + lut->movePortTo(id_A, lc, ctx->id("A" + std::to_string(index))); + lut->movePortTo(id_B, lc, ctx->id("B" + std::to_string(index))); + lut->movePortTo(id_C, lc, ctx->id("C" + std::to_string(index))); + lut->movePortTo(id_D, lc, ctx->id("D" + std::to_string(index))); + lut->movePortTo(id_Z, lc, ctx->id("F" + std::to_string(index))); } void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) @@ -285,22 +285,22 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) lc->params[id_CCU2_INJECT1_0] = str_or_default(ccu->params, id_INJECT1_0, "YES"); lc->params[id_CCU2_INJECT1_1] = str_or_default(ccu->params, id_INJECT1_1, "YES"); - replace_port(ccu, id_CIN, lc, id_FCI); + ccu->movePortTo(id_CIN, lc, id_FCI); - replace_port(ccu, id_A0, lc, id_A0); - replace_port(ccu, id_B0, lc, id_B0); - replace_port(ccu, id_C0, lc, id_C0); - replace_port(ccu, id_D0, lc, id_D0); + ccu->movePortTo(id_A0, lc, id_A0); + ccu->movePortTo(id_B0, lc, id_B0); + ccu->movePortTo(id_C0, lc, id_C0); + ccu->movePortTo(id_D0, lc, id_D0); - replace_port(ccu, id_A1, lc, id_A1); - replace_port(ccu, id_B1, lc, id_B1); - replace_port(ccu, id_C1, lc, id_C1); - replace_port(ccu, id_D1, lc, id_D1); + ccu->movePortTo(id_A1, lc, id_A1); + ccu->movePortTo(id_B1, lc, id_B1); + ccu->movePortTo(id_C1, lc, id_C1); + ccu->movePortTo(id_D1, lc, id_D1); - replace_port(ccu, id_S0, lc, id_F0); - replace_port(ccu, id_S1, lc, id_F1); + ccu->movePortTo(id_S0, lc, id_F0); + ccu->movePortTo(id_S1, lc, id_F1); - replace_port(ccu, id_COUT, lc, id_FCO); + ccu->movePortTo(id_COUT, lc, id_FCO); } void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc) @@ -308,15 +308,15 @@ void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc) if (lc->hierpath == IdString()) lc->hierpath = ram->hierpath; lc->params[id_MODE] = std::string("RAMW"); - replace_port(ram, ctx->id("WAD[0]"), lc, id_D0); - replace_port(ram, ctx->id("WAD[1]"), lc, id_B0); - replace_port(ram, ctx->id("WAD[2]"), lc, id_C0); - replace_port(ram, ctx->id("WAD[3]"), lc, id_A0); - - replace_port(ram, ctx->id("DI[0]"), lc, id_C1); - replace_port(ram, ctx->id("DI[1]"), lc, id_A1); - replace_port(ram, ctx->id("DI[2]"), lc, id_D1); - replace_port(ram, ctx->id("DI[3]"), lc, id_B1); + ram->movePortTo(ctx->id("WAD[0]"), lc, id_D0); + ram->movePortTo(ctx->id("WAD[1]"), lc, id_B0); + ram->movePortTo(ctx->id("WAD[2]"), lc, id_C0); + ram->movePortTo(ctx->id("WAD[3]"), lc, id_A0); + + ram->movePortTo(ctx->id("DI[0]"), lc, id_C1); + ram->movePortTo(ctx->id("DI[1]"), lc, id_A1); + ram->movePortTo(ctx->id("DI[2]"), lc, id_D1); + ram->movePortTo(ctx->id("DI[3]"), lc, id_B1); } static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit) @@ -367,45 +367,45 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw lc->params[id_LUT1_INITVAL] = Property(permuted_init1, 16); if (ram->ports.count(ctx->id("RAD[0]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, id_D0); - connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, id_D1); + lc->connectPort(id_D0, ram->ports.at(ctx->id("RAD[0]")).net); + lc->connectPort(id_D1, ram->ports.at(ctx->id("RAD[0]")).net); } if (ram->ports.count(ctx->id("RAD[1]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, id_B0); - connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, id_B1); + lc->connectPort(id_B0, ram->ports.at(ctx->id("RAD[1]")).net); + lc->connectPort(id_B1, ram->ports.at(ctx->id("RAD[1]")).net); } if (ram->ports.count(ctx->id("RAD[2]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, id_C0); - connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, id_C1); + lc->connectPort(id_C0, ram->ports.at(ctx->id("RAD[2]")).net); + lc->connectPort(id_C1, ram->ports.at(ctx->id("RAD[2]")).net); } if (ram->ports.count(ctx->id("RAD[3]"))) { - connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, id_A0); - connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, id_A1); + lc->connectPort(id_A0, ram->ports.at(ctx->id("RAD[3]")).net); + lc->connectPort(id_A1, ram->ports.at(ctx->id("RAD[3]")).net); } if (ram->ports.count(id_WRE)) - connect_port(ctx, ram->ports.at(id_WRE).net, lc, id_WRE); + lc->connectPort(id_WRE, ram->ports.at(id_WRE).net); if (ram->ports.count(id_WCK)) - connect_port(ctx, ram->ports.at(id_WCK).net, lc, id_WCK); + lc->connectPort(id_WCK, ram->ports.at(id_WCK).net); - connect_ports(ctx, ramw, id_WADO0, lc, id_WAD0); - connect_ports(ctx, ramw, id_WADO1, lc, id_WAD1); - connect_ports(ctx, ramw, id_WADO2, lc, id_WAD2); - connect_ports(ctx, ramw, id_WADO3, lc, id_WAD3); + ramw->connectPorts(id_WADO0, lc, id_WAD0); + ramw->connectPorts(id_WADO1, lc, id_WAD1); + ramw->connectPorts(id_WADO2, lc, id_WAD2); + ramw->connectPorts(id_WADO3, lc, id_WAD3); if (index == 0) { - connect_ports(ctx, ramw, id_WDO0, lc, id_WD0); - connect_ports(ctx, ramw, id_WDO1, lc, id_WD1); + ramw->connectPorts(id_WDO0, lc, id_WD0); + ramw->connectPorts(id_WDO1, lc, id_WD1); - replace_port(ram, ctx->id("DO[0]"), lc, id_F0); - replace_port(ram, ctx->id("DO[1]"), lc, id_F1); + ram->movePortTo(ctx->id("DO[0]"), lc, id_F0); + ram->movePortTo(ctx->id("DO[1]"), lc, id_F1); } else if (index == 1) { - connect_ports(ctx, ramw, id_WDO2, lc, id_WD0); - connect_ports(ctx, ramw, id_WDO3, lc, id_WD1); + ramw->connectPorts(id_WDO2, lc, id_WD0); + ramw->connectPorts(id_WDO3, lc, id_WD1); - replace_port(ram, ctx->id("DO[2]"), lc, id_F0); - replace_port(ram, ctx->id("DO[3]"), lc, id_F1); + ram->movePortTo(ctx->id("DO[2]"), lc, id_F0); + ram->movePortTo(ctx->id("DO[3]"), lc, id_F1); } else { NPNR_ASSERT_FALSE("bad DPRAM index"); } @@ -416,21 +416,21 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectortype == ctx->id("$nextpnr_ibuf")) { trio->params[id_DIR] = std::string("INPUT"); - replace_port(nxio, id_O, trio, id_O); + nxio->movePortTo(id_O, trio, id_O); } else if (nxio->type == ctx->id("$nextpnr_obuf")) { trio->params[id_DIR] = std::string("OUTPUT"); - replace_port(nxio, id_I, trio, id_I); + nxio->movePortTo(id_I, trio, id_I); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below - NetInfo *i = get_net_or_empty(nxio, id_I); + NetInfo *i = nxio->getPort(id_I); if (i == nullptr || i->driver.cell == nullptr) trio->params[id_DIR] = std::string("INPUT"); else { log_info("%s: %s.%s\n", ctx->nameOf(i), ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); trio->params[id_DIR] = std::string("BIDIR"); } - replace_port(nxio, id_I, trio, id_I); - replace_port(nxio, id_O, trio, id_O); + nxio->movePortTo(id_I, trio, id_I); + nxio->movePortTo(id_O, trio, id_O); } else { NPNR_ASSERT(false); } @@ -438,9 +438,11 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectorname == nxio->name) - rename_net(ctx, donet, ctx->id(donet->name.str(ctx) + "$TRELLIS_IO_OUT")); + if (donet) + ctx->renameNet(donet->name, ctx->id(donet->name.str(ctx) + "$TRELLIS_IO_OUT")); if (dinet != nullptr && dinet->name == nxio->name) - rename_net(ctx, dinet, ctx->id(dinet->name.str(ctx) + "$TRELLIS_IO_IN")); + if (dinet) + ctx->renameNet(dinet->name, ctx->id(dinet->name.str(ctx) + "$TRELLIS_IO_IN")); if (ctx->nets.count(nxio->name)) { int i = 0; @@ -448,7 +450,8 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectorid(nxio->name.str(ctx) + "$rename$" + std::to_string(i++)); } while (ctx->nets.count(new_name)); - rename_net(ctx, ctx->nets.at(nxio->name).get(), new_name); + if (ctx->nets.at(nxio->name).get()) + ctx->renameNet(ctx->nets.at(nxio->name).get()->name, new_name); } // Create a new top port net for accurate IO timing analysis and simulation netlists @@ -458,7 +461,7 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectornet_aliases.erase(tn_netname); NetInfo *toplevel_net = ctx->createNet(tn_netname); toplevel_net->name = tn_netname; - connect_port(ctx, toplevel_net, trio, id_B); + trio->connectPort(id_B, toplevel_net); ctx->ports[nxio->name].net = toplevel_net; } @@ -466,12 +469,12 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectortype == ctx->id("$_TBUF_"); }, id_Y); if (tbuf) { - replace_port(tbuf, id_A, trio, id_I); + tbuf->movePortTo(id_A, trio, id_I); // Need to invert E to form T std::unique_ptr inv_lut = create_ecp5_cell(ctx, id_LUT4, trio->name.str(ctx) + "$invert_T"); - replace_port(tbuf, id_E, inv_lut.get(), id_A); + tbuf->movePortTo(id_E, inv_lut.get(), id_A); inv_lut->params[id_INIT] = Property(21845, 16); - connect_ports(ctx, inv_lut.get(), id_Z, trio, id_T); + inv_lut->connectPorts(id_Z, trio, id_T); created_cells.push_back(std::move(inv_lut)); if (donet->users.size() > 1) { diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 844c596b91..7b48e69388 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -399,7 +399,7 @@ class Ecp5GlobalRouter { BelId best_bel; WireId best_bel_pclkcib; - bool using_ce = get_net_or_empty(dcc, id_CE) != nullptr; + bool using_ce = dcc->getPort(id_CE) != nullptr; wirelen_t best_wirelen = 9999999; bool dedicated_routing = false; for (auto bel : ctx->getBels()) { @@ -533,7 +533,7 @@ class Ecp5GlobalRouter } for (auto ci : dcsc_cells) { for (auto port : {id_CLK0, id_CLK1}) { - NetInfo *net = get_net_or_empty(ci, port); + NetInfo *net = ci->getPort(port); if (net != nullptr) insert_dcc(net, ci); } @@ -609,7 +609,7 @@ class Ecp5GlobalRouter pins.push_back(id_CLK1); } for (auto pin : pins) { - NetInfo *ni = get_net_or_empty(ci, pin); + NetInfo *ni = ci->getPort(pin); if (ni == nullptr) continue; log_info(" trying dedicated routing for edge clock source %s\n", ctx->nameOf(ni)); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index d49dbdf37b..2b069db098 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -112,7 +112,7 @@ class Ecp5Packer if (znet != nullptr) { CellInfo *ff = net_only_drives(ctx, znet, is_ff, id_DI, false); // Can't combine preload FF with LUT due to conflict on M - if (ff != nullptr && get_net_or_empty(ff, id_M) == nullptr) { + if (ff != nullptr && ff->getPort(id_M) == nullptr) { lutffPairs[ci->name] = ff->name; fflutPairs[ff->name] = ci->name; } @@ -124,9 +124,9 @@ class Ecp5Packer // Check if a flipflop is available in a slice bool is_ff_available(CellInfo *slice, int ff) { - if (get_net_or_empty(slice, (ff == 1) ? id_Q1 : id_Q0) != nullptr) + if (slice->getPort((ff == 1) ? id_Q1 : id_Q0) != nullptr) return false; - if (get_net_or_empty(slice, (ff == 1) ? id_M1 : id_M0) != nullptr) + if (slice->getPort((ff == 1) ? id_M1 : id_M0) != nullptr) return false; return true; } @@ -146,8 +146,8 @@ class Ecp5Packer if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR")) return false; } - bool has_ff0 = get_net_or_empty(slice, id_Q0) != nullptr; - bool has_ff1 = get_net_or_empty(slice, id_Q1) != nullptr; + bool has_ff0 = slice->getPort(id_Q0) != nullptr; + bool has_ff1 = slice->getPort(id_Q1) != nullptr; if (!has_ff0 && !has_ff1) return true; if (str_or_default(ff->params, id_GSR, "DISABLED") != str_or_default(slice->params, id_GSR, "DISABLED")) @@ -224,7 +224,7 @@ class Ecp5Packer // Return true if a FF can be added to a DPRAM slice bool can_pack_ff_dram(CellInfo *dpram, CellInfo *ff) { - if (get_net_or_empty(ff, id_M) != nullptr) + if (ff->getPort(id_M) != nullptr) return false; // skip PRLD FFs due to M/DI conflict std::string wckmux = str_or_default(dpram->params, id_WCKMUX, "WCK"); std::string clkmux = str_or_default(ff->params, id_CLKMUX, "CLK"); @@ -452,7 +452,7 @@ class Ecp5Packer // No IO buffer insertion in out-of-context mode, just remove the nextpnr buffer // and leave the top level port for (auto &port : ci->ports) - disconnect_port(ctx, ci, port.first); + ci->disconnectPort(port.first); } else if (trio != nullptr) { // Trivial case, TRELLIS_IO used. Just remove the IOBUF log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), @@ -498,7 +498,7 @@ class Ecp5Packer trio = new_cells.back().get(); } for (auto port : ci->ports) - disconnect_port(ctx, ci, port.first); + ci->disconnectPort(port.first); packed_cells.insert(ci->name); if (trio != nullptr) { for (const auto &attr : ci->attrs) @@ -546,16 +546,16 @@ class Ecp5Packer log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); if (ctx->verbose) log_info(" mux '%s' forms part of a LUT5\n", cell.first.c_str(ctx)); - replace_port(lut0, id_A, packed.get(), id_A0); - replace_port(lut0, id_B, packed.get(), id_B0); - replace_port(lut0, id_C, packed.get(), id_C0); - replace_port(lut0, id_D, packed.get(), id_D0); - replace_port(lut1, id_A, packed.get(), id_A1); - replace_port(lut1, id_B, packed.get(), id_B1); - replace_port(lut1, id_C, packed.get(), id_C1); - replace_port(lut1, id_D, packed.get(), id_D1); - replace_port(ci, id_C0, packed.get(), id_M0); - replace_port(ci, id_Z, packed.get(), id_OFX0); + lut0->movePortTo(id_A, packed.get(), id_A0); + lut0->movePortTo(id_B, packed.get(), id_B0); + lut0->movePortTo(id_C, packed.get(), id_C0); + lut0->movePortTo(id_D, packed.get(), id_D0); + lut1->movePortTo(id_A, packed.get(), id_A1); + lut1->movePortTo(id_B, packed.get(), id_B1); + lut1->movePortTo(id_C, packed.get(), id_C1); + lut1->movePortTo(id_D, packed.get(), id_D1); + ci->movePortTo(id_C0, packed.get(), id_M0); + ci->movePortTo(id_Z, packed.get(), id_OFX0); packed->params[id_LUT0_INITVAL] = get_or_default(lut0->params, id_INIT, Property(0, 16)); packed->params[id_LUT1_INITVAL] = get_or_default(lut1->params, id_INIT, Property(0, 16)); @@ -611,10 +611,10 @@ class Ecp5Packer } if (ctx->verbose) log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx)); - replace_port(ci, id_D0, slice1, id_FXA); - replace_port(ci, id_D1, slice1, id_FXB); - replace_port(ci, id_SD, slice1, id_M1); - replace_port(ci, id_Z, slice1, id_OFX1); + ci->movePortTo(id_D0, slice1, id_FXA); + ci->movePortTo(id_D1, slice1, id_FXB); + ci->movePortTo(id_SD, slice1, id_M1); + ci->movePortTo(id_Z, slice1, id_OFX1); slice0->constr_z = 1; slice0->constr_x = 0; slice0->constr_y = 0; @@ -676,10 +676,10 @@ class Ecp5Packer slice3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx), fxa_1->driver.port.c_str(ctx)); - replace_port(ci, id_D0, slice2, id_FXA); - replace_port(ci, id_D1, slice2, id_FXB); - replace_port(ci, id_SD, slice2, id_M1); - replace_port(ci, id_Z, slice2, id_OFX1); + ci->movePortTo(id_D0, slice2, id_FXA); + ci->movePortTo(id_D1, slice2, id_FXB); + ci->movePortTo(id_SD, slice2, id_M1); + ci->movePortTo(id_Z, slice2, id_OFX1); for (auto slice : {slice0, slice1, slice2, slice3}) { slice->constr_children.clear(); @@ -747,12 +747,12 @@ class Ecp5Packer return user.port == chain_in.port && user.cell == chain_in.cell; }), carry->users.end()); - connect_port(ctx, carry, feedin.get(), id_A0); + feedin->connectPort(id_A0, carry); NetInfo *new_carry = ctx->createNet(ctx->id(feedin->name.str(ctx) + "$COUT")); - connect_port(ctx, new_carry, feedin.get(), id_COUT); + feedin->connectPort(id_COUT, new_carry); chain_in.cell->ports[chain_in.port].net = nullptr; - connect_port(ctx, new_carry, chain_in.cell, chain_in.port); + chain_in.cell->connectPort(chain_in.port, new_carry); CellInfo *feedin_ptr = feedin.get(); IdString feedin_name = feedin->name; @@ -772,16 +772,16 @@ class Ecp5Packer PortRef carry_drv = carry->driver; carry->driver.cell = nullptr; - connect_port(ctx, carry, feedout.get(), id_S0); + feedout->connectPort(id_S0, carry); NetInfo *new_cin = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$CIN")); new_cin->driver = carry_drv; carry_drv.cell->ports.at(carry_drv.port).net = new_cin; - connect_port(ctx, new_cin, feedout.get(), id_CIN); + feedout->connectPort(id_CIN, new_cin); if (chain_next) { // Loop back into LUT4_1 for feedthrough - connect_port(ctx, carry, feedout.get(), id_A1); + feedout->connectPort(id_A1, carry); carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), [chain_next](const PortRef &user) { @@ -790,10 +790,10 @@ class Ecp5Packer carry->users.end()); NetInfo *new_cout = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$COUT")); - connect_port(ctx, new_cout, feedout.get(), id_COUT); + feedout->connectPort(id_COUT, new_cout); chain_next->cell->ports[chain_next->port].net = nullptr; - connect_port(ctx, new_cout, chain_next->cell, chain_next->port); + chain_next->cell->connectPort(chain_next->port, new_cout); } CellInfo *feedout_ptr = feedout.get(); @@ -970,13 +970,13 @@ class Ecp5Packer dram_to_ram_slice(ctx, ci, ram1_slice.get(), ramw_slice.get(), 1); // Disconnect ports of original cell after packing - disconnect_port(ctx, ci, id_WCK); - disconnect_port(ctx, ci, id_WRE); + ci->disconnectPort(id_WCK); + ci->disconnectPort(id_WRE); - disconnect_port(ctx, ci, ctx->id("RAD[0]")); - disconnect_port(ctx, ci, ctx->id("RAD[1]")); - disconnect_port(ctx, ci, ctx->id("RAD[2]")); - disconnect_port(ctx, ci, ctx->id("RAD[3]")); + ci->disconnectPort(ctx->id("RAD[0]")); + ci->disconnectPort(ctx->id("RAD[1]")); + ci->disconnectPort(ctx->id("RAD[2]")); + ci->disconnectPort(ctx->id("RAD[3]")); // Attempt to pack FFs into RAM slices std::vector> ff_packing; @@ -1159,7 +1159,7 @@ class Ecp5Packer CellInfo *ci = cell.second.get(); if (is_ff(ctx, ci)) { bool pack_dense = used_slices > (dense_pack_mode_thresh * available_slices); - bool requires_m = get_net_or_empty(ci, id_M) != nullptr; + bool requires_m = ci->getPort(id_M) != nullptr; if (pack_dense && !requires_m) { // If dense packing threshold exceeded; always try and pack the FF into an existing slice // Find a SLICE with space "near" the flipflop in the netlist @@ -1421,8 +1421,8 @@ class Ecp5Packer auto rename_bus = [&](CellInfo *c, const std::string &oldname, const std::string &newname, int width, int oldoffset, int newoffset) { for (int i = 0; i < width; i++) - rename_port(ctx, c, ctx->id(oldname + std::to_string(i + oldoffset)), - ctx->id(newname + std::to_string(i + newoffset))); + c->renamePort(ctx->id(oldname + std::to_string(i + oldoffset)), + ctx->id(newname + std::to_string(i + newoffset))); }; auto rename_param = [&](CellInfo *c, const std::string &oldname, const std::string &newname) { IdString o = ctx->id(oldname), n = ctx->id(newname); @@ -1446,11 +1446,11 @@ class Ecp5Packer rename_bus(ci, "DI", "DIB", 18, 18, 0); rename_bus(ci, "DO", "DOA", 18, 18, 0); rename_bus(ci, "DO", "DOB", 18, 0, 0); - rename_port(ctx, ci, id_CLKW, id_CLKA); - rename_port(ctx, ci, id_CLKR, id_CLKB); - rename_port(ctx, ci, id_CEW, id_CEA); - rename_port(ctx, ci, id_CER, id_CEB); - rename_port(ctx, ci, id_OCER, id_OCEB); + ci->renamePort(id_CLKW, id_CLKA); + ci->renamePort(id_CLKR, id_CLKB); + ci->renamePort(id_CEW, id_CEA); + ci->renamePort(id_CER, id_CEB); + ci->renamePort(id_OCER, id_OCEB); rename_param(ci, "CLKWMUX", "CLKAMUX"); if (str_or_default(ci->params, id_CLKAMUX) == "CLKW") ci->params[id_CLKAMUX] = std::string("CLKA"); @@ -1468,9 +1468,9 @@ class Ecp5Packer autocreate_empty_port(ci, id_RSTA); autocreate_empty_port(ci, id_RSTB); NetInfo *rst = ci->ports.at(id_RST).net; - connect_port(ctx, rst, ci, id_RSTA); - connect_port(ctx, rst, ci, id_RSTB); - disconnect_port(ctx, ci, id_RST); + ci->connectPort(id_RSTA, rst); + ci->connectPort(id_RSTB, rst); + ci->disconnectPort(id_RST); ci->ports.erase(id_RST); } ci->type = id_DP16KD; @@ -1721,12 +1721,12 @@ class Ecp5Packer // Disconnect these ports if connected to constant to prevent routing failure for (auto ndport : {id_D_TXBIT_CLKP_FROM_ND, id_D_TXBIT_CLKN_FROM_ND, id_D_SYNC_ND, id_D_TXPLL_LOL_FROM_ND, id_CH0_HDINN, id_CH0_HDINP, id_CH1_HDINN, id_CH1_HDINP}) { - const NetInfo *net = get_net_or_empty(ci, ndport); + const NetInfo *net = ci->getPort(ndport); if (net == nullptr || net->driver.cell == nullptr) continue; IdString ct = net->driver.cell->type; if (ct == id_GND || ct == id_VCC) { - disconnect_port(ctx, ci, ndport); + ci->disconnectPort(ndport); ci->ports.erase(ndport); } } @@ -1814,9 +1814,9 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_USRMCLK) { - rename_port(ctx, ci, id_USRMCLKI, id_PADDO); - rename_port(ctx, ci, id_USRMCLKTS, id_PADDT); - rename_port(ctx, ci, id_USRMCLKO, id_PADDI); + ci->renamePort(id_USRMCLKI, id_PADDO); + ci->renamePort(id_USRMCLKTS, id_PADDT); + ci->renamePort(id_USRMCLKO, id_PADDI); } else if (ci->type == id_GSR || ci->type == id_SGSR) { ci->params[id_MODE] = std::string("ACTIVE_LOW"); ci->params[id_SYNCMODE] = ci->type == id_SGSR ? std::string("SYNC") : std::string("ASYNC"); @@ -1959,8 +1959,8 @@ class Ecp5Packer eclkbuf->attrs[id_BEL] = ctx->getBelName(target_bel).str(ctx); - connect_port(ctx, ecknet, eclkbuf.get(), id_ECLKI); - connect_port(ctx, eclk.buf, eclkbuf.get(), id_ECLKO); + eclkbuf->connectPort(id_ECLKI, ecknet); + eclkbuf->connectPort(id_ECLKO, eclk.buf); found_eclk = free_eclk; eclk.buffer = eclkbuf.get(); new_cells.push_back(std::move(eclkbuf)); @@ -1968,9 +1968,9 @@ class Ecp5Packer } auto &eclk = eclks[std::make_pair(bank, found_eclk)]; - disconnect_port(ctx, usr_cell, usr_port.name); + usr_cell->disconnectPort(usr_port.name); usr_port.net = nullptr; - connect_port(ctx, eclk.buf, usr_cell, usr_port.name); + usr_cell->connectPort(usr_port.name, eclk.buf); // Simple ECLK router WireId userWire = ctx->getBelPinWire(usr_bel, usr_port.name); @@ -2024,8 +2024,8 @@ class Ecp5Packer auto zero_cell = std::make_unique(ctx, name, id_GND); NetInfo *zero_net = ctx->createNet(name); zero_cell->addOutput(id_GND); - connect_port(ctx, zero_net, zero_cell.get(), id_GND); - connect_port(ctx, zero_net, ci, port); + zero_cell->connectPort(id_GND, zero_net); + ci->connectPort(port, zero_net); new_cells.push_back(std::move(zero_cell)); } @@ -2136,11 +2136,11 @@ class Ecp5Packer log_error("IOLOGIC '%s' has conflicting clocks '%s' and '%s'\n", iol->name.c_str(ctx), iol->ports[id_CLK].net->name.c_str(ctx), sclk->name.c_str(ctx)); } else { - connect_port(ctx, sclk, iol, id_CLK); + iol->connectPort(id_CLK, sclk); } } if (prim->ports.count(port) && disconnect) - disconnect_port(ctx, prim, port); + prim->disconnectPort(port); }; auto set_iologic_eclk = [&](CellInfo *iol, CellInfo *prim, IdString port) { @@ -2155,10 +2155,10 @@ class Ecp5Packer log_error("IOLOGIC '%s' has conflicting ECLKs '%s' and '%s'\n", iol->name.c_str(ctx), iol->ports[id_ECLK].net->name.c_str(ctx), eclk->name.c_str(ctx)); } else { - connect_port(ctx, eclk, iol, id_ECLK); + iol->connectPort(id_ECLK, eclk); } if (prim->ports.count(port)) - disconnect_port(ctx, prim, port); + prim->disconnectPort(port); }; auto set_iologic_lsr = [&](CellInfo *iol, CellInfo *prim, IdString port, bool input, bool disconnect = true) { @@ -2174,11 +2174,11 @@ class Ecp5Packer log_error("IOLOGIC '%s' has conflicting LSR signals '%s' and '%s'\n", iol->name.c_str(ctx), iol->ports[id_LSR].net->name.c_str(ctx), lsr->name.c_str(ctx)); } else if (iol->ports[id_LSR].net == nullptr) { - connect_port(ctx, lsr, iol, id_LSR); + iol->connectPort(id_LSR, lsr); } } if (prim->ports.count(port) && disconnect) - disconnect_port(ctx, prim, port); + prim->disconnectPort(port); }; bool warned_oddrx_iddrx = false; @@ -2245,7 +2245,7 @@ class Ecp5Packer log_error("IOLOGIC '%s' has conflicting %s signals '%s' and '%s'\n", iol->name.c_str(ctx), port.c_str(ctx), iol->ports[port].net->name.c_str(ctx), sig->name.c_str(ctx)); } - disconnect_port(ctx, prim, port); + prim->disconnectPort(port); } else { bool dqsr; int dqsgroup; @@ -2263,7 +2263,7 @@ class Ecp5Packer "%cDQ%d\n", port.c_str(ctx), prim->name.c_str(ctx), dqsr ? 'R' : 'L', dqsgroup, sig->driver.cell->name.c_str(ctx), driver_group.first ? 'R' : 'L', driver_group.second); - replace_port(prim, port, iol, port); + prim->movePortTo(port, iol, port); } }; @@ -2284,18 +2284,18 @@ class Ecp5Packer if (drives_iologic) { // Reconnect to PIO which the packer expects later on NetInfo *input_net = ci->ports.at(id_A).net, *dly_net = ci->ports.at(id_Z).net; - disconnect_port(ctx, i_pio, id_O); + i_pio->disconnectPort(id_O); i_pio->ports.at(id_O).net = nullptr; - disconnect_port(ctx, ci, id_A); + ci->disconnectPort(id_A); ci->ports.at(id_A).net = nullptr; - disconnect_port(ctx, ci, id_Z); + ci->disconnectPort(id_Z); ci->ports.at(id_Z).net = nullptr; - connect_port(ctx, dly_net, i_pio, id_O); - connect_port(ctx, input_net, iol, id_INDD); - connect_port(ctx, input_net, iol, id_DI); + i_pio->connectPort(id_O, dly_net); + iol->connectPort(id_INDD, input_net); + iol->connectPort(id_DI, input_net); } else { - replace_port(ci, id_A, iol, id_PADDI); - replace_port(ci, id_Z, iol, id_INDD); + ci->movePortTo(id_A, iol, id_PADDI); + ci->movePortTo(id_Z, iol, id_INDD); } packed_cells.insert(cell.first); } else if (o_pio != nullptr) { @@ -2307,22 +2307,22 @@ class Ecp5Packer input_net->driver.port == id_Q) driven_by_iol = true; if (driven_by_iol) { - disconnect_port(ctx, o_pio, id_I); + o_pio->disconnectPort(id_I); o_pio->ports.at(id_I).net = nullptr; - disconnect_port(ctx, ci, id_A); + ci->disconnectPort(id_A); ci->ports.at(id_A).net = nullptr; - disconnect_port(ctx, ci, id_Z); + ci->disconnectPort(id_Z); ci->ports.at(id_Z).net = nullptr; - connect_port(ctx, input_net, o_pio, id_I); + o_pio->connectPort(id_I, input_net); ctx->nets.erase(dly_net->name); } else { - replace_port(ci, id_A, iol, id_TXDATA0); - replace_port(ci, id_Z, iol, id_IOLDO); + ci->movePortTo(id_A, iol, id_TXDATA0); + ci->movePortTo(id_Z, iol, id_IOLDO); if (!o_pio->ports.count(id_IOLDO)) { o_pio->ports[id_IOLDO].name = id_IOLDO; o_pio->ports[id_IOLDO].type = PORT_IN; } - replace_port(o_pio, id_I, o_pio, id_IOLDO); + o_pio->movePortTo(id_I, o_pio, id_IOLDO); } packed_cells.insert(cell.first); } else { @@ -2336,19 +2336,19 @@ class Ecp5Packer std::string(ci->params.at(id_DEL_VALUE).as_string()).substr(0, 5) != "DELAY")) iol->params[ctx->id("DELAY.DEL_VALUE")] = ci->params.at(id_DEL_VALUE); if (ci->ports.count(id_LOADN)) - replace_port(ci, id_LOADN, iol, id_LOADN); + ci->movePortTo(id_LOADN, iol, id_LOADN); else tie_zero(iol, id_LOADN); if (ci->ports.count(id_MOVE)) - replace_port(ci, id_MOVE, iol, id_MOVE); + ci->movePortTo(id_MOVE, iol, id_MOVE); else tie_zero(iol, id_MOVE); if (ci->ports.count(id_DIRECTION)) - replace_port(ci, id_DIRECTION, iol, id_DIRECTION); + ci->movePortTo(id_DIRECTION, iol, id_DIRECTION); else tie_zero(iol, id_DIRECTION); if (ci->ports.count(id_CFLAG)) - replace_port(ci, id_CFLAG, iol, id_CFLAG); + ci->movePortTo(id_CFLAG, iol, id_CFLAG); } } @@ -2365,11 +2365,11 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IDDRX1_ODDRX1"); - replace_port(ci, id_D, iol, id_PADDI); + ci->movePortTo(id_D, iol, id_PADDI); set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_lsr(iol, ci, id_RST, true); - replace_port(ci, id_Q0, iol, id_RXDATA0); - replace_port(ci, id_Q1, iol, id_RXDATA1); + ci->movePortTo(id_Q0, iol, id_RXDATA0); + ci->movePortTo(id_Q1, iol, id_RXDATA1); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); } else if (ci->type == id_ODDRX1F) { @@ -2383,17 +2383,17 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IDDRX1_ODDRX1"); - replace_port(ci, id_Q, iol, id_IOLDO); + ci->movePortTo(id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } - replace_port(pio, id_I, pio, id_IOLDO); + pio->movePortTo(id_I, pio, id_IOLDO); pio->params[id_DATAMUX_ODDR] = std::string("IOLDO"); set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_lsr(iol, ci, id_RST, false); - replace_port(ci, id_D0, iol, id_TXDATA0); - replace_port(ci, id_D1, iol, id_TXDATA1); + ci->movePortTo(id_D0, iol, id_TXDATA0); + ci->movePortTo(id_D1, iol, id_TXDATA1); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); } else if (ci->type == id_ODDRX2F || ci->type == id_ODDR71B) { @@ -2407,28 +2407,28 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "ODDRXN"); - replace_port(ci, id_Q, iol, id_IOLDO); + ci->movePortTo(id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } - replace_port(pio, id_I, pio, id_IOLDO); + pio->movePortTo(id_I, pio, id_IOLDO); set_iologic_sclk(iol, ci, id_SCLK, false, false); set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_eclk(iol, ci, id_ECLK); set_iologic_lsr(iol, ci, id_RST, false, false); set_iologic_lsr(iol, ci, id_RST, true); - replace_port(ci, id_D0, iol, id_TXDATA0); - replace_port(ci, id_D1, iol, id_TXDATA1); - replace_port(ci, id_D2, iol, id_TXDATA2); - replace_port(ci, id_D3, iol, id_TXDATA3); + ci->movePortTo(id_D0, iol, id_TXDATA0); + ci->movePortTo(id_D1, iol, id_TXDATA1); + ci->movePortTo(id_D2, iol, id_TXDATA2); + ci->movePortTo(id_D3, iol, id_TXDATA3); if (ci->type == id_ODDR71B) { Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string())); if (loc.z % 2 == 1) log_error("ODDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx)); - replace_port(ci, id_D4, iol, id_TXDATA4); - replace_port(ci, id_D5, iol, id_TXDATA5); - replace_port(ci, id_D6, iol, id_TXDATA6); + ci->movePortTo(id_D4, iol, id_TXDATA4); + ci->movePortTo(id_D5, iol, id_TXDATA5); + ci->movePortTo(id_D6, iol, id_TXDATA6); iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDR71"); } else { iol->params[ctx->id("ODDRXN.MODE")] = std::string("ODDRX2"); @@ -2447,22 +2447,22 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IDDRXN"); - replace_port(ci, id_D, iol, id_PADDI); + ci->movePortTo(id_D, iol, id_PADDI); set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_eclk(iol, ci, id_ECLK); set_iologic_lsr(iol, ci, id_RST, true); - replace_port(ci, id_Q0, iol, id_RXDATA0); - replace_port(ci, id_Q1, iol, id_RXDATA1); - replace_port(ci, id_Q2, iol, id_RXDATA2); - replace_port(ci, id_Q3, iol, id_RXDATA3); + ci->movePortTo(id_Q0, iol, id_RXDATA0); + ci->movePortTo(id_Q1, iol, id_RXDATA1); + ci->movePortTo(id_Q2, iol, id_RXDATA2); + ci->movePortTo(id_Q3, iol, id_RXDATA3); if (ci->type == id_IDDR71B) { Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(id_BEL).as_string())); if (loc.z % 2 == 1) log_error("IDDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx)); - replace_port(ci, id_Q4, iol, id_RXDATA4); - replace_port(ci, id_Q5, iol, id_RXDATA5); - replace_port(ci, id_Q6, iol, id_RXDATA6); - replace_port(ci, id_ALIGNWD, iol, id_SLIP); + ci->movePortTo(id_Q4, iol, id_RXDATA4); + ci->movePortTo(id_Q5, iol, id_RXDATA5); + ci->movePortTo(id_Q6, iol, id_RXDATA6); + ci->movePortTo(id_ALIGNWD, iol, id_SLIP); iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDR71"); } else { iol->params[ctx->id("IDDRXN.MODE")] = std::string("IDDRX2"); @@ -2480,18 +2480,18 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, id_Q, iol, id_IOLDO); + ci->movePortTo(id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } - replace_port(pio, id_I, pio, id_IOLDO); + pio->movePortTo(id_I, pio, id_IOLDO); set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_eclk(iol, ci, id_ECLK); set_iologic_lsr(iol, ci, id_RST, false, false); set_iologic_lsr(iol, ci, id_RST, true); - replace_port(ci, id_D0, iol, id_TXDATA0); - replace_port(ci, id_D1, iol, id_TXDATA2); + ci->movePortTo(id_D0, iol, id_TXDATA0); + ci->movePortTo(id_D1, iol, id_TXDATA2); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MODDRX.MODE")] = std::string("MOSHX2"); pio->params[id_DATAMUX_MDDR] = std::string("IOLDO"); @@ -2507,20 +2507,20 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, id_Q, iol, id_IOLDO); + ci->movePortTo(id_Q, iol, id_IOLDO); if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } - replace_port(pio, id_I, pio, id_IOLDO); + pio->movePortTo(id_I, pio, id_IOLDO); set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_eclk(iol, ci, id_ECLK); set_iologic_lsr(iol, ci, id_RST, false, false); set_iologic_lsr(iol, ci, id_RST, true); - replace_port(ci, id_D0, iol, id_TXDATA0); - replace_port(ci, id_D1, iol, id_TXDATA1); - replace_port(ci, id_D2, iol, id_TXDATA2); - replace_port(ci, id_D3, iol, id_TXDATA3); + ci->movePortTo(id_D0, iol, id_TXDATA0); + ci->movePortTo(id_D1, iol, id_TXDATA1); + ci->movePortTo(id_D2, iol, id_TXDATA2); + ci->movePortTo(id_D3, iol, id_TXDATA3); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MODDRX.MODE")] = std::string("MODDRX2"); iol->params[ctx->id("MIDDRX_MODDRX.WRCLKMUX")] = @@ -2539,15 +2539,15 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, id_D, iol, id_PADDI); + ci->movePortTo(id_D, iol, id_PADDI); set_iologic_sclk(iol, ci, id_SCLK, true); set_iologic_eclk(iol, ci, id_ECLK); set_iologic_lsr(iol, ci, id_RST, true); - replace_port(ci, id_Q0, iol, id_RXDATA0); - replace_port(ci, id_Q1, iol, id_RXDATA1); - replace_port(ci, id_Q2, iol, id_RXDATA2); - replace_port(ci, id_Q3, iol, id_RXDATA3); - replace_port(ci, id_QWL, iol, id_INFF); + ci->movePortTo(id_Q0, iol, id_RXDATA0); + ci->movePortTo(id_Q1, iol, id_RXDATA1); + ci->movePortTo(id_Q2, iol, id_RXDATA2); + ci->movePortTo(id_Q3, iol, id_RXDATA3); + ci->movePortTo(id_QWL, iol, id_INFF); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MIDDRX.MODE")] = std::string("MIDDRX2"); process_dqs_port(ci, pio, iol, id_DQSR90); @@ -2569,17 +2569,17 @@ class Ecp5Packer else iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "MIDDRX_MODDRX"); - replace_port(ci, id_Q, iol, id_IOLTO); + ci->movePortTo(id_Q, iol, id_IOLTO); if (!pio->ports.count(id_IOLTO)) { pio->ports[id_IOLTO].name = id_IOLTO; pio->ports[id_IOLTO].type = PORT_IN; } - replace_port(pio, id_T, pio, id_IOLTO); + pio->movePortTo(id_T, pio, id_IOLTO); set_iologic_sclk(iol, ci, id_SCLK, false); set_iologic_eclk(iol, ci, id_ECLK); set_iologic_lsr(iol, ci, id_RST, false); - replace_port(ci, id_T0, iol, id_TSDATA0); - replace_port(ci, id_T1, iol, id_TSDATA1); + ci->movePortTo(id_T0, iol, id_TSDATA0); + ci->movePortTo(id_T1, iol, id_TSDATA1); process_dqs_port(ci, pio, iol, ci->type == id_TSHX2DQSA ? id_DQSW : id_DQSW270); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); iol->params[ctx->id("MTDDRX.MODE")] = std::string("MTSHX2"); @@ -2595,7 +2595,7 @@ class Ecp5Packer std::string mode = str_or_default(ci->attrs, id_ioff_dir, ""); if (mode != "output") { // See if it can be packed as an input ff - NetInfo *d = get_net_or_empty(ci, id_DI); + NetInfo *d = ci->getPort(id_DI); CellInfo *pio = net_driven_by(ctx, d, is_trellis_io, id_O); if (pio != nullptr && d->users.size() == 1) { // Input FF @@ -2613,10 +2613,10 @@ class Ecp5Packer if (str_or_default(ci->params, id_CEMUX, "CE") == "CE") { iol->params[id_CEIMUX] = std::string("CEMUX"); iol->params[id_CEMUX] = std::string("CE"); - if (get_net_or_empty(ci, id_CE) == nullptr) - replace_port(ci, id_CE, iol, id_CE); + if (ci->getPort(id_CE) == nullptr) + ci->movePortTo(id_CE, iol, id_CE); else - disconnect_port(ctx, ci, id_CE); + ci->disconnectPort(id_CE); } else { iol->params[id_CEIMUX] = std::string("1"); } @@ -2625,8 +2625,8 @@ class Ecp5Packer iol->params[ctx->id("FF.REGSET")] = str_or_default(ci->params, id_REGSET, "RESET"); iol->params[id_SRMODE] = str_or_default(ci->params, id_SRMODE, "ASYNC"); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); - replace_port(ci, id_DI, iol, id_PADDI); - replace_port(ci, id_Q, iol, id_INFF); + ci->movePortTo(id_DI, iol, id_PADDI); + ci->movePortTo(id_Q, iol, id_INFF); packed_cells.insert(cell.first); continue; } @@ -2645,21 +2645,21 @@ class Ecp5Packer iol = create_pio_iologic(pio, ci); set_iologic_mode(iol, "IREG_OREG"); // Connection between FF and PIO - replace_port(ci, id_Q, iol, tri ? id_IOLTO : id_IOLDO); + ci->movePortTo(id_Q, iol, tri ? id_IOLTO : id_IOLDO); if (tri) { if (!pio->ports.count(id_IOLTO)) { pio->ports[id_IOLTO].name = id_IOLTO; pio->ports[id_IOLTO].type = PORT_IN; } pio->params[id_TRIMUX_TSREG] = std::string("IOLTO"); - replace_port(pio, id_T, pio, id_IOLTO); + pio->movePortTo(id_T, pio, id_IOLTO); } else { if (!pio->ports.count(id_IOLDO)) { pio->ports[id_IOLDO].name = id_IOLDO; pio->ports[id_IOLDO].type = PORT_IN; } pio->params[id_DATAMUX_OREG] = std::string("IOLDO"); - replace_port(pio, id_I, pio, id_IOLDO); + pio->movePortTo(id_I, pio, id_IOLDO); } set_iologic_sclk(iol, ci, id_CLK, false); @@ -2671,10 +2671,10 @@ class Ecp5Packer if (str_or_default(ci->params, id_CEMUX, "CE") == "CE") { iol->params[id_CEOMUX] = std::string("CEMUX"); iol->params[id_CEMUX] = std::string("CE"); - if (get_net_or_empty(ci, id_CE) == nullptr) - replace_port(ci, id_CE, iol, id_CE); + if (ci->getPort(id_CE) == nullptr) + ci->movePortTo(id_CE, iol, id_CE); else - disconnect_port(ctx, ci, id_CE); + ci->disconnectPort(id_CE); } else { iol->params[id_CEOMUX] = std::string("1"); } @@ -2684,7 +2684,7 @@ class Ecp5Packer str_or_default(ci->params, id_REGSET, "RESET"); iol->params[id_SRMODE] = str_or_default(ci->params, id_SRMODE, "ASYNC"); // Data input - replace_port(ci, id_DI, iol, tri ? id_TSDATA0 : id_TXDATA0); + ci->movePortTo(id_DI, iol, tri ? id_TSDATA0 : id_TXDATA0); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); continue; @@ -2699,8 +2699,7 @@ class Ecp5Packer CellInfo *ci = cell.second.get(); if (ci->type == id_ECLKBRIDGECS) { Loc loc; - NetInfo *i0 = get_net_or_empty(ci, id_CLK0), *i1 = get_net_or_empty(ci, id_CLK1), - *o = get_net_or_empty(ci, id_ECSOUT); + NetInfo *i0 = ci->getPort(id_CLK0), *i1 = ci->getPort(id_CLK1), *o = ci->getPort(id_ECSOUT); for (NetInfo *input : {i0, i1}) { if (input == nullptr) continue; @@ -2753,7 +2752,7 @@ class Ecp5Packer for (auto user2 : o->users) { // Set side hint to ensure edge clock choice is routeable if (user2.cell->type == id_ECLKSYNCB && user2.port == id_ECLKI) { - NetInfo *synco = get_net_or_empty(user2.cell, id_ECLKO); + NetInfo *synco = user2.cell->getPort(id_ECLKO); if (synco != nullptr) bridge_side_hint[synco] = (loc.x > 1) ? 0 : 1; } diff --git a/fpga_interchange/arch_pack_clusters.cc b/fpga_interchange/arch_pack_clusters.cc index b003812ead..31e0522bf9 100644 --- a/fpga_interchange/arch_pack_clusters.cc +++ b/fpga_interchange/arch_pack_clusters.cc @@ -901,7 +901,7 @@ void Arch::prepare_cluster(const ClusterPOD *cluster, uint32_t index) // reachable due to the fixed dedicated interconnect. // E.g.: The CI input of carry chains in 7series corresponds to the CIN bel port, // which can only be connected to the COUT output of the tile below. - disconnect_port(ctx, ci, sink_port); + ci->disconnectPort(sink_port); } } diff --git a/fpga_interchange/macros.cc b/fpga_interchange/macros.cc index aa7d318405..8f7f823162 100644 --- a/fpga_interchange/macros.cc +++ b/fpga_interchange/macros.cc @@ -99,9 +99,9 @@ void Arch::expand_macros() // TODO: case of multiple top level ports on the same net? NPNR_ASSERT(net == nullptr); // Use the corresponding pre-expansion port net - net = get_net_or_empty(cell, IdString(net_port.port)); + net = cell->getPort(IdString(net_port.port)); // Disconnect the original port pre-expansion - disconnect_port(ctx, cell, IdString(net_port.port)); + cell->disconnectPort(IdString(net_port.port)); } // If not on a top level port, create a new net if (net == nullptr) @@ -115,7 +115,7 @@ void Arch::expand_macros() ctx->cells.at(derived_name(ctx, cell->name, IdString(net_port.instance))).get(); inst_cell->ports[port_name].name = port_name; inst_cell->ports[port_name].type = PortType(net_port.dir); - connect_port(ctx, net, inst_cell, port_name); + inst_cell->connectPort(port_name, net); } } diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 08e950e21c..4e3d460a26 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -953,7 +953,7 @@ static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetI new_cell->belStrength = STRENGTH_PLACER; ctx->tileStatus.at(inverting_bel.tile).boundcells[inverting_bel.index] = new_cell; - connect_port(ctx, net_before_inverter, new_cell, id_I); + new_cell->connectPort(id_I, net_before_inverter); // The original BEL pin is now routed, but only through the inverter. // Because the cell/net model doesn't allow for multiple source pins diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index ed9354b63f..8ac61bbea6 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -484,7 +484,7 @@ template struct GenericFrontend log_error("Net '%s' is multiply driven by cell ports %s.%s and %s.%s\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), ctx->nameOf(inst_name), port_bit_name.c_str()); - connect_port(ctx, net, ci, port_bit_ids); + ci->connectPort(port_bit_ids, net); } }); // Import attributes and parameters @@ -578,12 +578,12 @@ template struct GenericFrontend } NPNR_ASSERT(net->driver.cell == nullptr); // Connect IBUF output and net - connect_port(ctx, net, iobuf, ctx->id("O")); + iobuf->connectPort(ctx->id("O"), net); } else if (dir == PORT_OUT) { iobuf->type = ctx->id("$nextpnr_obuf"); iobuf->addInput(ctx->id("I")); // Connect IBUF input and net - connect_port(ctx, net, iobuf, ctx->id("I")); + iobuf->connectPort(ctx->id("I"), net); } else if (dir == PORT_INOUT) { iobuf->type = ctx->id("$nextpnr_iobuf"); @@ -597,16 +597,16 @@ template struct GenericFrontend NetInfo *split_iobuf_i = ctx->createNet(unique_name("", "$" + name + "$iobuf_i", true)); auto drv = net->driver; if (drv.cell != nullptr) { - disconnect_port(ctx, drv.cell, drv.port); + drv.cell->disconnectPort(drv.port); drv.cell->ports[drv.port].net = nullptr; - connect_port(ctx, split_iobuf_i, drv.cell, drv.port); + drv.cell->connectPort(drv.port, split_iobuf_i); } - connect_port(ctx, split_iobuf_i, iobuf, ctx->id("I")); + iobuf->connectPort(ctx->id("I"), split_iobuf_i); NPNR_ASSERT(net->driver.cell == nullptr); - connect_port(ctx, net, iobuf, ctx->id("O")); + iobuf->connectPort(ctx->id("O"), net); } else { iobuf->addInout(ctx->id("IO")); - connect_port(ctx, net, iobuf, ctx->id("IO")); + iobuf->connectPort(ctx->id("IO"), net); } } @@ -669,7 +669,7 @@ template struct GenericFrontend if (net->driver.cell != nullptr) log_error("Net '%s' is multiply driven by port %s.%s and constant '%c'\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell), ctx->nameOf(net->driver.port), constval); - connect_port(ctx, net, cc, ctx->id("Y")); + cc->connectPort(ctx->id("Y"), net); } // Merge two nets - e.g. if one net in a submodule bifurcates to two output bits and therefore two different diff --git a/generic/arch.cc b/generic/arch.cc index ad054efd6d..c4814bab0b 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -721,7 +721,7 @@ void Arch::assignArchInfo() CellInfo *ci = cell.second.get(); if (ci->type == id("GENERIC_SLICE")) { ci->is_slice = true; - ci->slice_clk = get_net_or_empty(ci, id("CLK")); + ci->slice_clk = ci->getPort(id("CLK")); } else { ci->is_slice = false; } diff --git a/generic/cells.cc b/generic/cells.cc index c14ddf7306..76d6474f70 100644 --- a/generic/cells.cc +++ b/generic/cells.cc @@ -66,19 +66,19 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) for (int i = 0; i < lut_k; i++) { IdString port = ctx->id("I[" + std::to_string(i) + "]"); - replace_port(lut, port, lc, port); + lut->movePortTo(port, lc, port); } if (no_dff) { lc->params[ctx->id("FF_USED")] = 0; - replace_port(lut, ctx->id("Q"), lc, ctx->id("F")); + lut->movePortTo(ctx->id("Q"), lc, ctx->id("F")); } } void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut) { lc->params[ctx->id("FF_USED")] = 1; - replace_port(dff, ctx->id("CLK"), lc, ctx->id("CLK")); + dff->movePortTo(ctx->id("CLK"), lc, ctx->id("CLK")); if (pass_thru_lut) { // Fill LUT with alternating 10 @@ -89,26 +89,26 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l init.append("10"); lc->params[ctx->id("INIT")] = Property::from_string(init); - replace_port(dff, ctx->id("D"), lc, ctx->id("I[0]")); + dff->movePortTo(ctx->id("D"), lc, ctx->id("I[0]")); } - replace_port(dff, ctx->id("Q"), lc, ctx->id("Q")); + dff->movePortTo(ctx->id("Q"), lc, ctx->id("Q")); } void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &todelete_cells) { if (nxio->type == ctx->id("$nextpnr_ibuf")) { iob->params[ctx->id("INPUT_USED")] = 1; - replace_port(nxio, ctx->id("O"), iob, ctx->id("O")); + nxio->movePortTo(ctx->id("O"), iob, ctx->id("O")); } else if (nxio->type == ctx->id("$nextpnr_obuf")) { iob->params[ctx->id("OUTPUT_USED")] = 1; - replace_port(nxio, ctx->id("I"), iob, ctx->id("I")); + nxio->movePortTo(ctx->id("I"), iob, ctx->id("I")); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below iob->params[ctx->id("INPUT_USED")] = 1; iob->params[ctx->id("OUTPUT_USED")] = 1; - replace_port(nxio, ctx->id("I"), iob, ctx->id("I")); - replace_port(nxio, ctx->id("O"), iob, ctx->id("O")); + nxio->movePortTo(ctx->id("I"), iob, ctx->id("I")); + nxio->movePortTo(ctx->id("O"), iob, ctx->id("O")); } else { NPNR_ASSERT(false); } @@ -118,8 +118,8 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &to ctx->id("Y")); if (tbuf) { iob->params[ctx->id("ENABLE_USED")] = 1; - replace_port(tbuf, ctx->id("A"), iob, ctx->id("I")); - replace_port(tbuf, ctx->id("E"), iob, ctx->id("EN")); + tbuf->movePortTo(ctx->id("A"), iob, ctx->id("I")); + tbuf->movePortTo(ctx->id("E"), iob, ctx->id("EN")); if (donet->users.size() > 1) { for (auto user : donet->users) diff --git a/generic/pack.cc b/generic/pack.cc index 8bdbbed07c..cb3f58974b 100644 --- a/generic/pack.cc +++ b/generic/pack.cc @@ -242,7 +242,7 @@ static void pack_io(Context *ctx) } else if (bool_or_default(ctx->settings, ctx->id("disable_iobs"))) { // No IO buffer insertion; just remove nextpnr_[io]buf for (auto &p : ci->ports) - disconnect_port(ctx, ci, p.first); + ci->disconnectPort(p.first); } else { // Create a GENERIC_IOB buffer std::unique_ptr ice_cell = diff --git a/generic/viaduct/example/example.cc b/generic/viaduct/example/example.cc index 3d1c201cbf..49b3679269 100644 --- a/generic/viaduct/example/example.cc +++ b/generic/viaduct/example/example.cc @@ -253,10 +253,10 @@ struct ExampleImpl : ViaductAPI CellInfo *ci = cell.second.get(); auto &fc = fast_cell_info.at(ci->flat_index); if (ci->type == id_LUT4) { - fc.lut_f = get_net_or_empty(ci, id_F); - fc.lut_i3_used = (get_net_or_empty(ci, ctx->id(stringf("I[%d]", K - 1))) != nullptr); + fc.lut_f = ci->getPort(id_F); + fc.lut_i3_used = (ci->getPort(ctx->id(stringf("I[%d]", K - 1))) != nullptr); } else if (ci->type == id_DFF) { - fc.ff_d = get_net_or_empty(ci, id_D); + fc.ff_d = ci->getPort(id_D); } } } diff --git a/generic/viaduct_helpers.cc b/generic/viaduct_helpers.cc index e9f7fa60e6..10c3b8020a 100644 --- a/generic/viaduct_helpers.cc +++ b/generic/viaduct_helpers.cc @@ -59,13 +59,13 @@ void ViaductHelpers::remove_nextpnr_iobs(const pool &top_ports) auto &ci = *cell.second; if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf"))) continue; - NetInfo *i = get_net_or_empty(&ci, ctx->id("I")); + NetInfo *i = ci.getPort(ctx->id("I")); if (i && i->driver.cell) { if (!top_ports.count(CellTypePort(i->driver))) log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port)); } - NetInfo *o = get_net_or_empty(&ci, ctx->id("O")); + NetInfo *o = ci.getPort(ctx->id("O")); if (o) { for (auto &usr : o->users) { if (!top_ports.count(CellTypePort(usr))) @@ -73,8 +73,8 @@ void ViaductHelpers::remove_nextpnr_iobs(const pool &top_ports) ctx->nameOf(usr.cell), ctx->nameOf(usr.port)); } } - disconnect_port(ctx, &ci, ctx->id("I")); - disconnect_port(ctx, &ci, ctx->id("O")); + ci.disconnectPort(ctx->id("I")); + ci.disconnectPort(ctx->id("O")); to_remove.push_back(ci.name); } for (IdString cell_name : to_remove) diff --git a/gowin/arch.cc b/gowin/arch.cc index b104013dbc..af85146799 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1600,9 +1600,9 @@ void Arch::assignArchInfo() ci->is_slice = true; ci->ff_used = ci->params.at(id_FF_USED).as_bool(); ci->ff_type = id(ci->params.at(id_FF_TYPE).as_string()); - ci->slice_clk = get_net_or_empty(ci, id_CLK); - ci->slice_ce = get_net_or_empty(ci, id_CE); - ci->slice_lsr = get_net_or_empty(ci, id_LSR); + ci->slice_clk = ci->getPort(id_CLK); + ci->slice_ce = ci->getPort(id_CE); + ci->slice_lsr = ci->getPort(id_LSR); // add timing paths addCellTimingClock(cname, id_CLK); diff --git a/gowin/cells.cc b/gowin/cells.cc index aef34f535a..d862458c33 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -93,12 +93,12 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3}; IdString wire_names[4] = {id_A, id_B, id_C, id_D}; for (int i = 0; i < 4; i++) { - replace_port(lut, sim_names[i], lc, wire_names[i]); + lut->movePortTo(sim_names[i], lc, wire_names[i]); } if (no_dff) { lc->params[id_FF_USED] = 0; - replace_port(lut, id_F, lc, id_F); + lut->movePortTo(id_F, lc, id_F); } } @@ -106,12 +106,12 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l { lc->params[id_FF_USED] = 1; lc->params[id_FF_TYPE] = dff->type.str(ctx); - replace_port(dff, id_CLK, lc, id_CLK); - replace_port(dff, id_CE, lc, id_CE); - replace_port(dff, id_SET, lc, id_LSR); - replace_port(dff, id_RESET, lc, id_LSR); - replace_port(dff, id_CLEAR, lc, id_LSR); - replace_port(dff, id_PRESET, lc, id_LSR); + dff->movePortTo(id_CLK, lc, id_CLK); + dff->movePortTo(id_CE, lc, id_CE); + dff->movePortTo(id_SET, lc, id_LSR); + dff->movePortTo(id_RESET, lc, id_LSR); + dff->movePortTo(id_CLEAR, lc, id_LSR); + dff->movePortTo(id_PRESET, lc, id_LSR); if (pass_thru_lut) { // Fill LUT with alternating 10 const int init_size = 1 << 4; @@ -121,10 +121,10 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l init.append("10"); lc->params[id_INIT] = Property::from_string(init); - replace_port(dff, id_D, lc, id_A); + dff->movePortTo(id_D, lc, id_A); } - replace_port(dff, id_Q, lc, id_Q); + dff->movePortTo(id_Q, lc, id_Q); } void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &todelete_cells) @@ -132,29 +132,29 @@ void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &to if (nxio->type == id_IBUF) { if (iob->type == id_IOBS) { // VCC -> OEN - connect_port(ctx, ctx->nets[ctx->id("$PACKER_VCC_NET")].get(), iob, id_OEN); + iob->connectPort(id_OEN, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); } iob->params[id_INPUT_USED] = 1; - replace_port(nxio, id_O, iob, id_O); + nxio->movePortTo(id_O, iob, id_O); } else if (nxio->type == id_OBUF) { if (iob->type == id_IOBS) { // VSS -> OEN - connect_port(ctx, ctx->nets[ctx->id("$PACKER_GND_NET")].get(), iob, id_OEN); + iob->connectPort(id_OEN, ctx->nets[ctx->id("$PACKER_GND_NET")].get()); } iob->params[id_OUTPUT_USED] = 1; - replace_port(nxio, id_I, iob, id_I); + nxio->movePortTo(id_I, iob, id_I); } else if (nxio->type == id_TBUF) { iob->params[id_ENABLE_USED] = 1; iob->params[id_OUTPUT_USED] = 1; - replace_port(nxio, id_I, iob, id_I); - replace_port(nxio, id_OEN, iob, id_OEN); + nxio->movePortTo(id_I, iob, id_I); + nxio->movePortTo(id_OEN, iob, id_OEN); } else if (nxio->type == id_IOBUF) { iob->params[id_ENABLE_USED] = 1; iob->params[id_INPUT_USED] = 1; iob->params[id_OUTPUT_USED] = 1; - replace_port(nxio, id_I, iob, id_I); - replace_port(nxio, id_O, iob, id_O); - replace_port(nxio, id_OEN, iob, id_OEN); + nxio->movePortTo(id_I, iob, id_I); + nxio->movePortTo(id_O, iob, id_O); + nxio->movePortTo(id_OEN, iob, id_OEN); } else { NPNR_ASSERT(false); } diff --git a/gowin/pack.cc b/gowin/pack.cc index 1201e31086..268f26ef4e 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -96,7 +96,7 @@ static void pack_alus(Context *ctx) log_info("packed ALU head into %s. CIN net is %s\n", ctx->nameOf(packed_head.get()), ctx->nameOf(cin_netId)); } - connect_port(ctx, ctx->nets[ctx->id("$PACKER_VCC_NET")].get(), packed_head.get(), id_C); + packed_head->connectPort(id_C, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); if (cin_netId == ctx->id("$PACKER_GND_NET")) { // CIN = 0 packed_head->params[id_ALU_MODE] = std::string("C2L"); @@ -106,8 +106,8 @@ static void pack_alus(Context *ctx) packed_head->params[id_ALU_MODE] = std::string("ONE2C"); } else { // CIN from logic - connect_port(ctx, ctx->nets[cin_netId].get(), packed_head.get(), id_B); - connect_port(ctx, ctx->nets[cin_netId].get(), packed_head.get(), id_D); + packed_head->connectPort(id_B, ctx->nets[cin_netId].get()); + packed_head->connectPort(id_D, ctx->nets[cin_netId].get()); packed_head->params[id_ALU_MODE] = std::string("0"); // ADD } } @@ -123,9 +123,9 @@ static void pack_alus(Context *ctx) packed_cells.insert(ci->name); // CIN/COUT are hardwired, delete - disconnect_port(ctx, ci, id_CIN); + ci->disconnectPort(id_CIN); NetInfo *cout = ci->ports.at(id_COUT).net; - disconnect_port(ctx, ci, id_COUT); + ci->disconnectPort(id_COUT); std::unique_ptr packed = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "_ALULC"); if (ctx->verbose) { @@ -135,9 +135,9 @@ static void pack_alus(Context *ctx) int mode = int_or_default(ci->params, id_ALU_MODE); packed->params[id_ALU_MODE] = mode; if (mode == 9) { // MULT - connect_port(ctx, ctx->nets[ctx->id("$PACKER_GND_NET")].get(), packed.get(), id_C); + packed->connectPort(id_C, ctx->nets[ctx->id("$PACKER_GND_NET")].get()); } else { - connect_port(ctx, ctx->nets[ctx->id("$PACKER_VCC_NET")].get(), packed.get(), id_C); + packed->connectPort(id_C, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); } // add to cluster @@ -149,30 +149,30 @@ static void pack_alus(Context *ctx) ++alu_idx; // connect all remainig ports - replace_port(ci, id_SUM, packed.get(), id_F); + ci->movePortTo(id_SUM, packed.get(), id_F); switch (mode) { case 0: // ADD - replace_port(ci, id_I0, packed.get(), id_B); - replace_port(ci, id_I1, packed.get(), id_D); + ci->movePortTo(id_I0, packed.get(), id_B); + ci->movePortTo(id_I1, packed.get(), id_D); break; case 1: // SUB - replace_port(ci, id_I0, packed.get(), id_A); - replace_port(ci, id_I1, packed.get(), id_D); + ci->movePortTo(id_I0, packed.get(), id_A); + ci->movePortTo(id_I1, packed.get(), id_D); break; case 5: // LE - replace_port(ci, id_I0, packed.get(), id_A); - replace_port(ci, id_I1, packed.get(), id_B); + ci->movePortTo(id_I0, packed.get(), id_A); + ci->movePortTo(id_I1, packed.get(), id_B); break; case 9: // MULT - replace_port(ci, id_I0, packed.get(), id_A); - replace_port(ci, id_I1, packed.get(), id_B); - disconnect_port(ctx, packed.get(), id_D); - connect_port(ctx, ctx->nets[ctx->id("$PACKER_VCC_NET")].get(), packed.get(), id_D); + ci->movePortTo(id_I0, packed.get(), id_A); + ci->movePortTo(id_I1, packed.get(), id_B); + packed->disconnectPort(id_D); + packed->connectPort(id_D, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); break; default: - replace_port(ci, id_I0, packed.get(), id_A); - replace_port(ci, id_I1, packed.get(), id_B); - replace_port(ci, id_I3, packed.get(), id_D); + ci->movePortTo(id_I0, packed.get(), id_A); + ci->movePortTo(id_I1, packed.get(), id_B); + ci->movePortTo(id_I3, packed.get(), id_D); } new_cells.push_back(std::move(packed)); @@ -191,7 +191,7 @@ static void pack_alus(Context *ctx) ctx->nameOf(cout)); } packed_tail->params[id_ALU_MODE] = std::string("C2L"); - connect_port(ctx, cout, packed_tail.get(), id_F); + packed_tail->connectPort(id_F, cout); // add to cluster packed_tail->cluster = packed_head->name; packed_tail->constr_z = alu_idx % 6; @@ -275,8 +275,8 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce packed->constr_children.clear(); // reconnect MUX ports - replace_port(ci, id_O, packed.get(), id_OF); - replace_port(ci, id_I1, packed.get(), id_I1); + ci->movePortTo(id_O, packed.get(), id_OF); + ci->movePortTo(id_I1, packed.get(), id_I1); // remove cells packed_cells.insert(ci->name); @@ -320,10 +320,10 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce packed->constr_children.clear(); // reconnect MUX ports - replace_port(ci, id_O, packed.get(), id_OF); - replace_port(ci, id_S0, packed.get(), id_SEL); - replace_port(ci, id_I0, packed.get(), id_I0); - replace_port(ci, id_I1, packed.get(), id_I1); + ci->movePortTo(id_O, packed.get(), id_OF); + ci->movePortTo(id_S0, packed.get(), id_SEL); + ci->movePortTo(id_I0, packed.get(), id_I0); + ci->movePortTo(id_I1, packed.get(), id_I1); // remove cells packed_cells.insert(ci->name); @@ -394,10 +394,10 @@ static void pack_mux2_lut(Context *ctx, CellInfo *ci, bool (*pred)(const BaseCtx packed->constr_children.push_back(mux1); // reconnect MUX ports - replace_port(ci, id_O, packed.get(), id_OF); - replace_port(ci, id_S0, packed.get(), id_SEL); - replace_port(ci, id_I0, packed.get(), id_I0); - replace_port(ci, id_I1, packed.get(), id_I1); + ci->movePortTo(id_O, packed.get(), id_OF); + ci->movePortTo(id_S0, packed.get(), id_SEL); + ci->movePortTo(id_I0, packed.get(), id_I0); + ci->movePortTo(id_I1, packed.get(), id_I1); // remove cells packed_cells.insert(ci->name); @@ -711,7 +711,7 @@ static void pack_io(Context *ctx) // delete the $nexpnr_[io]buf for (auto &p : iob->ports) { IdString netname = p.second.net->name; - disconnect_port(ctx, iob, p.first); + iob->disconnectPort(p.first); delete_nets.insert(netname); } packed_cells.insert(iob->name); diff --git a/ice40/arch.cc b/ice40/arch.cc index cf7e99a54b..b36c82d522 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -1219,17 +1219,17 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE); cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE); cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK); - cell->lcInfo.clk = get_net_or_empty(cell, id_CLK); - cell->lcInfo.cen = get_net_or_empty(cell, id_CEN); - cell->lcInfo.sr = get_net_or_empty(cell, id_SR); + cell->lcInfo.clk = cell->getPort(id_CLK); + cell->lcInfo.cen = cell->getPort(id_CEN); + cell->lcInfo.sr = cell->getPort(id_SR); cell->lcInfo.inputCount = 0; - if (get_net_or_empty(cell, id_I0)) + if (cell->getPort(id_I0)) cell->lcInfo.inputCount++; - if (get_net_or_empty(cell, id_I1)) + if (cell->getPort(id_I1)) cell->lcInfo.inputCount++; - if (get_net_or_empty(cell, id_I2)) + if (cell->getPort(id_I2)) cell->lcInfo.inputCount++; - if (get_net_or_empty(cell, id_I3)) + if (cell->getPort(id_I3)) cell->lcInfo.inputCount++; } else if (cell->type == id_SB_IO) { cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT"; diff --git a/ice40/cells.cc b/ice40/cells.cc index a8d3034708..b5f759b22c 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -340,12 +340,12 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) if (lc->hierpath == IdString()) lc->hierpath = lut->hierpath; lc->params[id_LUT_INIT] = lut->params[id_LUT_INIT].extract(0, 16, Property::State::S0); - replace_port(lut, id_I0, lc, id_I0); - replace_port(lut, id_I1, lc, id_I1); - replace_port(lut, id_I2, lc, id_I2); - replace_port(lut, id_I3, lc, id_I3); + lut->movePortTo(id_I0, lc, id_I0); + lut->movePortTo(id_I1, lc, id_I1); + lut->movePortTo(id_I2, lc, id_I2); + lut->movePortTo(id_I3, lc, id_I3); if (no_dff) { - replace_port(lut, id_O, lc, id_O); + lut->movePortTo(id_O, lc, id_O); lc->params[id_DFF_ENABLE] = Property::State::S0; } } @@ -357,7 +357,7 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l lc->params[id_DFF_ENABLE] = Property::State::S1; std::string config = dff->type.str(ctx).substr(6); auto citer = config.begin(); - replace_port(dff, id_C, lc, id_CLK); + dff->movePortTo(id_C, lc, id_CLK); if (citer != config.end() && *citer == 'N') { lc->params[id_NEG_CLK] = Property::State::S1; @@ -367,7 +367,7 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l } if (citer != config.end() && *citer == 'E') { - replace_port(dff, id_E, lc, id_CEN); + dff->movePortTo(id_E, lc, id_CEN); ++citer; } @@ -382,12 +382,12 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l if (*citer == 'S') { citer++; - replace_port(dff, id_S, lc, id_SR); + dff->movePortTo(id_S, lc, id_SR); lc->params[id_SET_NORESET] = Property::State::S1; } else { NPNR_ASSERT(*citer == 'R'); citer++; - replace_port(dff, id_R, lc, id_SR); + dff->movePortTo(id_R, lc, id_SR); lc->params[id_SET_NORESET] = Property::State::S0; } } @@ -396,10 +396,10 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l if (pass_thru_lut) { lc->params[id_LUT_INIT] = Property(2, 16); - replace_port(dff, id_D, lc, id_I0); + dff->movePortTo(id_D, lc, id_I0); } - replace_port(dff, id_Q, lc, id_O); + dff->movePortTo(id_Q, lc, id_O); } void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &todelete_cells) @@ -409,13 +409,13 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to auto pu_attr = nxio->attrs.find(id_PULLUP); if (pu_attr != nxio->attrs.end()) sbio->params[id_PULLUP] = pu_attr->second; - replace_port(nxio, id_O, sbio, id_D_IN_0); + nxio->movePortTo(id_O, sbio, id_D_IN_0); } else if (nxio->type == ctx->id("$nextpnr_obuf")) { sbio->params[id_PIN_TYPE] = 25; - replace_port(nxio, id_I, sbio, id_D_OUT_0); + nxio->movePortTo(id_I, sbio, id_D_OUT_0); } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { // N.B. tristate will be dealt with below - NetInfo *i = get_net_or_empty(nxio, id_I); + NetInfo *i = nxio->getPort(id_I); if (i == nullptr || i->driver.cell == nullptr) sbio->params[id_PIN_TYPE] = 1; else @@ -423,8 +423,8 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to auto pu_attr = nxio->attrs.find(id_PULLUP); if (pu_attr != nxio->attrs.end()) sbio->params[id_PULLUP] = pu_attr->second; - replace_port(nxio, id_I, sbio, id_D_OUT_0); - replace_port(nxio, id_O, sbio, id_D_IN_0); + nxio->movePortTo(id_I, sbio, id_D_OUT_0); + nxio->movePortTo(id_O, sbio, id_D_IN_0); } else { NPNR_ASSERT(false); } @@ -432,9 +432,11 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to // Rename I/O nets to avoid conflicts if (donet != nullptr && donet->name == nxio->name) - rename_net(ctx, donet, ctx->id(donet->name.str(ctx) + "$SB_IO_OUT")); + if (donet) + ctx->renameNet(donet->name, ctx->id(donet->name.str(ctx) + "$SB_IO_OUT")); if (dinet != nullptr && dinet->name == nxio->name) - rename_net(ctx, dinet, ctx->id(dinet->name.str(ctx) + "$SB_IO_IN")); + if (dinet) + ctx->renameNet(dinet->name, ctx->id(dinet->name.str(ctx) + "$SB_IO_IN")); if (ctx->nets.count(nxio->name)) { int i = 0; @@ -442,7 +444,8 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to do { new_name = ctx->id(nxio->name.str(ctx) + "$rename$" + std::to_string(i++)); } while (ctx->nets.count(new_name)); - rename_net(ctx, ctx->nets.at(nxio->name).get(), new_name); + if (ctx->nets.at(nxio->name).get()) + ctx->renameNet(ctx->nets.at(nxio->name).get()->name, new_name); } // Create a new top port net for accurate IO timing analysis and simulation netlists @@ -451,7 +454,7 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to NPNR_ASSERT(!ctx->nets.count(tn_netname)); ctx->net_aliases.erase(tn_netname); NetInfo *toplevel_net = ctx->createNet(tn_netname); - connect_port(ctx, toplevel_net, sbio, id_PACKAGE_PIN); + sbio->connectPort(id_PACKAGE_PIN, toplevel_net); ctx->ports[nxio->name].net = toplevel_net; } @@ -460,8 +463,8 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to id_Y); if (tbuf) { sbio->params[id_PIN_TYPE] = 41; - replace_port(tbuf, id_A, sbio, id_D_OUT_0); - replace_port(tbuf, id_E, sbio, id_OUTPUT_ENABLE); + tbuf->movePortTo(id_A, sbio, id_D_OUT_0); + tbuf->movePortTo(id_E, sbio, id_OUTPUT_ENABLE); if (donet->users.size() > 1) { for (auto user : donet->users) diff --git a/ice40/pack.cc b/ice40/pack.cc index 0220d4fe76..4244f1921b 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -227,8 +227,8 @@ static void pack_carries(Context *ctx) ++carry_only; } carry_lc->params[id_CARRY_ENABLE] = Property::State::S1; - replace_port(ci, id_CI, carry_lc, id_CIN); - replace_port(ci, id_CO, carry_lc, id_COUT); + ci->movePortTo(id_CI, carry_lc, id_CIN); + ci->movePortTo(id_CO, carry_lc, id_COUT); if (i0_net) { auto &i0_usrs = i0_net->users; i0_usrs.erase(std::remove_if(i0_usrs.begin(), i0_usrs.end(), [ci, ctx](const PortRef &pr) { @@ -300,7 +300,7 @@ static void pack_ram(Context *ctx) newname = "RCLK"; else if (pi.name == id_WCLKN) newname = "WCLK"; - replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); + ci->movePortTo(ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } new_cells.push_back(std::move(packed)); } @@ -442,7 +442,7 @@ static std::unique_ptr create_padin_gbuf(Context *ctx, CellInfo *cell, gb->attrs[id_BEL] = ctx->getBelName(gb_bel).str(ctx); // Reconnect the net to that port for easier identification it's a global net - replace_port(cell, port_name, gb.get(), id_GLOBAL_BUFFER_OUTPUT); + cell->movePortTo(port_name, gb.get(), id_GLOBAL_BUFFER_OUTPUT); return gb; } @@ -514,7 +514,7 @@ static void pack_io(Context *ctx) } else if (rgb != nullptr) { log_info("%s use by SB_RGBA_DRV/SB_RGB_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx)); - disconnect_port(ctx, ci, id_I); + ci->disconnectPort(id_I); packed_cells.insert(ci->name); continue; } else { @@ -525,7 +525,7 @@ static void pack_io(Context *ctx) sb = new_cells.back().get(); } for (auto port : ci->ports) - disconnect_port(ctx, ci, port.first); + ci->disconnectPort(port.first); packed_cells.insert(ci->name); for (auto &attr : ci->attrs) sb->attrs[attr.first] = attr.second; @@ -1138,13 +1138,13 @@ static void pack_special(Context *ctx) std::unique_ptr packed = create_ice_cell(ctx, id_ICESTORM_LFOSC, ci->name.str(ctx) + "_OSC"); packed_cells.insert(ci->name); cell_place_unique(ctx, packed.get()); - replace_port(ci, id_CLKLFEN, packed.get(), id_CLKLFEN); - replace_port(ci, id_CLKLFPU, packed.get(), id_CLKLFPU); + ci->movePortTo(id_CLKLFEN, packed.get(), id_CLKLFEN); + ci->movePortTo(id_CLKLFPU, packed.get(), id_CLKLFPU); if (bool_or_default(ci->attrs, id_ROUTE_THROUGH_FABRIC)) { - replace_port(ci, id_CLKLF, packed.get(), id_CLKLF_FABRIC); + ci->movePortTo(id_CLKLF, packed.get(), id_CLKLF_FABRIC); set_period(ctx, packed.get(), id_CLKLF_FABRIC, 100000000); // 10kHz } else { - replace_port(ci, id_CLKLF, packed.get(), id_CLKLF); + ci->movePortTo(id_CLKLF, packed.get(), id_CLKLF); std::unique_ptr gb = create_padin_gbuf(ctx, packed.get(), id_CLKLF, "$gbuf_" + ci->name.str(ctx) + "_lfosc"); set_period(ctx, gb.get(), id_GLOBAL_BUFFER_OUTPUT, 100000000); // 10kHz @@ -1157,11 +1157,11 @@ static void pack_special(Context *ctx) cell_place_unique(ctx, packed.get()); packed->params[id_TRIM_EN] = str_or_default(ci->params, id_TRIM_EN, "0b0"); packed->params[id_CLKHF_DIV] = str_or_default(ci->params, id_CLKHF_DIV, "0b00"); - replace_port(ci, id_CLKHFEN, packed.get(), id_CLKHFEN); - replace_port(ci, id_CLKHFPU, packed.get(), id_CLKHFPU); + ci->movePortTo(id_CLKHFEN, packed.get(), id_CLKHFEN); + ci->movePortTo(id_CLKHFPU, packed.get(), id_CLKHFPU); for (int i = 0; i < 10; i++) { auto port = ctx->id("TRIM" + std::to_string(i)); - replace_port(ci, port, packed.get(), port); + ci->movePortTo(port, packed.get(), port); } std::string div = packed->params[id_CLKHF_DIV].as_string(); int frequency; @@ -1176,10 +1176,10 @@ static void pack_special(Context *ctx) else log_error("Invalid HFOSC divider value '%s' - expecting 0b00, 0b01, 0b10 or 0b11\n", div.c_str()); if (bool_or_default(ci->attrs, id_ROUTE_THROUGH_FABRIC)) { - replace_port(ci, id_CLKHF, packed.get(), id_CLKHF_FABRIC); + ci->movePortTo(id_CLKHF, packed.get(), id_CLKHF_FABRIC); set_period(ctx, packed.get(), id_CLKHF_FABRIC, 1000000 / frequency); } else { - replace_port(ci, id_CLKHF, packed.get(), id_CLKHF); + ci->movePortTo(id_CLKHF, packed.get(), id_CLKHF); std::unique_ptr gb = create_padin_gbuf(ctx, packed.get(), id_CLKHF, "$gbuf_" + ci->name.str(ctx) + "_hfosc"); set_period(ctx, gb.get(), id_GLOBAL_BUFFER_OUTPUT, 1000000 / frequency); @@ -1198,7 +1198,7 @@ static void pack_special(Context *ctx) if (bpos != std::string::npos) { newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); + ci->movePortTo(ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } new_cells.push_back(std::move(packed)); } else if (is_sb_mac16(ctx, ci)) { @@ -1216,7 +1216,7 @@ static void pack_special(Context *ctx) if (bpos != std::string::npos) { newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); + ci->movePortTo(ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } new_cells.push_back(std::move(packed)); } else if (is_sb_rgba_drv(ctx, ci) || is_sb_rgb_drv(ctx, ci)) { @@ -1410,7 +1410,7 @@ void pack_plls(Context *ctx) } } } - replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); + ci->movePortTo(ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); } // Compute derive constraints diff --git a/machxo2/cells.cc b/machxo2/cells.cc index 1c4f753be9..c546489201 100644 --- a/machxo2/cells.cc +++ b/machxo2/cells.cc @@ -131,10 +131,10 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) for (std::string i : {"A", "B", "C", "D"}) { IdString lut_port = ctx->id(i); IdString lc_port = ctx->id(i + "0"); - replace_port(lut, lut_port, lc, lc_port); + lut->movePortTo(lut_port, lc, lc_port); } - replace_port(lut, id_Z, lc, id_F0); + lut->movePortTo(id_Z, lc, id_F0); } void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type) @@ -142,26 +142,26 @@ void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type) // FIXME: This will have to change once we support FFs with reset value of 1. lc->params[id_REG0_REGSET] = std::string("RESET"); - replace_port(dff, id_CLK, lc, id_CLK); - replace_port(dff, id_LSR, lc, id_LSR); - replace_port(dff, id_Q, lc, id_Q0); + dff->movePortTo(id_CLK, lc, id_CLK); + dff->movePortTo(id_LSR, lc, id_LSR); + dff->movePortTo(id_Q, lc, id_Q0); if (lut_type == LutType::PassThru) { // If a register's DI port is fed by a constant, options for placing are // limited. Use the LUT to get around this. // LUT output will go to F0, which will feed back to DI0 input. lc->params[id_LUT0_INITVAL] = Property(0xAAAA, 16); - replace_port(dff, id_DI, lc, id_A0); - connect_ports(ctx, lc, id_F0, lc, id_DI0); + dff->movePortTo(id_DI, lc, id_A0); + lc->connectPorts(id_F0, lc, id_DI0); } else if (lut_type == LutType::None) { // If there is no LUT, use the M0 input because DI0 requires // going through the LUTs. lc->params[id_REG0_SD] = std::string("0"); - replace_port(dff, id_DI, lc, id_M0); + dff->movePortTo(id_DI, lc, id_M0); } else { // Otherwise, there's a LUT being used in the slice and mapping DI to // DI0 input is fine. - replace_port(dff, id_DI, lc, id_DI0); + dff->movePortTo(id_DI, lc, id_DI0); } } diff --git a/machxo2/pack.cc b/machxo2/pack.cc index de6078656a..5051a98123 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -286,7 +286,7 @@ static void pack_io(Context *ctx) log_info("Removing top-level IOBUF '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); for (auto &p : ci->ports) - disconnect_port(ctx, ci, p.first); + ci->disconnectPort(p.first); packed_cells.insert(ci->name); } else if (is_facade_iob(ctx, ci)) { // If FACADE_IO has LOC attribute, convert the LOC (pin) to a BEL diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index e18d141346..35c1303a6e 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -86,8 +86,7 @@ struct MistralBitgen void write_io_cell(CellInfo *ci, int x, int y, int bi) { - bool is_output = - (ci->type == id_MISTRAL_OB || (ci->type == id_MISTRAL_IO && get_net_or_empty(ci, id_OE) != nullptr)); + bool is_output = (ci->type == id_MISTRAL_OB || (ci->type == id_MISTRAL_IO && ci->getPort(id_OE) != nullptr)); auto pos = CycloneV::xy2pos(x, y); // TODO: configurable pull, IO standard, etc cv->bmux_b_set(CycloneV::GPIO, pos, CycloneV::USE_WEAK_PULLUP, bi, false); @@ -229,8 +228,8 @@ struct MistralBitgen cv->bmux_m_set(block_type, pos, clk_sel[i / 2], alm, clk_choice[ce_idx]); if (ff->ffInfo.ctrlset.clk.inverted) cv->bmux_b_set(block_type, pos, clk_inv[ce_idx], 0, true); - if (get_net_or_empty(ff, id_ENA) != nullptr) { // not using ffInfo.ctrlset, this has a fake net always to - // ensure different constants don't collide + if (ff->getPort(id_ENA) != nullptr) { // not using ffInfo.ctrlset, this has a fake net always to + // ensure different constants don't collide cv->bmux_b_set(block_type, pos, en_en[ce_idx], 0, true); cv->bmux_b_set(block_type, pos, en_ninv[ce_idx], 0, ff->ffInfo.ctrlset.ena.inverted); } else { @@ -262,7 +261,7 @@ struct MistralBitgen cv->bmux_m_set(block_type, pos, clk_sel[1], alm, clk_choice[ce_idx]); if (lut->combInfo.wclk.inverted) cv->bmux_b_set(block_type, pos, clk_inv[ce_idx], 0, true); - if (get_net_or_empty(lut, id_A1EN) != nullptr) { + if (lut->getPort(id_A1EN) != nullptr) { cv->bmux_b_set(block_type, pos, en_en[ce_idx], 0, true); cv->bmux_b_set(block_type, pos, en_ninv[ce_idx], 0, lut->combInfo.we.inverted); } else { diff --git a/mistral/lab.cc b/mistral/lab.cc index 4f6b9b00bc..4b66ed0c8a 100644 --- a/mistral/lab.cc +++ b/mistral/lab.cc @@ -212,7 +212,7 @@ namespace { ControlSig get_ctrlsig(const Context *ctx, const CellInfo *cell, IdString port, bool explicit_const = false) { ControlSig result; - result.net = get_net_or_empty(cell, port); + result.net = cell->getPort(port); if (result.net == nullptr && explicit_const) { // For ENA, 1 (and 0) are explicit control set choices even though they aren't routed, as "no ENA" still // consumes a clock+ENA pair @@ -278,10 +278,10 @@ void Arch::assign_comb_info(CellInfo *cell) const cell->combInfo.lut_input_count = 5; cell->combInfo.lut_bits_count = 32; for (int i = 0; i < 5; i++) - cell->combInfo.lut_in[i] = get_net_or_empty(cell, id(stringf("B1ADDR[%d]", i))); + cell->combInfo.lut_in[i] = cell->getPort(id(stringf("B1ADDR[%d]", i))); auto key = get_mlab_key(cell); cell->combInfo.mlab_group = mlab_groups(key); - cell->combInfo.comb_out = get_net_or_empty(cell, id_B1DATA); + cell->combInfo.comb_out = cell->getPort(id_B1DATA); } else if (cell->type == id_MISTRAL_ALUT_ARITH) { cell->combInfo.is_carry = true; cell->combInfo.lut_input_count = 5; @@ -292,14 +292,14 @@ void Arch::assign_comb_info(CellInfo *cell) const { int i = 0; for (auto pin : arith_pins) { - cell->combInfo.lut_in[i++] = get_net_or_empty(cell, pin); + cell->combInfo.lut_in[i++] = cell->getPort(pin); } } - const NetInfo *ci = get_net_or_empty(cell, id_CI); - const NetInfo *co = get_net_or_empty(cell, id_CO); + const NetInfo *ci = cell->getPort(id_CI); + const NetInfo *co = cell->getPort(id_CO); - cell->combInfo.comb_out = get_net_or_empty(cell, id_SO); + cell->combInfo.comb_out = cell->getPort(id_SO); cell->combInfo.carry_start = (ci == nullptr) || (ci->driver.cell == nullptr); cell->combInfo.carry_end = (co == nullptr) || (co->users.empty()); @@ -308,10 +308,10 @@ void Arch::assign_comb_info(CellInfo *cell) const const CellInfo *prev = ci->driver.cell; if (prev != nullptr) { for (int i = 0; i < 5; i++) { - const NetInfo *a = get_net_or_empty(cell, arith_pins[i]); + const NetInfo *a = cell->getPort(arith_pins[i]); if (a == nullptr) continue; - const NetInfo *b = get_net_or_empty(prev, arith_pins[i]); + const NetInfo *b = prev->getPort(arith_pins[i]); if (a == b) ++cell->combInfo.chain_shared_input_count; } @@ -323,28 +323,28 @@ void Arch::assign_comb_info(CellInfo *cell) const switch (cell->type.index) { case ID_MISTRAL_ALUT6: ++cell->combInfo.lut_input_count; - cell->combInfo.lut_in[5] = get_net_or_empty(cell, id_F); + cell->combInfo.lut_in[5] = cell->getPort(id_F); [[fallthrough]]; case ID_MISTRAL_ALUT5: ++cell->combInfo.lut_input_count; - cell->combInfo.lut_in[4] = get_net_or_empty(cell, id_E); + cell->combInfo.lut_in[4] = cell->getPort(id_E); [[fallthrough]]; case ID_MISTRAL_ALUT4: ++cell->combInfo.lut_input_count; - cell->combInfo.lut_in[3] = get_net_or_empty(cell, id_D); + cell->combInfo.lut_in[3] = cell->getPort(id_D); [[fallthrough]]; case ID_MISTRAL_ALUT3: ++cell->combInfo.lut_input_count; - cell->combInfo.lut_in[2] = get_net_or_empty(cell, id_C); + cell->combInfo.lut_in[2] = cell->getPort(id_C); [[fallthrough]]; case ID_MISTRAL_ALUT2: ++cell->combInfo.lut_input_count; - cell->combInfo.lut_in[1] = get_net_or_empty(cell, id_B); + cell->combInfo.lut_in[1] = cell->getPort(id_B); [[fallthrough]]; case ID_MISTRAL_BUF: // used to route through to FFs etc case ID_MISTRAL_NOT: // used for inverters that map to LUTs ++cell->combInfo.lut_input_count; - cell->combInfo.lut_in[0] = get_net_or_empty(cell, id_A); + cell->combInfo.lut_in[0] = cell->getPort(id_A); [[fallthrough]]; case ID_MISTRAL_CONST: // MISTRAL_CONST is a nextpnr-inserted cell type for 0-input, constant-generating LUTs @@ -375,8 +375,8 @@ void Arch::assign_ff_info(CellInfo *cell) const cell->ffInfo.ctrlset.sload.inverted = false; } - cell->ffInfo.sdata = get_net_or_empty(cell, id_SDATA); - cell->ffInfo.datain = get_net_or_empty(cell, id_DATAIN); + cell->ffInfo.sdata = cell->getPort(id_SDATA); + cell->ffInfo.datain = cell->getPort(id_DATAIN); } // Validity checking functions @@ -883,7 +883,7 @@ void Arch::reassign_alm_inputs(uint32_t lab, uint8_t alm) auto &bel_pins = luts[i]->pin_data[log].bel_pins; bel_pins.clear(); - NetInfo *net = get_net_or_empty(luts[i], log); + NetInfo *net = luts[i]->getPort(log); if (net == nullptr) { // Disconnected inputs don't need to be allocated a pin, because the router won't be routing these continue; @@ -922,10 +922,10 @@ void Arch::reassign_alm_inputs(uint32_t lab, uint8_t alm) rt_lut->addInput(id_A); rt_lut->addOutput(id_Q); // Disconnect the original data input to the FF, and connect it to the route-thru LUT instead - NetInfo *datain = get_net_or_empty(ff, id_DATAIN); - disconnect_port(getCtx(), ff, id_DATAIN); - connect_port(getCtx(), datain, rt_lut, id_A); - connect_ports(getCtx(), rt_lut, id_Q, ff, id_DATAIN); + NetInfo *datain = ff->getPort(id_DATAIN); + ff->disconnectPort(id_DATAIN); + rt_lut->connectPort(id_A, datain); + rt_lut->connectPorts(id_Q, ff, id_DATAIN); // Assign route-thru LUT physical ports, input goes to the first half-specific input rt_lut->pin_data[id_A].bel_pins.push_back(i ? id_D : id_C); rt_lut->pin_data[id_Q].bel_pins.push_back(id_COMBOUT); @@ -1050,7 +1050,7 @@ uint64_t Arch::compute_lut_mask(uint32_t lab, uint8_t alm) else if (state == PIN_1) index |= (1 << init_idx); // Ignore if no associated physical pin - if (get_net_or_empty(lut, log_pin) == nullptr || lut->pin_data.at(log_pin).bel_pins.empty()) + if (lut->getPort(log_pin) == nullptr || lut->pin_data.at(log_pin).bel_pins.empty()) continue; // ALM inputs appear to be inverted by default (TODO: check!) // so only invert if an inverter has _not_ been folded into the pin diff --git a/mistral/pack.cc b/mistral/pack.cc index 4b434015b2..c4b3afe357 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -41,13 +41,13 @@ struct MistralPacker vcc_drv->addOutput(id_Q); gnd_net = ctx->createNet(ctx->id("$PACKER_GND_NET")); vcc_net = ctx->createNet(ctx->id("$PACKER_VCC_NET")); - connect_port(ctx, gnd_net, gnd_drv, id_Q); - connect_port(ctx, vcc_net, vcc_drv, id_Q); + gnd_drv->connectPort(id_Q, gnd_net); + vcc_drv->connectPort(id_Q, vcc_net); } CellPinState get_pin_needed_muxval(CellInfo *cell, IdString port) { - NetInfo *net = get_net_or_empty(cell, port); + NetInfo *net = cell->getPort(port); if (net == nullptr || net->driver.cell == nullptr) { // Pin is disconnected // If a mux value exists already, honour it @@ -78,14 +78,14 @@ struct MistralPacker void uninvert_port(CellInfo *cell, IdString port) { // Rewire a port so it is driven by the input to an inverter - NetInfo *net = get_net_or_empty(cell, port); + NetInfo *net = cell->getPort(port); NPNR_ASSERT(net != nullptr && net->driver.cell != nullptr && net->driver.cell->type == id_MISTRAL_NOT); CellInfo *inv = net->driver.cell; - disconnect_port(ctx, cell, port); + cell->disconnectPort(port); - NetInfo *inv_a = get_net_or_empty(inv, id_A); + NetInfo *inv_a = inv->getPort(id_A); if (inv_a != nullptr) { - connect_port(ctx, inv_a, cell, port); + cell->connectPort(port, inv_a); } } @@ -117,12 +117,12 @@ struct MistralPacker // Pin is tied to a constant // If there is a hard constant option; use it if ((pin_style & int(req_mux)) == req_mux) { - disconnect_port(ctx, cell, port_name); + cell->disconnectPort(port_name); cell->pin_data[port_name].state = req_mux; } else { - disconnect_port(ctx, cell, port_name); + cell->disconnectPort(port_name); // There is no hard constant, we need to connect it to the relevant soft-constant net - connect_port(ctx, (req_mux == PIN_1) ? vcc_net : gnd_net, cell, port_name); + cell->connectPort(port_name, (req_mux == PIN_1) ? vcc_net : gnd_net); } } } @@ -138,7 +138,7 @@ struct MistralPacker if (ci->type != id_MISTRAL_NOT && ci->type != id_GND && ci->type != id_VCC) continue; IdString port = (ci->type == id_MISTRAL_NOT) ? id_Q : id_Y; - NetInfo *out = get_net_or_empty(ci, port); + NetInfo *out = ci->getPort(port); if (out == nullptr) { trim_cells.push_back(ci->name); continue; @@ -146,7 +146,7 @@ struct MistralPacker if (!out->users.empty()) continue; - disconnect_port(ctx, ci, id_A); + ci->disconnectPort(id_A); trim_cells.push_back(ci->name); trim_nets.push_back(out->name); @@ -174,7 +174,7 @@ struct MistralPacker continue; if (ci->get_pin_state(id_SLOAD) != PIN_0) continue; - disconnect_port(ctx, ci, id_SDATA); + ci->disconnectPort(id_SDATA); } // Remove superfluous inverters and constant drivers trim_design(); @@ -197,7 +197,7 @@ struct MistralPacker if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { // Might have an input buffer (IB etc) connected to it is_npnr_iob = true; - NetInfo *o = get_net_or_empty(ci, id_O); + NetInfo *o = ci->getPort(id_O); if (o == nullptr) ; else if (o->users.size() > 1) @@ -208,7 +208,7 @@ struct MistralPacker if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { // Might have an output buffer (OB etc) connected to it is_npnr_iob = true; - NetInfo *i = get_net_or_empty(ci, id_I); + NetInfo *i = ci->getPort(id_I); if (i != nullptr && i->driver.cell != nullptr) { if (top_port.cell != nullptr) log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); @@ -245,8 +245,8 @@ struct MistralPacker port.second.net = top_port.cell->ports.at(top_port.port).net; } // Now remove the nextpnr-inserted buffer - disconnect_port(ctx, ci, id_I); - disconnect_port(ctx, ci, id_O); + ci->disconnectPort(id_I); + ci->disconnectPort(id_O); ctx->cells.erase(port.first); } } @@ -290,14 +290,14 @@ struct MistralPacker CellInfo *ci = cell.second.get(); if (ci->type != id_MISTRAL_ALUT_ARITH) continue; - const NetInfo *cin = get_net_or_empty(ci, id_CI); + const NetInfo *cin = ci->getPort(id_CI); if (cin != nullptr && cin->driver.cell != nullptr) continue; // not the start of a chain std::vector chain; CellInfo *cursor = ci; while (true) { chain.push_back(cursor); - const NetInfo *co = get_net_or_empty(cursor, id_CO); + const NetInfo *co = cursor->getPort(id_CO); if (co == nullptr || co->users.empty()) break; if (co->users.size() > 1) @@ -327,7 +327,7 @@ struct MistralPacker for (int i = 0; i < int(chain.size()); i++) { auto &c = chain.at(i); log_info(" i=%d cell=%s dy=%d z=%d ci=%s co=%s\n", i, ctx->nameOf(c), c->constr_y, c->constr_z, - ctx->nameOf(get_net_or_empty(c, id_CI)), ctx->nameOf(get_net_or_empty(c, id_CO))); + ctx->nameOf(c->getPort(id_CI)), ctx->nameOf(c->getPort(id_CO))); } } } @@ -338,7 +338,7 @@ struct MistralPacker continue; if (ci->cluster == ClusterId()) log_error("Failed to include arith cell '%s' in any chain (CI=%s)\n", ctx->nameOf(ci), - ctx->nameOf(get_net_or_empty(ci, id_CI))); + ctx->nameOf(ci->getPort(id_CI))); } } diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 4aaecdf47d..c460e14bab 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -421,7 +421,7 @@ struct NexusFasmWriter BelId bel = cell->bel; used_io.insert(bel); push_bel(bel); - const NetInfo *t = get_net_or_empty(cell, id_T); + const NetInfo *t = cell->getPort(id_T); auto tmux = ctx->get_cell_pinmux(cell, id_T); bool is_input = false, is_output = false; if (tmux == PINMUX_0) { @@ -444,7 +444,7 @@ struct NexusFasmWriter used_io.insert(bel); push_bel(bel); push("SEIO18"); - const NetInfo *t = get_net_or_empty(cell, id_T); + const NetInfo *t = cell->getPort(id_T); auto tmux = ctx->get_cell_pinmux(cell, id_T); bool is_input = false, is_output = false; if (tmux == PINMUX_0) { @@ -481,7 +481,7 @@ struct NexusFasmWriter bank.diff_used = true; - const NetInfo *t = get_net_or_empty(cell, id_T); + const NetInfo *t = cell->getPort(id_T); auto tmux = ctx->get_cell_pinmux(cell, id_T); bool is_input = false, is_output = false; if (tmux == PINMUX_0) { @@ -843,7 +843,7 @@ struct NexusFasmWriter if (!ci->attrs.count(id_IO_TYPE)) continue; // VccO only concerns outputs - const NetInfo *t = get_net_or_empty(ci, id_T); + const NetInfo *t = ci->getPort(id_T); auto tmux = ctx->get_cell_pinmux(ci, id_T); if (tmux == PINMUX_1 || (tmux != PINMUX_0 && t == nullptr)) continue; diff --git a/nexus/pack.cc b/nexus/pack.cc index 13522c78c7..5509d997e1 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -126,12 +126,12 @@ struct NexusPacker for (auto pname : orig_port_names) { if (rule.port_multixform.count(pname)) { auto old_port = ci->ports.at(pname); - disconnect_port(ctx, ci, pname); + ci->disconnectPort(pname); ci->ports.erase(pname); for (auto new_name : rule.port_multixform.at(pname)) { ci->ports[new_name].name = new_name; ci->ports[new_name].type = old_port.type; - connect_port(ctx, old_port.net, ci, new_name); + ci->connectPort(new_name, old_port.net); } } else { IdString new_name; @@ -145,7 +145,7 @@ struct NexusPacker new_name = ctx->id(stripped_name); } if (new_name != pname) { - rename_port(ctx, ci, pname, new_name); + ci->renamePort(pname, new_name); } } } @@ -307,7 +307,7 @@ struct NexusPacker CellInfo *ci = cell.second.get(); if (ci->type != type) continue; - NetInfo *z = get_net_or_empty(ci, id_Z); + NetInfo *z = ci->getPort(id_Z); if (z == nullptr) continue; return z; @@ -316,13 +316,13 @@ struct NexusPacker NetInfo *new_net = ctx->createNet(ctx->id(stringf("$CONST_%s_NET_", type.c_str(ctx)))); CellInfo *new_cell = ctx->createCell(ctx->id(stringf("$CONST_%s_DRV_", type.c_str(ctx))), type); new_cell->addOutput(id_Z); - connect_port(ctx, new_net, new_cell, id_Z); + new_cell->connectPort(id_Z, new_net); return new_net; } CellPinMux get_pin_needed_muxval(CellInfo *cell, IdString port) { - NetInfo *net = get_net_or_empty(cell, port); + NetInfo *net = cell->getPort(port); if (net == nullptr || net->driver.cell == nullptr) { // Pin is disconnected // If a mux value exists already, honour it @@ -353,14 +353,14 @@ struct NexusPacker void uninvert_port(CellInfo *cell, IdString port) { // Rewire a port so it is driven by the input to an inverter - NetInfo *net = get_net_or_empty(cell, port); + NetInfo *net = cell->getPort(port); NPNR_ASSERT(net != nullptr && net->driver.cell != nullptr && net->driver.cell->type == id_INV); CellInfo *inv = net->driver.cell; - disconnect_port(ctx, cell, port); + cell->disconnectPort(port); - NetInfo *inv_a = get_net_or_empty(inv, id_A); + NetInfo *inv_a = inv->getPort(id_A); if (inv_a != nullptr) { - connect_port(ctx, inv_a, cell, port); + cell->connectPort(port, inv_a); } } @@ -373,7 +373,7 @@ struct NexusPacker CellInfo *ci = cell.second.get(); if (ci->type != id_INV && ci->type != id_VLO && ci->type != id_VHI && ci->type != id_VCC_DRV) continue; - NetInfo *z = get_net_or_empty(ci, id_Z); + NetInfo *z = ci->getPort(id_Z); if (z == nullptr) { trim_cells.push_back(ci->name); continue; @@ -381,7 +381,7 @@ struct NexusPacker if (!z->users.empty()) continue; - disconnect_port(ctx, ci, id_A); + ci->disconnectPort(id_A); trim_cells.push_back(ci->name); trim_nets.push_back(z->name); @@ -415,7 +415,7 @@ struct NexusPacker for (IdString port : port_names) { IdString new_name = ctx->id(remove_brackets(port.str(ctx))); if (new_name != port) - rename_port(ctx, cell, port, new_name); + cell->renamePort(port, new_name); } } @@ -453,17 +453,17 @@ struct NexusPacker if ((cell->type == id_OXIDE_COMB) && (req_mux == PINMUX_1)) { // We need to add a connection to the dedicated Vcc resource that can feed these cell ports - disconnect_port(ctx, cell, port_name); - connect_port(ctx, dedi_vcc_net, cell, port_name); + cell->disconnectPort(port_name); + cell->connectPort(port_name, dedi_vcc_net); continue; } - disconnect_port(ctx, cell, port_name); + cell->disconnectPort(port_name); ctx->set_cell_pinmux(cell, port_name, req_mux); } else if (port.second.net == nullptr) { // If the port is disconnected; and there is no hard constant // then we need to connect it to the relevant soft-constant net - connect_port(ctx, (req_mux == PINMUX_1) ? vcc_net : gnd_net, cell, port_name); + cell->connectPort(port_name, (req_mux == PINMUX_1) ? vcc_net : gnd_net); } } } @@ -486,7 +486,7 @@ struct NexusPacker if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { // Might have an input buffer (IB etc) connected to it is_npnr_iob = true; - NetInfo *o = get_net_or_empty(ci, id_O); + NetInfo *o = ci->getPort(id_O); if (o == nullptr) ; else if (o->users.size() > 1) @@ -497,7 +497,7 @@ struct NexusPacker if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { // Might have an output buffer (OB etc) connected to it is_npnr_iob = true; - NetInfo *i = get_net_or_empty(ci, id_I); + NetInfo *i = ci->getPort(id_I); if (i != nullptr && i->driver.cell != nullptr) { if (top_port.cell != nullptr) log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); @@ -534,8 +534,8 @@ struct NexusPacker port.second.net = top_port.cell->ports.at(top_port.port).net; } // Now remove the nextpnr-inserted buffer - disconnect_port(ctx, ci, id_I); - disconnect_port(ctx, ci, id_O); + ci->disconnectPort(id_I); + ci->disconnectPort(id_O); ctx->cells.erase(port.first); } } @@ -635,13 +635,13 @@ struct NexusPacker // For non-bidirectional IO, we also need to configure tristate and rename B if (ci->type == id_IB) { ctx->set_cell_pinmux(ci, id_T, PINMUX_1); - rename_port(ctx, ci, id_I, id_B); + ci->renamePort(id_I, id_B); } else if (ci->type == id_OB) { ctx->set_cell_pinmux(ci, id_T, PINMUX_0); - rename_port(ctx, ci, id_O, id_B); + ci->renamePort(id_O, id_B); } else if (ci->type == id_OBZ) { ctx->set_cell_pinmux(ci, id_T, PINMUX_SIG); - rename_port(ctx, ci, id_O, id_B); + ci->renamePort(id_O, id_B); } // Get the IO bel BelId bel = get_bel_attr(ci); @@ -760,7 +760,7 @@ struct NexusPacker if (cell->attrs.count(id_BEL)) return false; - NetInfo *pin_net = get_net_or_empty(cell, pin); + NetInfo *pin_net = cell->getPort(pin); if (pin_net == nullptr) return false; @@ -827,7 +827,7 @@ struct NexusPacker buffer->addInput(i); buffer->addOutput(o); // Drive the buffered net with the buffer - connect_port(ctx, buffered_net, buffer, o); + buffer->connectPort(o, buffered_net); // Filter users std::vector remaining_users; @@ -843,7 +843,7 @@ struct NexusPacker std::swap(net->users, remaining_users); // Connect buffer input to original net - connect_port(ctx, net, buffer, i); + buffer->connectPort(i, net); return buffer; } @@ -936,56 +936,56 @@ struct NexusPacker combs.push_back( ctx->createCell(ctx->id(stringf("%s$lutram_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); // Rewiring - external WCK and WRE - replace_port(ci, id_WCK, ramw, id_CLK); - replace_port(ci, id_WRE, ramw, id_LSR); + ci->movePortTo(id_WCK, ramw, id_CLK); + ci->movePortTo(id_WRE, ramw, id_LSR); // Internal WCK and WRE signals ramw->addOutput(id_WCKO); ramw->addOutput(id_WREO); NetInfo *int_wck = ctx->createNet(ctx->id(stringf("%s$lutram_wck$", ctx->nameOf(ci)))); NetInfo *int_wre = ctx->createNet(ctx->id(stringf("%s$lutram_wre$", ctx->nameOf(ci)))); - connect_port(ctx, int_wck, ramw, id_WCKO); - connect_port(ctx, int_wre, ramw, id_WREO); + ramw->connectPort(id_WCKO, int_wck); + ramw->connectPort(id_WREO, int_wre); uint64_t initval = ctx->parse_lattice_param(ci, id_INITVAL, 64, 0).as_int64(); // Rewiring - buses for (int i = 0; i < 4; i++) { // Write address - external - replace_port(ci, bus("WAD", i), ramw, ramw_wado[i]); + ci->movePortTo(bus("WAD", i), ramw, ramw_wado[i]); // Write data - external - replace_port(ci, bus("DI", i), ramw, ramw_wdo[i]); + ci->movePortTo(bus("DI", i), ramw, ramw_wdo[i]); // Read data - replace_port(ci, bus("DO", i), combs[i], id_F); + ci->movePortTo(bus("DO", i), combs[i], id_F); // Read address - NetInfo *rad = get_net_or_empty(ci, bus("RAD", i)); + NetInfo *rad = ci->getPort(bus("RAD", i)); if (rad != nullptr) { for (int j = 0; j < 4; j++) { IdString port = (j % 2) ? comb1_rad[i] : comb0_rad[i]; combs[j]->addInput(port); - connect_port(ctx, rad, combs[j], port); + combs[j]->connectPort(port, rad); } - disconnect_port(ctx, ci, bus("RAD", i)); + ci->disconnectPort(bus("RAD", i)); } // Write address - internal NetInfo *int_wad = ctx->createNet(ctx->id(stringf("%s$lutram_wad[%d]$", ctx->nameOf(ci), i))); ramw->addOutput(bus_flat("WADO", i)); - connect_port(ctx, int_wad, ramw, bus_flat("WADO", i)); + ramw->connectPort(bus_flat("WADO", i), int_wad); for (int j = 0; j < 4; j++) { combs[j]->addInput(bus_flat("WAD", i)); - connect_port(ctx, int_wad, combs[j], bus_flat("WAD", i)); + combs[j]->connectPort(bus_flat("WAD", i), int_wad); } // Write data - internal NetInfo *int_wd = ctx->createNet(ctx->id(stringf("%s$lutram_wd[%d]$", ctx->nameOf(ci), i))); ramw->addOutput(bus_flat("WDO", i)); - connect_port(ctx, int_wd, ramw, bus_flat("WDO", i)); + ramw->connectPort(bus_flat("WDO", i), int_wd); combs[i]->addInput(id_WDI); - connect_port(ctx, int_wd, combs[i], id_WDI); + combs[i]->connectPort(id_WDI, int_wd); // Write clock and enable - internal combs[i]->addInput(id_WCK); combs[i]->addInput(id_WRE); - connect_port(ctx, int_wck, combs[i], id_WCK); - connect_port(ctx, int_wre, combs[i], id_WRE); + combs[i]->connectPort(id_WCK, int_wck); + combs[i]->connectPort(id_WRE, int_wre); // Remap init val uint64_t split_init = 0; for (int j = 0; j < 16; j++) @@ -1178,7 +1178,7 @@ struct NexusPacker base->params[param.first] = param.second; } for (auto &port : mergee->ports) { - replace_port(mergee, port.first, base, port.first); + mergee->movePortTo(port.first, base, port.first); } ctx->cells.erase(mergee->name); } @@ -1191,13 +1191,13 @@ struct NexusPacker if (ci->type != id_IOLOGIC) continue; CellInfo *iob = nullptr; - NetInfo *di = get_net_or_empty(ci, id_DI); + NetInfo *di = ci->getPort(id_DI); if (di != nullptr && di->driver.cell != nullptr) iob = di->driver.cell; - NetInfo *dout = get_net_or_empty(ci, id_DOUT); + NetInfo *dout = ci->getPort(id_DOUT); if (dout != nullptr && dout->users.size() == 1) iob = dout->users.at(0).cell; - NetInfo *tout = get_net_or_empty(ci, id_TOUT); + NetInfo *tout = ci->getPort(id_TOUT); if (tout != nullptr && tout->users.size() == 1) iob = tout->users.at(0).cell; if (iob == nullptr || @@ -1251,20 +1251,20 @@ struct NexusPacker ctx->createCell(ctx->id(stringf("%s$widefn_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); for (int i = 0; i < 2; i++) { - replace_port(ci, bus_flat("A", i), combs[i], id_A); - replace_port(ci, bus_flat("B", i), combs[i], id_B); - replace_port(ci, bus_flat("C", i), combs[i], id_C); - replace_port(ci, bus_flat("D", i), combs[i], id_D); + ci->movePortTo(bus_flat("A", i), combs[i], id_A); + ci->movePortTo(bus_flat("B", i), combs[i], id_B); + ci->movePortTo(bus_flat("C", i), combs[i], id_C); + ci->movePortTo(bus_flat("D", i), combs[i], id_D); } - replace_port(ci, id_SEL, combs[0], id_SEL); - replace_port(ci, id_Z, combs[0], id_OFX); + ci->movePortTo(id_SEL, combs[0], id_SEL); + ci->movePortTo(id_Z, combs[0], id_OFX); NetInfo *f1 = ctx->createNet(ctx->id(stringf("%s$widefn_f1$", ctx->nameOf(ci)))); combs[0]->addInput(id_F1); combs[1]->addOutput(id_F); - connect_port(ctx, f1, combs[1], id_F); - connect_port(ctx, f1, combs[0], id_F1); + combs[1]->connectPort(id_F, f1); + combs[0]->connectPort(id_F1, f1); combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0); combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0); @@ -1290,7 +1290,7 @@ struct NexusPacker CellInfo *ci = cell.second.get(); if (ci->type != id_CCU2) continue; - if (get_net_or_empty(ci, id_CIN) != nullptr) + if (ci->getPort(id_CIN) != nullptr) continue; roots.push_back(ci); } @@ -1309,16 +1309,16 @@ struct NexusPacker // Rewire LUT ports for (int i = 0; i < 2; i++) { combs[i]->params[id_MODE] = std::string("CCU2"); - replace_port(ci, bus_flat("A", i), combs[i], id_A); - replace_port(ci, bus_flat("B", i), combs[i], id_B); - replace_port(ci, bus_flat("C", i), combs[i], id_C); - replace_port(ci, bus_flat("D", i), combs[i], id_D); - replace_port(ci, bus_flat("S", i), combs[i], id_F); + ci->movePortTo(bus_flat("A", i), combs[i], id_A); + ci->movePortTo(bus_flat("B", i), combs[i], id_B); + ci->movePortTo(bus_flat("C", i), combs[i], id_C); + ci->movePortTo(bus_flat("D", i), combs[i], id_D); + ci->movePortTo(bus_flat("S", i), combs[i], id_F); } // External carry chain - replace_port(ci, id_CIN, combs[0], id_FCI); - replace_port(ci, id_COUT, combs[1], id_FCO); + ci->movePortTo(id_CIN, combs[0], id_FCI); + ci->movePortTo(id_COUT, combs[1], id_FCO); // Copy parameters if (ci->params.count(id_INJECT)) @@ -1330,8 +1330,8 @@ struct NexusPacker NetInfo *int_cy = ctx->createNet(ctx->id(stringf("%s$widefn_int_cy$", ctx->nameOf(ci)))); combs[0]->addOutput(id_FCO); combs[1]->addInput(id_FCI); - connect_port(ctx, int_cy, combs[0], id_FCO); - connect_port(ctx, int_cy, combs[1], id_FCI); + combs[0]->connectPort(id_FCO, int_cy); + combs[1]->connectPort(id_FCI, int_cy); // Relative constraints for (int i = 0; i < 2; i++) { @@ -1355,7 +1355,7 @@ struct NexusPacker ctx->cells.erase(ci->name); // Find next cell in chain, if it exists - NetInfo *fco = get_net_or_empty(combs[1], id_FCO); + NetInfo *fco = combs[1]->getPort(id_FCO); ci = nullptr; if (fco != nullptr) { if (fco->users.size() > 1) @@ -1443,13 +1443,13 @@ struct NexusPacker continue; // Skip pins that are already in use - if (get_net_or_empty(other_cell, bp.pin) != nullptr) + if (other_cell->getPort(bp.pin) != nullptr) continue; // Create the input if it doesn't exist if (!other_cell->ports.count(bp.pin)) other_cell->addInput(bp.pin); // Make the connection - connect_ports(ctx, cell, port.first, other_cell, bp.pin); + cell->connectPorts(port.first, other_cell, bp.pin); if (ctx->debug) log_info(" found %s.%s\n", ctx->nameOf(other_cell), ctx->nameOf(bp.pin)); @@ -1729,31 +1729,31 @@ struct NexusPacker if (mt.wide > 0) { // Dot-product mode special case - copy_bus(ctx, ci, ctx->id(stringf("B%d", (i * 9) / mt.wide)), (i * 9) % mt.wide, true, preadd9[i], - id_B, 0, false, 9); - copy_bus(ctx, ci, ctx->id(stringf("A%d", (i * 9) / mt.wide)), (i * 9) % mt.wide, true, mult9[i], - id_A, 0, false, 9); - copy_port(ctx, ci, id_CLK, mult9[i], id_CLK); - copy_port(ctx, ci, (i > 1) ? id_CEA2A3 : id_CEA0A1, mult9[i], id_CEA); - copy_port(ctx, ci, (i > 1) ? id_RSTA2A3 : id_RSTA0A1, mult9[i], id_RSTA); - copy_port(ctx, ci, id_CLK, preadd9[i], id_CLK); - copy_port(ctx, ci, (i > 1) ? id_CEB2B3 : id_CEB0B1, preadd9[i], id_CEB); - copy_port(ctx, ci, (i > 1) ? id_RSTB2B3 : id_RSTB0B1, preadd9[i], id_RSTB); + ci->copyPortBusTo(ctx->id(stringf("B%d", (i * 9) / mt.wide)), (i * 9) % mt.wide, true, preadd9[i], + id_B, 0, false, 9); + ci->copyPortBusTo(ctx->id(stringf("A%d", (i * 9) / mt.wide)), (i * 9) % mt.wide, true, mult9[i], + id_A, 0, false, 9); + ci->copyPortTo(id_CLK, mult9[i], id_CLK); + ci->copyPortTo((i > 1) ? id_CEA2A3 : id_CEA0A1, mult9[i], id_CEA); + ci->copyPortTo((i > 1) ? id_RSTA2A3 : id_RSTA0A1, mult9[i], id_RSTA); + ci->copyPortTo(id_CLK, preadd9[i], id_CLK); + ci->copyPortTo((i > 1) ? id_CEB2B3 : id_CEB0B1, preadd9[i], id_CEB); + ci->copyPortTo((i > 1) ? id_RSTB2B3 : id_RSTB0B1, preadd9[i], id_RSTB); // Copy register configuration copy_param(ci, ctx->id(stringf("REGINPUTAB%d", i)), mult9[i], id_REGBYPSA1); copy_param(ci, ctx->id(stringf("REGINPUTAB%d", i)), preadd9[i], id_REGBYPSBR0); } else { // B input split across pre-adders - copy_bus(ctx, ci, id_B, b_start, true, preadd9[i], id_B, 0, false, 9); + ci->copyPortBusTo(id_B, b_start, true, preadd9[i], id_B, 0, false, 9); // A input split across MULT9s - copy_bus(ctx, ci, id_A, a_start, true, mult9[i], id_A, 0, false, 9); + ci->copyPortBusTo(id_A, a_start, true, mult9[i], id_A, 0, false, 9); // Connect control set signals - copy_port(ctx, ci, id_CLK, mult9[i], id_CLK); - copy_port(ctx, ci, id_CEA, mult9[i], id_CEA); - copy_port(ctx, ci, id_RSTA, mult9[i], id_RSTA); - copy_port(ctx, ci, id_CLK, preadd9[i], id_CLK); - copy_port(ctx, ci, id_CEB, preadd9[i], id_CEB); - copy_port(ctx, ci, id_RSTB, preadd9[i], id_RSTB); + ci->copyPortTo(id_CLK, mult9[i], id_CLK); + ci->copyPortTo(id_CEA, mult9[i], id_CEA); + ci->copyPortTo(id_RSTA, mult9[i], id_RSTA); + ci->copyPortTo(id_CLK, preadd9[i], id_CLK); + ci->copyPortTo(id_CEB, preadd9[i], id_CEB); + ci->copyPortTo(id_RSTB, preadd9[i], id_RSTB); // Copy register configuration copy_param(ci, id_REGINPUTA, mult9[i], id_REGBYPSA1); copy_param(ci, id_REGINPUTB, preadd9[i], id_REGBYPSBR0); @@ -1761,12 +1761,12 @@ struct NexusPacker // Connect and configure pre-adder if it isn't bypassed if (mt.has_preadd) { - copy_bus(ctx, ci, id_C, 9 * i, true, preadd9[i], id_C, 0, false, 9); + ci->copyPortBusTo(id_C, 9 * i, true, preadd9[i], id_C, 0, false, 9); if (i == (mt.N9x9 - 1)) - copy_port(ctx, ci, id_SIGNEDC, preadd9[i], id_C9); + ci->copyPortTo(id_SIGNEDC, preadd9[i], id_C9); copy_param(ci, id_REGINPUTC, preadd9[i], id_REGBYPSBL); - copy_port(ctx, ci, id_CEC, preadd9[i], id_CECL); - copy_port(ctx, ci, id_RSTC, preadd9[i], id_RSTCL); + ci->copyPortTo(id_CEC, preadd9[i], id_CECL); + ci->copyPortTo(id_RSTC, preadd9[i], id_RSTCL); // Enable preadder preadd9[i]->params[id_BYPASS_PREADD9] = std::string("USED"); preadd9[i]->params[id_OPC] = std::string("INPUT_C_AS_PREADDER_OPERAND"); @@ -1774,14 +1774,14 @@ struct NexusPacker preadd9[i]->params[id_PREADDCAS_EN] = std::string("ENABLED"); } else if (mt.has_addsub) { // Connect only for routeability reasons - copy_bus(ctx, ci, id_C, 10 * i + ((i >= 4) ? 14 : 0), true, preadd9[i], id_C, 0, false, 10); + ci->copyPortBusTo(id_C, 10 * i + ((i >= 4) ? 14 : 0), true, preadd9[i], id_C, 0, false, 10); } // Connect up signedness for the most significant nonet if (((b_start + 9) == mt.b_width) || (mt.wide > 0)) - copy_port(ctx, ci, mt.has_addsub ? id_SIGNED : id_SIGNEDB, preadd9[i], id_BSIGNED); + ci->copyPortTo(mt.has_addsub ? id_SIGNED : id_SIGNEDB, preadd9[i], id_BSIGNED); if (((a_start + 9) == mt.a_width) || (mt.wide > 0)) - copy_port(ctx, ci, mt.has_addsub ? id_SIGNED : id_SIGNEDA, mult9[i], id_ASIGNED); + ci->copyPortTo(mt.has_addsub ? id_SIGNED : id_SIGNEDA, mult9[i], id_ASIGNED); } bool mult36_used = (mt.a_width >= 36) && (mt.b_width >= 36) && !(mt.wide > 0); @@ -1800,11 +1800,11 @@ struct NexusPacker for (int i = 0; i < Nreg18; i++) { // Output split across reg18s if (!mt.has_addsub) - replace_bus(ctx, ci, id_Z, i * 18, true, reg18[i], id_PP, 0, false, 18); + ci->movePortBusTo(id_Z, i * 18, true, reg18[i], id_PP, 0, false, 18); // Connect control set signals - copy_port(ctx, ci, id_CLK, reg18[i], id_CLK); - copy_port(ctx, ci, mt.has_addsub ? id_CEPIPE : id_CEOUT, reg18[i], id_CEP); - copy_port(ctx, ci, mt.has_addsub ? id_RSTPIPE : id_RSTOUT, reg18[i], id_RSTP); + ci->copyPortTo(id_CLK, reg18[i], id_CLK); + ci->copyPortTo(mt.has_addsub ? id_CEPIPE : id_CEOUT, reg18[i], id_CEP); + ci->copyPortTo(mt.has_addsub ? id_RSTPIPE : id_RSTOUT, reg18[i], id_RSTP); // Copy register configuration copy_param(ci, mt.has_addsub ? id_REGPIPELINE : id_REGOUTPUT, reg18[i], id_REGBYPS); } @@ -1817,35 +1817,35 @@ struct NexusPacker acc54[i] = create_dsp_cell(ci->name, id_ACC54_CORE, preadd9[0], (i * 4) + 2, 5); for (int i = 0; i < Nacc54; i++) { // C addsub input - copy_bus(ctx, ci, id_C, 54 * i, true, acc54[i], id_CINPUT, 0, false, 54); + ci->copyPortBusTo(id_C, 54 * i, true, acc54[i], id_CINPUT, 0, false, 54); // Output - replace_bus(ctx, ci, id_Z, i * 54, true, acc54[i], id_SUM0, 0, false, 36); - replace_bus(ctx, ci, id_Z, i * 54 + 36, true, acc54[i], id_SUM1, 0, false, 18); + ci->movePortBusTo(id_Z, i * 54, true, acc54[i], id_SUM0, 0, false, 36); + ci->movePortBusTo(id_Z, i * 54 + 36, true, acc54[i], id_SUM1, 0, false, 18); // Control set - copy_port(ctx, ci, id_CLK, acc54[i], id_CLK); - copy_port(ctx, ci, id_RSTCTRL, acc54[i], id_RSTCTRL); - copy_port(ctx, ci, id_CECTRL, acc54[i], id_CECTRL); - copy_port(ctx, ci, id_RSTCIN, acc54[i], id_RSTCIN); - copy_port(ctx, ci, id_CECIN, acc54[i], id_CECIN); - copy_port(ctx, ci, id_RSTOUT, acc54[i], id_RSTO); - copy_port(ctx, ci, id_CEOUT, acc54[i], id_CEO); - copy_port(ctx, ci, id_RSTC, acc54[i], id_RSTC); - copy_port(ctx, ci, id_CEC, acc54[i], id_CEC); + ci->copyPortTo(id_CLK, acc54[i], id_CLK); + ci->copyPortTo(id_RSTCTRL, acc54[i], id_RSTCTRL); + ci->copyPortTo(id_CECTRL, acc54[i], id_CECTRL); + ci->copyPortTo(id_RSTCIN, acc54[i], id_RSTCIN); + ci->copyPortTo(id_CECIN, acc54[i], id_CECIN); + ci->copyPortTo(id_RSTOUT, acc54[i], id_RSTO); + ci->copyPortTo(id_CEOUT, acc54[i], id_CEO); + ci->copyPortTo(id_RSTC, acc54[i], id_RSTC); + ci->copyPortTo(id_CEC, acc54[i], id_CEC); // Add/acc control if (i == 0) - copy_port(ctx, ci, id_CIN, acc54[i], id_CIN); + ci->copyPortTo(id_CIN, acc54[i], id_CIN); else ctx->set_cell_pinmux(acc54[i], id_CIN, PINMUX_1); if (i == (Nacc54 - 1)) - copy_port(ctx, ci, id_SIGNED, acc54[i], id_SIGNEDI); + ci->copyPortTo(id_SIGNED, acc54[i], id_SIGNEDI); if (mt.wide > 0) { - replace_bus(ctx, ci, id_ADDSUB, 0, true, acc54[i], id_ADDSUB, 0, false, 2); - replace_bus(ctx, ci, id_ADDSUB, 2, true, acc54[i], id_M9ADDSUB, 0, false, 2); + ci->movePortBusTo(id_ADDSUB, 0, true, acc54[i], id_ADDSUB, 0, false, 2); + ci->movePortBusTo(id_ADDSUB, 2, true, acc54[i], id_M9ADDSUB, 0, false, 2); } else { - copy_port(ctx, ci, id_ADDSUB, acc54[i], id_ADDSUB0); - copy_port(ctx, ci, id_ADDSUB, acc54[i], id_ADDSUB1); + ci->copyPortTo(id_ADDSUB, acc54[i], id_ADDSUB0); + ci->copyPortTo(id_ADDSUB, acc54[i], id_ADDSUB1); } - copy_port(ctx, ci, id_LOADC, acc54[i], id_LOAD); + ci->copyPortTo(id_LOADC, acc54[i], id_LOAD); // Configuration copy_param(ci, id_REGINPUTC, acc54[i], id_CREGBYPS1); copy_param(ci, id_REGADDSUB, acc54[i], id_ADDSUBSIGNREGBYPS1); @@ -1881,7 +1881,7 @@ struct NexusPacker for (auto cell : to_remove) { for (auto &port : cell->ports) - disconnect_port(ctx, cell, port.first); + cell->disconnectPort(port.first); ctx->cells.erase(cell->name); } } @@ -2042,9 +2042,9 @@ struct NexusPacker CellInfo *ci = cell.second.get(); if (ci->type == id_PLL_CORE) { // Extra log to phys rules - rename_port(ctx, ci, id_PLLPOWERDOWN_N, id_PLLPDN); - rename_port(ctx, ci, id_LMMIWRRD_N, id_LMMIWRRDN); - rename_port(ctx, ci, id_LMMIRESET_N, id_LMMIRESETN); + ci->renamePort(id_PLLPOWERDOWN_N, id_PLLPDN); + ci->renamePort(id_LMMIWRRD_N, id_LMMIWRRDN); + ci->renamePort(id_LMMIRESET_N, id_LMMIRESETN); for (auto &defparam : pll_defaults) if (!ci->params.count(defparam.first)) ci->params[defparam.first] = defparam.second; @@ -2086,7 +2086,7 @@ struct NexusPacker { // Get the net - NetInfo *net = get_net_or_empty(iob, port); + NetInfo *net = iob->getPort(port); if (net == nullptr) { return nullptr; } @@ -2099,8 +2099,8 @@ struct NexusPacker // Get clock nets of IOLOGIC and the flip-flop if (iol != nullptr) { - NetInfo *iol_c = get_net_or_empty(iol, id_SCLKOUT); - NetInfo *ff_c = get_net_or_empty(ff, id_CLK); + NetInfo *iol_c = iol->getPort(id_SCLKOUT); + NetInfo *ff_c = ff->getPort(id_CLK); // If one of them is floating or it is not the same net then abort if (iol_c == nullptr || ff_c == nullptr) { @@ -2113,8 +2113,8 @@ struct NexusPacker // Get reset nets of IOLOGIC and the flip-flop if (iol != nullptr) { - NetInfo *iol_r = get_net_or_empty(iol, id_LSROUT); - NetInfo *ff_r = get_net_or_empty(ff, id_LSR); + NetInfo *iol_r = iol->getPort(id_LSROUT); + NetInfo *ff_r = ff->getPort(id_LSR); // If one of them is floating or it is not the same net then abort. // But both can be floating. @@ -2155,17 +2155,17 @@ struct NexusPacker bool isODDR = false; CellInfo *iob = nullptr; - NetInfo *di = get_net_or_empty(iol, id_DI); + NetInfo *di = iol->getPort(id_DI); if (di != nullptr && di->driver.cell != nullptr) { iob = di->driver.cell; isIDDR = true; } - NetInfo *dout = get_net_or_empty(iol, id_DOUT); + NetInfo *dout = iol->getPort(id_DOUT); if (dout != nullptr && dout->users.size() == 1) { iob = dout->users.at(0).cell; isODDR = true; } - NetInfo *tout = get_net_or_empty(iol, id_TOUT); + NetInfo *tout = iol->getPort(id_TOUT); if (tout != nullptr && tout->users.size() == 1) { iob = tout->users.at(0).cell; isODDR = true; // FIXME: Not sure @@ -2205,9 +2205,9 @@ struct NexusPacker // same ned as SEIO33_CORE.I. // // - NetInfo *iob_t = get_net_or_empty(iob, id_T); + NetInfo *iob_t = iob->getPort(id_T); if (iob_t != nullptr && isODDR) { - NetInfo *iol_t = get_net_or_empty(iol, id_TOUT); + NetInfo *iol_t = iol->getPort(id_TOUT); // SIOLOGIC.TOUT is not driving SEIO33_CORE.T if ((iol_t == nullptr) || (iol_t != nullptr && iol_t->users.empty()) || @@ -2216,7 +2216,7 @@ struct NexusPacker // In this case if SIOLOGIC.TSDATA0 is not connected // to the same net as SEIO33_CORE.T and is not // floating then that configuration is illegal. - NetInfo *iol_ti = get_net_or_empty(iol, id_TSDATA0); + NetInfo *iol_ti = iol->getPort(id_TSDATA0); if (iol_ti != nullptr && (iol_ti->name != iob_t->name) && (iol_ti->name != gnd_net->name)) { log_error("Cannot have %s.TSDATA0 and %s.T driven by different nets (%s vs. %s)\n", ctx->nameOf(iol), ctx->nameOf(iob), ctx->nameOf(iol_ti), ctx->nameOf(iob_t)); @@ -2227,8 +2227,8 @@ struct NexusPacker if (!iol->ports.count(id_TSDATA0)) { iol->addInput(id_TSDATA0); } - disconnect_port(ctx, iol, id_TSDATA0); - connect_port(ctx, iob_t, iol, id_TSDATA0); + iol->disconnectPort(id_TSDATA0); + iol->connectPort(id_TSDATA0, iob_t); if (ctx->debug) { log_info(" Reconnecting %s.TSDATA0 to %s\n", ctx->nameOf(iol), ctx->nameOf(iob_t)); @@ -2256,10 +2256,10 @@ struct NexusPacker for (auto &it : tff_map) { CellInfo *ff = ctx->cells.at(it.first).get(); - NetInfo *ff_d = get_net_or_empty(ff, id_M); // FIXME: id_D or id_M ?! + NetInfo *ff_d = ff->getPort(id_M); // FIXME: id_D or id_M ?! NPNR_ASSERT(ff_d != nullptr); - NetInfo *ff_q = get_net_or_empty(ff, id_Q); + NetInfo *ff_q = ff->getPort(id_Q); NPNR_ASSERT(ff_q != nullptr); for (auto &ios : it.second) { @@ -2269,12 +2269,12 @@ struct NexusPacker log_info(" Integrating %s into %s\n", ctx->nameOf(ff), ctx->nameOf(iol)); // Disconnect "old" T net - disconnect_port(ctx, iol, id_TSDATA0); - disconnect_port(ctx, iob, id_T); + iol->disconnectPort(id_TSDATA0); + iob->disconnectPort(id_T); // Connect the "new" one - connect_port(ctx, ff_d, iol, id_TSDATA0); - connect_port(ctx, ff_d, iob, id_T); + iol->connectPort(id_TSDATA0, ff_d); + iob->connectPort(id_T, ff_d); // Propagate parameters iol->params[id_SRMODE] = ff->params.at(id_SRMODE); @@ -2288,7 +2288,7 @@ struct NexusPacker // Disconnect the flip-flop for (auto &port : ff->ports) { - disconnect_port(ctx, ff, port.first); + ff->disconnectPort(port.first); } // Check if the flip-flop can be removed @@ -2316,9 +2316,9 @@ struct NexusPacker ctrlset.clkmux = ctx->id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; ctrlset.cemux = ctx->id(str_or_default(cell->params, id_CEMUX, "CE")).index; ctrlset.lsrmux = ctx->id(str_or_default(cell->params, id_LSRMUX, "LSR")).index; - ctrlset.clk = get_net_or_empty(cell, id_CLK); - ctrlset.ce = get_net_or_empty(cell, id_CE); - ctrlset.lsr = get_net_or_empty(cell, id_LSR); + ctrlset.clk = cell->getPort(id_CLK); + ctrlset.ce = cell->getPort(id_CE); + ctrlset.lsr = cell->getPort(id_LSR); return ctrlset; } @@ -2353,7 +2353,7 @@ struct NexusPacker // Get input net // At the packing stage all inputs go to M - NetInfo *di = get_net_or_empty(ff, id_M); + NetInfo *di = ff->getPort(id_M); if (di == nullptr || di->driver.cell == nullptr) { continue; } @@ -2373,7 +2373,7 @@ struct NexusPacker } // The FF must not use M and DI at the same time - if (get_net_or_empty(ff, id_DI)) { + if (ff->getPort(id_DI)) { continue; } @@ -2445,7 +2445,7 @@ struct NexusPacker } // Reconnect M to DI - rename_port(ctx, ff, id_M, id_DI); + ff->renamePort(id_M, id_DI); ff->params[id_SEL] = std::string("DL"); // Store FF settings of the cluster @@ -2528,8 +2528,8 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lutInfo.is_memory = str_or_default(cell->params, id_MODE, "LOGIC") == "DPRAM"; cell->lutInfo.is_carry = str_or_default(cell->params, id_MODE, "LOGIC") == "CCU2"; cell->lutInfo.mux2_used = port_used(cell, id_OFX); - cell->lutInfo.f = get_net_or_empty(cell, id_F); - cell->lutInfo.ofx = get_net_or_empty(cell, id_OFX); + cell->lutInfo.f = cell->getPort(id_F); + cell->lutInfo.ofx = cell->getPort(id_OFX); if (cell->lutInfo.is_carry) { cell->tmg_portmap[id_A] = id_A0; cell->tmg_portmap[id_B] = id_B0; @@ -2551,11 +2551,11 @@ void Arch::assignCellInfo(CellInfo *cell) cell->ffInfo.ctrlset.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; cell->ffInfo.ctrlset.cemux = id(str_or_default(cell->params, id_CEMUX, "CE")).index; cell->ffInfo.ctrlset.lsrmux = id(str_or_default(cell->params, id_LSRMUX, "LSR")).index; - cell->ffInfo.ctrlset.clk = get_net_or_empty(cell, id_CLK); - cell->ffInfo.ctrlset.ce = get_net_or_empty(cell, id_CE); - cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); - cell->ffInfo.di = get_net_or_empty(cell, id_DI); - cell->ffInfo.m = get_net_or_empty(cell, id_M); + cell->ffInfo.ctrlset.clk = cell->getPort(id_CLK); + cell->ffInfo.ctrlset.ce = cell->getPort(id_CE); + cell->ffInfo.ctrlset.lsr = cell->getPort(id_LSR); + cell->ffInfo.di = cell->getPort(id_DI); + cell->ffInfo.m = cell->getPort(id_M); cell->tmg_index = get_cell_timing_idx(id_OXIDE_FF, id("PPP:SYNC")); } else if (cell->type == id_RAMW) { cell->ffInfo.ctrlset.async = true; @@ -2564,9 +2564,9 @@ void Arch::assignCellInfo(CellInfo *cell) cell->ffInfo.ctrlset.clkmux = id(str_or_default(cell->params, id_CLKMUX, "CLK")).index; cell->ffInfo.ctrlset.cemux = ID_CE; cell->ffInfo.ctrlset.lsrmux = ID_INV; - cell->ffInfo.ctrlset.clk = get_net_or_empty(cell, id_CLK); + cell->ffInfo.ctrlset.clk = cell->getPort(id_CLK); cell->ffInfo.ctrlset.ce = nullptr; - cell->ffInfo.ctrlset.lsr = get_net_or_empty(cell, id_LSR); + cell->ffInfo.ctrlset.lsr = cell->getPort(id_LSR); cell->ffInfo.di = nullptr; cell->ffInfo.m = nullptr; cell->tmg_index = get_cell_timing_idx(id_RAMW); diff --git a/nexus/post_place.cc b/nexus/post_place.cc index f42c0f1892..289f82b254 100644 --- a/nexus/post_place.cc +++ b/nexus/post_place.cc @@ -94,12 +94,12 @@ struct NexusPostPlaceOpt if (ff->type != id_OXIDE_FF) continue; // Check M ('fabric') input net - NetInfo *m = get_net_or_empty(ff, id_M); + NetInfo *m = ff->getPort(id_M); if (m == nullptr) continue; // Ignore FFs that need both DI and M (PRLD mode) - if (get_net_or_empty(ff, id_DI) != nullptr) + if (ff->getPort(id_DI) != nullptr) continue; const auto &drv = m->driver; @@ -118,7 +118,7 @@ struct NexusPostPlaceOpt if (dest_ff != ff->bel) { // If dest_ff is already placed *and* using direct 'DI' input, don't touch it CellInfo *dest_ff_cell = ctx->getBoundBelCell(dest_ff); - if (dest_ff_cell != nullptr && get_net_or_empty(dest_ff_cell, id_DI) != nullptr) + if (dest_ff_cell != nullptr && dest_ff_cell->getPort(id_DI) != nullptr) continue; // Attempt the swap bool swap_result = swap_cell_placement(ff, dest_ff); @@ -126,7 +126,7 @@ struct NexusPostPlaceOpt continue; } // Use direct interconnect - rename_port(ctx, ff, id_M, id_DI); + ff->renamePort(id_M, id_DI); ff->params[id_SEL] = std::string("DL"); ++moves_made; continue; From 75c45dbef158a7f8867e5fc9565cb14a5f06ab2a Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 20 Feb 2022 18:26:17 +0000 Subject: [PATCH 070/712] Add IdStringList::concat overrides taking IdString Signed-off-by: gatecat --- common/idstringlist.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/idstringlist.h b/common/idstringlist.h index fd57540b39..5e462d0e8d 100644 --- a/common/idstringlist.h +++ b/common/idstringlist.h @@ -67,6 +67,10 @@ struct IdStringList } static IdStringList concat(IdStringList a, IdStringList b); + static IdStringList concat(IdString a, IdString b) { return concat(IdStringList(a), IdStringList(b)); } + static IdStringList concat(IdStringList a, IdString b) { return concat(a, IdStringList(b)); } + static IdStringList concat(IdString a, IdStringList b) { return concat(IdStringList(a), b); } + IdStringList slice(size_t s, size_t e) const; unsigned int hash() const From ad49b7c78d393cf95d5d80707008d5904d302df2 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 23 Feb 2022 15:53:04 +1000 Subject: [PATCH 071/712] gowin: Add support for true differential output The new primitive appears as an amalgamation of two existing OBUF primitives. Compatible with older versions of apicula, although, of course, using TLVDS_OBUF with old databases will not bring the desired result, but no crash. Signed-off-by: YRabbit --- gowin/constids.inc | 5 +++ gowin/pack.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/gowin/constids.inc b/gowin/constids.inc index be016e0e62..3e72340abd 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -636,6 +636,8 @@ X(I) X(O) X(IO) X(OE) +X(OB) + // bels X(DFF0) X(DFF1) @@ -735,6 +737,7 @@ X(IBUF) X(OBUF) X(IOBUF) X(TBUF) +X(TLVDS_OBUF) // primitive attributes X(INIT) @@ -744,6 +747,8 @@ X(INPUT_USED) X(OUTPUT_USED) X(ENABLE_USED) X(BEL) +X(DIFF) +X(DIFF_TYPE) // ports X(EN) diff --git a/gowin/pack.cc b/gowin/pack.cc index 268f26ef4e..cc71586436 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -678,6 +678,85 @@ static bool is_gowin_iob(const Context *ctx, const CellInfo *cell) } } +static bool is_gowin_diff_iob(const Context *ctx, const CellInfo *cell) +{ + switch (cell->type.index) { + case ID_TLVDS_OBUF: + return true; + default: + return false; + } +} + +static bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); } + +// Pack differential IO buffers +static void pack_diff_io(Context *ctx) +{ + pool packed_cells; + pool delete_nets; + + std::vector> new_cells; + log_info("Packing diff IOs..\n"); + + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ctx->verbose) + log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); + if (is_gowin_diff_iob(ctx, ci)) { + CellInfo *iob_p = nullptr; + CellInfo *iob_n = nullptr; + switch (ci->type.index) { + case ID_TLVDS_OBUF: { + iob_p = net_only_drives(ctx, ci->ports.at(id_O).net, is_iob, id_I); + iob_n = net_only_drives(ctx, ci->ports.at(id_OB).net, is_iob, id_I); + NPNR_ASSERT(iob_p != nullptr); + NPNR_ASSERT(iob_n != nullptr); + auto iob_p_bel_a = iob_p->attrs.find(id_BEL); + if (iob_p_bel_a == ci->attrs.end()) { + log_error("LVDS '%s' must be restricted.\n", ctx->nameOf(ci)); + continue; + } + BelId iob_p_bel = ctx->getBelByNameStr(iob_p_bel_a->second.as_string()); + Loc loc_p = ctx->getBelLocation(iob_p_bel); + if (loc_p.z != 0) { + log_error("LVDS '%s' positive pin is not A.\n", ctx->nameOf(ci)); + continue; + } + // restrict the N buffer + loc_p.z = 1; + iob_n->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(loc_p)).str(ctx); + // mark IOBs as part of DS pair + iob_n->attrs[id_DIFF] = std::string("N"); + iob_n->attrs[id_DIFF_TYPE] = std::string("TLVDS_OBUF"); + iob_p->attrs[id_DIFF] = std::string("P"); + iob_p->attrs[id_DIFF_TYPE] = std::string("TLVDS_OBUF"); + // disconnect N input: it is wired internally + delete_nets.insert(iob_n->ports.at(id_I).net->name); + iob_n->disconnectPort(id_I); + ci->disconnectPort(id_OB); + // disconnect P output + delete_nets.insert(ci->ports.at(id_O).net->name); + ci->disconnectPort(id_O); + // connect TLVDS input to P input + ci->movePortTo(id_I, iob_p, id_I); + packed_cells.insert(ci->name); + } break; + default: + break; + } + } + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + for (auto dnet : delete_nets) { + ctx->nets.erase(dnet); + } + for (auto &ncell : new_cells) { + ctx->cells[ncell->name] = std::move(ncell); + } +} // Pack IO buffers static void pack_io(Context *ctx) { @@ -689,6 +768,8 @@ static void pack_io(Context *ctx) for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); + if (ctx->verbose) + log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); if (is_gowin_iob(ctx, ci)) { CellInfo *iob = nullptr; switch (ci->type.index) { @@ -776,6 +857,7 @@ bool Arch::pack() log_break(); pack_constants(ctx); pack_io(ctx); + pack_diff_io(ctx); pack_wideluts(ctx); pack_alus(ctx); pack_lut_lutffs(ctx); From 3894a36ddbbaf30494b77f0f108cfa359886807a Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 24 Feb 2022 13:35:22 +1000 Subject: [PATCH 072/712] gowin: recognize partnumbers of GW1NZ-1 The model should be recognized by the partnumber, --family is needed only if the same partnumbers belong to different models. This is done in order to automatically generate parameters for calling nextpnr from Gowin files without problems: there also only partnumber is used and only in some cases the model is specified with the -name parameter and GW1NZ-1 is not such a case. Signed-off-by: YRabbit --- gowin/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gowin/main.cc b/gowin/main.cc index 38a67fcd18..1473f3e89b 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -55,7 +55,7 @@ po::options_description GowinCommandHandler::getArchOptions() std::unique_ptr GowinCommandHandler::createContext(dict &values) { - std::regex devicere = std::regex("GW1N(S?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*"); + std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*"); std::smatch match; std::string device = vm["device"].as(); if (!std::regex_match(device, match, devicere)) { From fbb02e286047108dbfbd6bd71cddc777a6752ecf Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 24 Feb 2022 20:29:43 +0000 Subject: [PATCH 073/712] okami: new Viaduct arch --- generic/family.cmake | 2 +- generic/viaduct/okami/constids.inc | 14 + generic/viaduct/okami/okami.cc | 546 +++++++++++++++++++++++ generic/viaduct/okami/okami_map.v | 12 + generic/viaduct/okami/okami_prims.v | 35 ++ generic/viaduct/okami/synth_okami.tcl | 24 + generic/viaduct/okami/viaduct_example.sh | 6 + 7 files changed, 638 insertions(+), 1 deletion(-) create mode 100644 generic/viaduct/okami/constids.inc create mode 100644 generic/viaduct/okami/okami.cc create mode 100644 generic/viaduct/okami/okami_map.v create mode 100644 generic/viaduct/okami/okami_prims.v create mode 100644 generic/viaduct/okami/synth_okami.tcl create mode 100755 generic/viaduct/okami/viaduct_example.sh diff --git a/generic/family.cmake b/generic/family.cmake index cd4e3801e4..de4ce1afa8 100644 --- a/generic/family.cmake +++ b/generic/family.cmake @@ -1,4 +1,4 @@ -set(VIADUCT_UARCHES "example") +set(VIADUCT_UARCHES "example" "okami") foreach(uarch ${VIADUCT_UARCHES}) aux_source_directory(${family}/viaduct/${uarch} UARCH_FILES) foreach(target ${family_targets}) diff --git a/generic/viaduct/okami/constids.inc b/generic/viaduct/okami/constids.inc new file mode 100644 index 0000000000..b40d5be8a0 --- /dev/null +++ b/generic/viaduct/okami/constids.inc @@ -0,0 +1,14 @@ +X(LUT4) +X(DFF) +X(CLK) +X(D) +X(F) +X(Q) +X(INBUF) +X(OUTBUF) +X(I) +X(EN) +X(O) +X(IOB) +X(PAD) +X(INIT) diff --git a/generic/viaduct/okami/okami.cc b/generic/viaduct/okami/okami.cc new file mode 100644 index 0000000000..bcb34e8472 --- /dev/null +++ b/generic/viaduct/okami/okami.cc @@ -0,0 +1,546 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * Copyright (C) 2022 Lofty + * + * 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" +#include "viaduct_api.h" +#include "viaduct_helpers.h" + +#define GEN_INIT_CONSTIDS +#define VIADUCT_CONSTIDS "viaduct/okami/constids.inc" +#include "viaduct_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct OkamiImpl : ViaductAPI +{ + ~OkamiImpl(){}; + void init(Context *ctx) override + { + init_uarch_constids(ctx); + ViaductAPI::init(ctx); + h.init(ctx); + init_wires(); + init_bels(); + init_pips(); + } + + void pack() override + { + // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis + const pool top_ports{ + CellTypePort(id_INBUF, id_PAD), + CellTypePort(id_OUTBUF, id_PAD), + }; + h.remove_nextpnr_iobs(top_ports); + // Replace constants with LUTs + const dict vcc_params = {{id_INIT, Property(0xFFFF, 16)}}; + const dict gnd_params = {{id_INIT, Property(0x0000, 16)}}; + h.replace_constants(CellTypePort(id_LUT4, id_F), CellTypePort(id_LUT4, id_F), vcc_params, gnd_params); + // Constrain directly connected LUTs and FFs together to use dedicated resources + int lutffs = h.constrain_cell_pairs(pool{{id_LUT4, id_F}}, pool{{id_DFF, id_D}}, 1, + false); + log_info("Constrained %d LUTFF pairs.\n", lutffs); + } + + void prePlace() override { assign_cell_info(); } + + bool isBelLocationValid(BelId bel) const override + { + Loc l = ctx->getBelLocation(bel); + if (is_io(l.x, l.y)) { + return true; + } else { + return slice_valid(l.x, l.y, l.z / 2); + } + } + + private: + ViaductHelpers h; + // Configuration + // Grid size including IOBs at edges + const int M = 32; + const int X = M, Y = M; + // SLICEs per tile + const int N = 8; + // LUT input count + const int K = 4; + // Number of tile input buses + const int InputMuxCount = 10; // >= 6 for attosoc; >= 10 for arbiter + // Number of output wires in a direction + const int OutputMuxCount = 8; // >= 5 for attosoc; >= 8 for arbiter + + // For fast wire lookups + struct TileWires + { + std::vector clk, q, f, d; + std::vector slice_inputs; + std::vector slice_outputs; + std::vector tile_inputs_north, tile_inputs_east, tile_inputs_south, tile_inputs_west; + std::vector tile_outputs_north, tile_outputs_east, tile_outputs_south, tile_outputs_west; + std::vector pad; + }; + + std::vector> wires_by_tile; + + // Create wires to attach to bels and pips + void init_wires() + { + NPNR_ASSERT(X >= 3); + NPNR_ASSERT(Y >= 3); + NPNR_ASSERT(K >= 2); + NPNR_ASSERT(N >= 1); + NPNR_ASSERT(InputMuxCount >= OutputMuxCount); + + log_info("Creating wires...\n"); + wires_by_tile.resize(Y); + for (int y = 0; y < Y; y++) { + auto &row_wires = wires_by_tile.at(y); + row_wires.resize(X); + for (int x = 0; x < X; x++) { + auto &w = row_wires.at(x); + for (int z = 0; z < N; z++) { + // Clock input + w.clk.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("CLK%d", z))), ctx->id("CLK"), x, y)); + // FF input + w.d.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("D%d", z))), ctx->id("D"), x, y)); + // FF and LUT outputs + w.q.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("Q%d", z))), ctx->id("Q"), x, y)); + w.f.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("F%d", z))), ctx->id("F"), x, y)); + // LUT inputs + for (int i = 0; i < K; i++) + w.slice_inputs.push_back( + ctx->addWire(h.xy_id(x, y, ctx->id(stringf("L%dI%d", z, i))), ctx->id("I"), x, y)); + w.slice_outputs.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("SLICEOUT[%d]", z))), + ctx->id("SLICEOUT"), x, y)); + } + // Tile inputs + for (int tile_input = 0; tile_input < InputMuxCount; tile_input++) { + w.tile_inputs_north.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEINN[%d]", tile_input))), ctx->id("TILEINN"), x, y)); + w.tile_inputs_east.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEINE[%d]", tile_input))), ctx->id("TILEINE"), x, y)); + w.tile_inputs_south.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEINS[%d]", tile_input))), ctx->id("TILEINS"), x, y)); + w.tile_inputs_west.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEINW[%d]", tile_input))), ctx->id("TILEINW"), x, y)); + } + // Tile outputs + for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) { + w.tile_outputs_north.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEOUTN[%d]", tile_output))), ctx->id("TILEOUTN"), x, y)); + w.tile_outputs_east.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEOUTE[%d]", tile_output))), ctx->id("TILEOUTE"), x, y)); + w.tile_outputs_south.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEOUTS[%d]", tile_output))), ctx->id("TILEOUTS"), x, y)); + w.tile_outputs_west.push_back(ctx->addWire( + h.xy_id(x, y, ctx->id(stringf("TILEOUTW[%d]", tile_output))), ctx->id("TILEOUTW"), x, y)); + } + // Pad wires for IO + if (is_io(x, y) && x != y) + for (int z = 0; z < 2; z++) + w.pad.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("PAD%d", z))), id_PAD, x, y)); + } + } + } + bool is_io(int x, int y) const + { + // IO are on the edges of the device + return (x == 0) || (x == (X - 1)) || (y == 0) || (y == (Y - 1)); + } + // Create IO bels in an IO tile + void add_io_bels(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + for (int z = 0; z < 2; z++) { + BelId b = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("IO%d", z))), id_IOB, Loc(x, y, z), false, false); + ctx->addBelInout(b, id_PAD, w.pad.at(z)); + ctx->addBelInput(b, id_I, w.slice_inputs.at(z * K + 0)); + ctx->addBelInput(b, id_EN, w.slice_inputs.at(z * K + 1)); + ctx->addBelOutput(b, id_O, w.slice_outputs.at(z)); + } + } + PipId add_pip(Loc loc, WireId src, WireId dst, delay_t delay = 0.05) + { + IdStringList name = IdStringList::concat(ctx->getWireName(dst), ctx->getWireName(src)); + return ctx->addPip(name, ctx->id("PIP"), src, dst, delay, loc); + } + // Create LUT and FF bels in a logic tile + void add_slice_bels(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + for (int z = 0; z < N; z++) { + // Create LUT bel + BelId lut = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_LUT", z))), id_LUT4, Loc(x, y, z * 2), false, + false); + for (int k = 0; k < K; k++) + ctx->addBelInput(lut, ctx->id(stringf("I[%d]", k)), w.slice_inputs.at(z * K + k)); + ctx->addBelOutput(lut, id_F, w.f.at(z)); + // FF data can come from LUT output or LUT I3 + add_pip(Loc(x, y, 0), w.f.at(z), w.d.at(z)); + add_pip(Loc(x, y, 0), w.slice_inputs.at(z * K + (K - 1)), w.d.at(z)); + // Create DFF bel + BelId dff = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_FF", z))), id_DFF, Loc(x, y, z * 2 + 1), + false, false); + ctx->addBelInput(dff, id_CLK, w.clk.at(z)); + ctx->addBelInput(dff, id_D, w.d.at(z)); + ctx->addBelOutput(dff, id_Q, w.q.at(z)); + } + } + // Create bels according to tile type + void init_bels() + { + log_info("Creating bels...\n"); + for (int y = 0; y < Y; y++) { + for (int x = 0; x < X; x++) { + if (is_io(x, y)) { + if (x == y) + continue; // don't put IO in corners + add_io_bels(x, y); + } else { + add_slice_bels(x, y); + } + } + } + } + + // Create PIPs inside a tile; following an example synthetic routing pattern + void add_io_pips(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + Loc loc(x, y, 0); + + const uint16_t tile_input_config[8] = { + 0b0000'0000'0000'0001, 0b0000'0000'0000'0001, 0b0000'0000'0000'0001, 0b0000'0000'0000'0001, + 0b0000'0000'0000'0010, 0b0000'0000'0000'0010, 0b0000'0000'0000'0010, 0b0000'0000'0000'0010, + }; + + // Tile inputs + for (int tile_input = 0; tile_input < InputMuxCount; tile_input++) { + auto &dst = w.tile_inputs_north.at(tile_input); + // North + for (int step = 1; step <= 4; step++) { + if (y - step <= 0 || x == 0 || x == X - 1) + break; + auto &w = wires_by_tile.at(y - step).at(x); + for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) + if ((1 << tile_input) & tile_input_config[tile_output]) + add_pip(loc, w.tile_outputs_north.at(tile_output), dst); + } + } + + for (int tile_input = 0; tile_input < InputMuxCount; tile_input++) { + auto &dst = w.tile_inputs_east.at(tile_input); + // East + for (int step = 1; step <= 4; step++) { + if (x - step <= 0 || y == 0 || y == Y - 1) + break; + auto &w = wires_by_tile.at(y).at(x - step); + for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) + if ((1 << tile_input) & tile_input_config[tile_output]) + add_pip(loc, w.tile_outputs_east.at(tile_output), dst); + } + } + + for (int tile_input = 0; tile_input < InputMuxCount; tile_input++) { + auto &dst = w.tile_inputs_south.at(tile_input); + // South + for (int step = 1; step <= 4; step++) { + if (y + step >= Y || x == 0 || x == X - 1) + break; + auto &w = wires_by_tile.at(y + step).at(x); + for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) + if ((1 << tile_input) & tile_input_config[tile_output]) + add_pip(loc, w.tile_outputs_south.at(tile_output), dst); + } + } + + for (int tile_input = 0; tile_input < InputMuxCount; tile_input++) { + auto &dst = w.tile_inputs_west.at(tile_input); + // West + for (int step = 1; step <= 4; step++) { + if (x + step >= X || y == 0 || y == Y - 1) + break; + auto &w = wires_by_tile.at(y).at(x + step); + for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) + if ((1 << tile_input) & tile_input_config[tile_output]) + add_pip(loc, w.tile_outputs_west.at(tile_output), dst); + } + } + + // Tile outputs + for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) { + for (int z = 0; z < 2; z++) { + WireId src = w.slice_outputs.at(z); + // O output + if (y == 0) + add_pip(loc, src, w.tile_outputs_north.at(tile_output)); + if (x == 0) + add_pip(loc, src, w.tile_outputs_east.at(tile_output)); + if (y == Y - 1) + add_pip(loc, src, w.tile_outputs_south.at(tile_output)); + if (x == X - 1) + add_pip(loc, src, w.tile_outputs_west.at(tile_output)); + } + } + + // Pad inputs + for (const auto &src : w.tile_inputs_north) { + for (int z = 0; z < 2; z++) { + // I input + add_pip(loc, src, w.slice_inputs.at(z * K + 0)); + // EN input + add_pip(loc, src, w.slice_inputs.at(z * K + 1)); + } + } + for (const auto &src : w.tile_inputs_east) { + for (int z = 0; z < 2; z++) { + // I input + add_pip(loc, src, w.slice_inputs.at(z * K + 0)); + // EN input + add_pip(loc, src, w.slice_inputs.at(z * K + 1)); + } + } + for (const auto &src : w.tile_inputs_south) { + for (int z = 0; z < 2; z++) { + // I input + add_pip(loc, src, w.slice_inputs.at(z * K + 0)); + // EN input + add_pip(loc, src, w.slice_inputs.at(z * K + 1)); + } + } + for (const auto &src : w.tile_inputs_west) { + for (int z = 0; z < 2; z++) { + // I input + add_pip(loc, src, w.slice_inputs.at(z * K + 0)); + // EN input + add_pip(loc, src, w.slice_inputs.at(z * K + 1)); + } + } + } + void add_slice_pips(int x, int y) + { + auto &w = wires_by_tile.at(y).at(x); + Loc loc(x, y, 0); + + const uint16_t tile_input_config[8] = {0b1010'1010'1010'1010, 0b0101'0101'0101'0101, 0b0110'0110'0110'0110, + 0b1001'1001'1001'1001, 0b0011'0011'0011'0011, 0b1100'1100'1100'1100, + 0b1111'0000'1111'0000, 0b0000'1111'0000'1111}; + + // Slice input selector + for (int lut = 0; lut < N; lut++) { + for (int lut_input = 0; lut_input < K; lut_input++) { + for (const auto &tile_input : w.tile_inputs_north) // Tile input bus + add_pip(loc, tile_input, w.slice_inputs.at(lut * K + lut_input)); + for (const auto &tile_input : w.tile_inputs_east) // Tile input bus + add_pip(loc, tile_input, w.slice_inputs.at(lut * K + lut_input)); + for (const auto &tile_input : w.tile_inputs_south) // Tile input bus + add_pip(loc, tile_input, w.slice_inputs.at(lut * K + lut_input)); + for (const auto &tile_input : w.tile_inputs_west) // Tile input bus + add_pip(loc, tile_input, w.slice_inputs.at(lut * K + lut_input)); + for (const auto &slice_output : w.slice_outputs) // Slice output bus + add_pip(loc, slice_output, w.slice_inputs.at(lut * K + lut_input)); + } + for (const auto &tile_input : w.tile_inputs_north) // Clock selector + add_pip(loc, tile_input, w.clk.at(lut)); + for (const auto &tile_input : w.tile_inputs_east) // Clock selector + add_pip(loc, tile_input, w.clk.at(lut)); + for (const auto &tile_input : w.tile_inputs_south) // Clock selector + add_pip(loc, tile_input, w.clk.at(lut)); + for (const auto &tile_input : w.tile_inputs_west) // Clock selector + add_pip(loc, tile_input, w.clk.at(lut)); + } + + // Slice output selector + for (int slice_output = 0; slice_output < N; slice_output++) { + add_pip(loc, w.f.at(slice_output), w.slice_outputs.at(slice_output)); // LUT output + add_pip(loc, w.q.at(slice_output), w.slice_outputs.at(slice_output)); // DFF output + } + + // Tile input selector + for (int step = 1; step <= 4; step++) { + if (y + step < Y) // South + for (size_t tile_input_index = 0; tile_input_index < w.tile_inputs_north.size(); tile_input_index++) + for (size_t tile_output_index = 0; + tile_output_index < wires_by_tile.at(y + step).at(x).tile_outputs_south.size(); + tile_output_index++) + if ((1 << tile_input_index) & tile_input_config[tile_output_index]) + add_pip(loc, wires_by_tile.at(y + step).at(x).tile_outputs_south.at(tile_output_index), + w.tile_inputs_north.at(tile_input_index)); + + if (x + step < X) // West + for (size_t tile_input_index = 0; tile_input_index < w.tile_inputs_east.size(); tile_input_index++) + for (size_t tile_output_index = 0; + tile_output_index < wires_by_tile.at(y).at(x + step).tile_outputs_west.size(); + tile_output_index++) + if ((1 << tile_input_index) & tile_input_config[tile_output_index]) + add_pip(loc, wires_by_tile.at(y).at(x + step).tile_outputs_west.at(tile_output_index), + w.tile_inputs_east.at(tile_input_index)); + + if (y - step >= 0) // North + for (size_t tile_input_index = 0; tile_input_index < w.tile_inputs_south.size(); tile_input_index++) + for (size_t tile_output_index = 0; + tile_output_index < wires_by_tile.at(y - step).at(x).tile_outputs_north.size(); + tile_output_index++) + if ((1 << tile_input_index) & tile_input_config[tile_output_index]) + add_pip(loc, wires_by_tile.at(y - step).at(x).tile_outputs_north.at(tile_output_index), + w.tile_inputs_south.at(tile_input_index)); + + if (x - step >= 0) // East + for (size_t tile_input_index = 0; tile_input_index < w.tile_inputs_west.size(); tile_input_index++) + for (size_t tile_output_index = 0; + tile_output_index < wires_by_tile.at(y).at(x - step).tile_outputs_east.size(); + tile_output_index++) + if ((1 << tile_input_index) & tile_input_config[tile_output_index]) + add_pip(loc, wires_by_tile.at(y).at(x - step).tile_outputs_east.at(tile_output_index), + w.tile_inputs_west.at(tile_input_index)); + } + + // Tile output selector + for (const auto &slice_output : w.slice_outputs) { + for (const auto &tile_output : w.tile_outputs_north) + add_pip(loc, slice_output, tile_output); + for (const auto &tile_output : w.tile_outputs_east) + add_pip(loc, slice_output, tile_output); + for (const auto &tile_output : w.tile_outputs_south) + add_pip(loc, slice_output, tile_output); + for (const auto &tile_output : w.tile_outputs_west) + add_pip(loc, slice_output, tile_output); + } + + for (const auto &tile_input : w.tile_inputs_north) { + for (const auto &tile_output : w.tile_outputs_north) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_east) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_south) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_west) + add_pip(loc, tile_input, tile_output); + } + for (const auto &tile_input : w.tile_inputs_east) { + for (const auto &tile_output : w.tile_outputs_north) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_east) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_south) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_west) + add_pip(loc, tile_input, tile_output); + } + for (const auto &tile_input : w.tile_inputs_south) { + for (const auto &tile_output : w.tile_outputs_north) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_east) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_south) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_west) + add_pip(loc, tile_input, tile_output); + } + for (const auto &tile_input : w.tile_inputs_west) { + for (const auto &tile_output : w.tile_outputs_north) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_east) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_south) + add_pip(loc, tile_input, tile_output); + for (const auto &tile_output : w.tile_outputs_west) + add_pip(loc, tile_input, tile_output); + } + } + void init_pips() + { + log_info("Creating pips...\n"); + for (int y = 0; y < Y; y++) + for (int x = 0; x < X; x++) { + if (is_io(x, y)) { + add_io_pips(x, y); + } else { + add_slice_pips(x, y); + } + } + } + // Validity checking + struct OkamiCellInfo + { + const NetInfo *lut_f = nullptr, *ff_d = nullptr; + bool lut_i3_used = false; + }; + std::vector fast_cell_info; + void assign_cell_info() + { + fast_cell_info.resize(ctx->cells.size()); + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + auto &fc = fast_cell_info.at(ci->flat_index); + if (ci->type == id_LUT4) { + fc.lut_f = ci->getPort(id_F); + fc.lut_i3_used = (ci->getPort(ctx->id(stringf("I[%d]", K - 1))) != nullptr); + } else if (ci->type == id_DFF) { + fc.ff_d = ci->getPort(id_D); + } + } + } + bool slice_valid(int x, int y, int z) const + { + const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2))); + const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1))); + if (!lut || !ff) + return true; // always valid if only LUT or FF used + const auto &lut_data = fast_cell_info.at(lut->flat_index); + const auto &ff_data = fast_cell_info.at(ff->flat_index); + // In our example arch; the FF D can either be driven from LUT F or LUT I3 + // so either; FF D must equal LUT F or LUT I3 must be unused + if (ff_data.ff_d == lut_data.lut_f && lut_data.lut_f->users.size() == 1) + return true; + // Can't route FF and LUT output separately + return false; + } + // Bel bucket functions + IdString getBelBucketForCellType(IdString cell_type) const override + { + if (cell_type.in(id_INBUF, id_OUTBUF)) + return id_IOB; + return cell_type; + } + bool isValidBelForCellType(IdString cell_type, BelId bel) const override + { + IdString bel_type = ctx->getBelType(bel); + if (bel_type == id_IOB) + return cell_type.in(id_INBUF, id_OUTBUF); + else + return (bel_type == cell_type); + } +}; + +struct OkamiArch : ViaductArch +{ + OkamiArch() : ViaductArch("okami"){}; + std::unique_ptr create(const dict &args) + { + return std::make_unique(); + } +} exampleArch; +} // namespace + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct/okami/okami_map.v b/generic/viaduct/okami/okami_map.v new file mode 100644 index 0000000000..f701f0ed7a --- /dev/null +++ b/generic/viaduct/okami/okami_map.v @@ -0,0 +1,12 @@ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + + localparam rep = 1<<(4-WIDTH); + + LUT4 #(.INIT({rep{LUT}})) _TECHMAP_REPLACE_ (.I(A), .F(Y)); +endmodule + +module \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule diff --git a/generic/viaduct/okami/okami_prims.v b/generic/viaduct/okami/okami_prims.v new file mode 100644 index 0000000000..d2813129b4 --- /dev/null +++ b/generic/viaduct/okami/okami_prims.v @@ -0,0 +1,35 @@ +module LUT4 #( + parameter [15:0] INIT = 0 +) ( + input [3:0] I, + output F +); + wire [7:0] s3 = I[3] ? INIT[15:8] : INIT[7:0]; + wire [3:0] s2 = I[2] ? s3[ 7:4] : s3[3:0]; + wire [1:0] s1 = I[1] ? s2[ 3:2] : s2[1:0]; + assign F = I[0] ? s1[1] : s1[0]; +endmodule + +module DFF ( + input CLK, D, + output reg Q +); + initial Q = 1'b0; + always @(posedge CLK) + Q <= D; +endmodule + +module INBUF ( + input PAD, + output O, +); + assign O = PAD; +endmodule + +module OUTBUF ( + output PAD, + input I, +); + assign PAD = I; +endmodule + diff --git a/generic/viaduct/okami/synth_okami.tcl b/generic/viaduct/okami/synth_okami.tcl new file mode 100644 index 0000000000..1a0212eb54 --- /dev/null +++ b/generic/viaduct/okami/synth_okami.tcl @@ -0,0 +1,24 @@ +# Usage +# tcl synth_okami.tcl {out.json} + +yosys read_verilog -lib [file dirname [file normalize $argv0]]/okami_prims.v +yosys hierarchy -check -top top +yosys proc +yosys flatten +yosys tribuf -logic +yosys deminout +yosys synth -run coarse +yosys memory_map +yosys opt -full +yosys iopadmap -bits -inpad INBUF O:PAD -outpad OUTBUF I:PAD +yosys techmap -map +/techmap.v +yosys opt -fast +yosys dfflegalize -cell \$_DFF_P_ 0 +yosys abc -lut 4 -dress +yosys clean +yosys techmap -map [file dirname [file normalize $argv0]]/okami_map.v +yosys clean +yosys hierarchy -check +yosys stat + +if {$argc > 0} { yosys write_json [lindex $argv 0] } diff --git a/generic/viaduct/okami/viaduct_example.sh b/generic/viaduct/okami/viaduct_example.sh new file mode 100755 index 0000000000..5a49dc10fd --- /dev/null +++ b/generic/viaduct/okami/viaduct_example.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -ex +# Run synthesis +yosys -p "tcl synth_okami.tcl blinky.json" ../../examples/blinky.v +# Run PnR +${NEXTPNR:-../../../build/nextpnr-generic} --uarch okami --json blinky.json --router router2 From 434a9737bb459189b463c8768454ea6c0e151406 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 26 Feb 2022 15:11:33 +0000 Subject: [PATCH 074/712] Add indexed_store container type Signed-off-by: gatecat --- common/indexed_store.h | 266 +++++++++++++++++++++++++++++++++++++++++ common/nextpnr_types.h | 1 + 2 files changed, 267 insertions(+) create mode 100644 common/indexed_store.h diff --git a/common/indexed_store.h b/common/indexed_store.h new file mode 100644 index 0000000000..5579b039fb --- /dev/null +++ b/common/indexed_store.h @@ -0,0 +1,266 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 INDEXED_STORE_H +#define INDEXED_STORE_H + +#include +#include +#include + +#include "nextpnr_assertions.h" +#include "nextpnr_namespaces.h" + +NEXTPNR_NAMESPACE_BEGIN + +template struct store_index +{ + int32_t m_index = -1; + store_index() = default; + explicit store_index(int32_t index) : m_index(index){}; + int32_t idx() const { return m_index; } + void set(int32_t index) { m_index = index; } + bool empty() const { return m_index == -1; } + bool operator==(const store_index &other) const { return m_index == other.m_index; } + bool operator!=(const store_index &other) const { return m_index != other.m_index; } + bool operator<(const store_index &other) const { return m_index < other.m_index; } + unsigned int hash() const { return m_index; } + + operator bool() const { return !empty(); } + bool operator!() const { return empty(); } +}; + +// "Slotted" indexed object store +template class indexed_store +{ + private: + // This should move to using std::optional at some point + class slot + { + private: + alignas(T) unsigned char storage[sizeof(T)]; + int32_t next_free; + bool active; + inline T &obj() { return reinterpret_cast(storage); } + inline const T &obj() const { return reinterpret_cast(storage); } + friend class indexed_store; + + public: + slot() : active(false), next_free(std::numeric_limits::max()){}; + slot(slot &&other) : active(other.active), next_free(other.next_free) + { + if (active) + ::new (static_cast(&storage)) T(std::move(other.obj())); + }; + + template void create(Args &&...args) + { + NPNR_ASSERT(!active); + active = true; + ::new (static_cast(&storage)) T(std::forward(args)...); + } + bool empty() const { return !active; } + T &get() + { + NPNR_ASSERT(active); + return reinterpret_cast(storage); + } + const T &get() const + { + NPNR_ASSERT(active); + return reinterpret_cast(storage); + } + void free(int32_t first_free) + { + NPNR_ASSERT(active); + obj().~T(); + active = false; + next_free = first_free; + } + ~slot() + { + if (active) + obj().~T(); + } + }; + + std::vector slots; + int32_t first_free = 0; + int32_t active_count = 0; + + public: + // Create a new entry and return its index + template store_index add(Args &&...args) + { + ++active_count; + if (first_free == int32_t(slots.size())) { + slots.emplace_back(); + slots.back().create(std::forward(args)...); + ++first_free; + return store_index(int32_t(slots.size()) - 1); + } else { + int32_t idx = first_free; + auto &slot = slots.at(idx); + first_free = slot.next_free; + slot.create(std::forward(args)...); + return store_index(idx); + } + } + + // Remove an entry at an index + void remove(store_index idx) + { + --active_count; + slots.at(idx.m_index).free(first_free); + first_free = idx.m_index; + } + + // Number of live entries + int32_t entries() const { return active_count; } + + // Reserve a certain amount of space + void reserve(int32_t size) { slots.reserve(size); } + + // Check if an index exists + int32_t count(store_index idx) + { + if (idx.m_index < 0 || idx.m_index >= int32_t(slots.size())) + return 0; + return slots.at(idx.m_index).empty() ? 0 : 1; + } + + // Get an item by index + T &at(store_index idx) { return slots.at(idx.m_index).get(); } + const T &at(store_index idx) const { return slots.at(idx.m_index).get(); } + T &operator[](store_index idx) { return slots.at(idx.m_index).get(); } + const T &operator[](store_index idx) const { return slots.at(idx.m_index).get(); } + + // Total size of the container + int32_t capacity() const { return int32_t(slots.size()); } + + // Iterate over items + class iterator + { + private: + indexed_store *base; + int32_t index = 0; + + public: + iterator(indexed_store *base, int32_t index) : base(base), index(index){}; + inline bool operator!=(const iterator &other) const { return other.index != index; } + inline bool operator==(const iterator &other) const { return other.index == index; } + inline iterator operator++() + { + // skip over unused slots + do { + index++; + } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active); + return *this; + } + inline iterator operator++(int) + { + iterator prior(*this); + do { + index++; + } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active); + return prior; + } + T &operator*() { return base->at(store_index(index)); } + template friend class enumerated_iterator; + }; + iterator begin() { return iterator{this, 0}; } + iterator end() { return iterator{this, int32_t(slots.size())}; } + + class const_iterator + { + private: + const indexed_store *base; + int32_t index = 0; + + public: + const_iterator(const indexed_store *base, int32_t index) : base(base), index(index){}; + inline bool operator!=(const const_iterator &other) const { return other.index != index; } + inline bool operator==(const const_iterator &other) const { return other.index == index; } + inline const_iterator operator++() + { + // skip over unused slots + do { + index++; + } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active); + return *this; + } + inline const_iterator operator++(int) + { + iterator prior(*this); + do { + index++; + } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active); + return prior; + } + const T &operator*() { return base->at(store_index(index)); } + template friend class enumerated_iterator; + }; + const_iterator begin() const { return const_iterator{this, 0}; } + const_iterator end() const { return const_iterator{this, int32_t(slots.size())}; } + + template struct enumerated_item + { + enumerated_item(int32_t index, T &value) : index(index), value(value){}; + store_index> index; + S &value; + }; + + template class enumerated_iterator + { + private: + It base; + + public: + enumerated_iterator(const It &base) : base(base){}; + inline bool operator!=(const enumerated_iterator &other) const { return other.base != base; } + inline bool operator==(const enumerated_iterator &other) const { return other.base == base; } + inline enumerated_iterator operator++() + { + ++base; + return *this; + } + inline enumerated_iterator operator++(int) + { + iterator prior(*this); + ++base; + return prior; + } + enumerated_item operator*() { return enumerated_item{base.index, *base}; } + }; + + template struct enumerated_range + { + enumerated_range(const It &begin, const It &end) : m_begin(begin), m_end(end){}; + enumerated_iterator m_begin, m_end; + enumerated_iterator begin() { return m_begin; } + enumerated_iterator end() { return m_end; } + }; + + enumerated_range enumerate() { return enumerated_range{begin(), end()}; } + enumerated_range enumerate() const { return enumerated_range{begin(), end()}; } +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index cf93a0714d..4e5432cedd 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -29,6 +29,7 @@ #include "archdefs.h" #include "hashlib.h" +#include "indexed_store.h" #include "nextpnr_base_types.h" #include "nextpnr_namespaces.h" #include "property.h" From 86699b42f619960bfefd4d0b479dd44a90527ea4 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 26 Feb 2022 15:17:46 +0000 Subject: [PATCH 075/712] Switch to potentially-sparse net users array This uses a new data structure for net.users that allows gaps, so removing a port from a net is no longer an O(n) operation on the number of users the net has. Signed-off-by: gatecat --- common/context.cc | 14 +-- common/design_utils.h | 14 ++- common/indexed_store.h | 47 +++++++-- common/nextpnr_types.cc | 25 ++--- common/nextpnr_types.h | 3 +- common/placer1.cc | 68 +++++------- common/placer_heap.cc | 14 +-- common/pybindings.cc | 4 +- common/pycontainers.h | 57 ++++++++++ common/router1.cc | 62 ++++++----- common/router2.cc | 139 +++++++++++++------------ common/timing.cc | 10 +- common/timing.h | 23 ---- common/timing_opt.cc | 65 ++++-------- ecp5/cells.cc | 7 +- ecp5/globals.cc | 22 ++-- ecp5/pack.cc | 56 +++++----- fpga_interchange/arch.cc | 4 +- fpga_interchange/arch_pack_clusters.cc | 6 +- fpga_interchange/globals.cc | 26 ++--- frontend/frontend_base.h | 2 +- generic/cells.cc | 2 +- generic/pack.cc | 7 +- generic/viaduct/okami/okami.cc | 2 +- generic/viaduct_helpers.cc | 4 +- gowin/pack.cc | 11 +- ice40/bitstream.cc | 2 +- ice40/cells.cc | 2 +- ice40/chains.cc | 61 +++-------- ice40/pack.cc | 81 +++++++------- machxo2/pack.cc | 2 +- mistral/pack.cc | 14 +-- nexus/global.cc | 8 +- nexus/pack.cc | 43 ++++---- 34 files changed, 442 insertions(+), 465 deletions(-) diff --git a/common/context.cc b/common/context.cc index faddf82595..e35d3e49ae 100644 --- a/common/context.cc +++ b/common/context.cc @@ -334,13 +334,13 @@ void Context::check() const nameOf(port.first), nameOf(net)); } } else if (port.second.type == PORT_IN) { - int usr_count = std::count_if(net->users.begin(), net->users.end(), [&](const PortRef &pr) { - return pr.cell == c.second.get() && pr.port == port.first; - }); - if (usr_count != 1) - CHECK_FAIL("input cell port '%s.%s' appears %d rather than expected 1 times in users vector of " - "net '%s'\n", - nameOf(c.first), nameOf(port.first), usr_count, nameOf(net)); + if (!port.second.user_idx) + CHECK_FAIL("input cell port '%s.%s' on net '%s' has no user index\n", nameOf(c.first), + nameOf(port.first), nameOf(net)); + auto net_user = net->users.at(port.second.user_idx); + if (net_user.cell != c.second.get() || net_user.port != port.first) + CHECK_FAIL("input cell port '%s.%s' not in associated user entry of net '%s'\n", + nameOf(c.first), nameOf(port.first), nameOf(net)); } } } diff --git a/common/design_utils.h b/common/design_utils.h index 63cb71d7b3..069600b548 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -47,14 +47,18 @@ CellInfo *net_only_drives(const Context *ctx, NetInfo *net, F1 cell_pred, IdStri return nullptr; if (exclusive) { if (exclude == nullptr) { - if (net->users.size() != 1) + if (net->users.entries() != 1) return nullptr; } else { - if (net->users.size() > 2) { + if (net->users.entries() > 2) { return nullptr; - } else if (net->users.size() == 2) { - if (std::find_if(net->users.begin(), net->users.end(), - [exclude](const PortRef &ref) { return ref.cell == exclude; }) == net->users.end()) + } else if (net->users.entries() == 2) { + bool found = false; + for (auto &usr : net->users) { + if (usr.cell == exclude) + found = true; + } + if (!found) return nullptr; } } diff --git a/common/indexed_store.h b/common/indexed_store.h index 5579b039fb..df607c13de 100644 --- a/common/indexed_store.h +++ b/common/indexed_store.h @@ -22,6 +22,7 @@ #include #include +#include #include #include "nextpnr_assertions.h" @@ -43,6 +44,7 @@ template struct store_index unsigned int hash() const { return m_index; } operator bool() const { return !empty(); } + operator int() const = delete; bool operator!() const { return empty(); } }; @@ -62,13 +64,19 @@ template class indexed_store friend class indexed_store; public: - slot() : active(false), next_free(std::numeric_limits::max()){}; - slot(slot &&other) : active(other.active), next_free(other.next_free) + slot() : next_free(std::numeric_limits::max()), active(false){}; + slot(slot &&other) : next_free(other.next_free), active(other.active) { if (active) ::new (static_cast(&storage)) T(std::move(other.obj())); }; + slot(const slot &other) : next_free(other.next_free), active(other.active) + { + if (active) + ::new (static_cast(&storage)) T(other.obj()); + }; + template void create(Args &&...args) { NPNR_ASSERT(!active); @@ -131,8 +139,16 @@ template class indexed_store first_free = idx.m_index; } + void clear() + { + active_count = 0; + first_free = 0; + slots.clear(); + } + // Number of live entries int32_t entries() const { return active_count; } + bool empty() const { return (entries() == 0); } // Reserve a certain amount of space void reserve(int32_t size) { slots.reserve(size); } @@ -155,6 +171,8 @@ template class indexed_store int32_t capacity() const { return int32_t(slots.size()); } // Iterate over items + template class enumerated_iterator; + class iterator { private: @@ -182,9 +200,14 @@ template class indexed_store return prior; } T &operator*() { return base->at(store_index(index)); } - template friend class enumerated_iterator; + template friend class indexed_store::enumerated_iterator; }; - iterator begin() { return iterator{this, 0}; } + iterator begin() + { + auto it = iterator{this, -1}; + ++it; + return it; + } iterator end() { return iterator{this, int32_t(slots.size())}; } class const_iterator @@ -214,15 +237,20 @@ template class indexed_store return prior; } const T &operator*() { return base->at(store_index(index)); } - template friend class enumerated_iterator; + template friend class indexed_store::enumerated_iterator; }; - const_iterator begin() const { return const_iterator{this, 0}; } + const_iterator begin() const + { + auto it = const_iterator{this, -1}; + ++it; + return it; + } const_iterator end() const { return const_iterator{this, int32_t(slots.size())}; } template struct enumerated_item { enumerated_item(int32_t index, T &value) : index(index), value(value){}; - store_index> index; + store_index> index; S &value; }; @@ -258,7 +286,10 @@ template class indexed_store }; enumerated_range enumerate() { return enumerated_range{begin(), end()}; } - enumerated_range enumerate() const { return enumerated_range{begin(), end()}; } + enumerated_range enumerate() const + { + return enumerated_range{begin(), end()}; + } }; NEXTPNR_NAMESPACE_END diff --git a/common/nextpnr_types.cc b/common/nextpnr_types.cc index c89a007102..57d816c008 100644 --- a/common/nextpnr_types.cc +++ b/common/nextpnr_types.cc @@ -66,7 +66,7 @@ void CellInfo::connectPort(IdString port_name, NetInfo *net) PortRef user; user.cell = this; user.port = port_name; - net->users.push_back(user); + port.user_idx = net->users.add(user); } else { NPNR_ASSERT_FALSE("invalid port type for connect_port"); } @@ -78,11 +78,8 @@ void CellInfo::disconnectPort(IdString port_name) return; PortInfo &port = ports.at(port_name); if (port.net != nullptr) { - port.net->users.erase(std::remove_if(port.net->users.begin(), port.net->users.end(), - [this, port_name](const PortRef &user) { - return user.cell == this && user.port == port_name; - }), - port.net->users.end()); + if (port.user_idx) + port.net->users.remove(port.user_idx); if (port.net->driver.cell == this && port.net->driver.port == port_name) port.net->driver.cell = nullptr; port.net = nullptr; @@ -116,7 +113,9 @@ void CellInfo::movePortTo(IdString port, CellInfo *other, IdString other_port) NPNR_ASSERT(old.type == rep.type); rep.net = old.net; + rep.user_idx = old.user_idx; old.net = nullptr; + old.user_idx = store_index{}; if (rep.type == PORT_OUT) { if (rep.net != nullptr) { rep.net->driver.cell = other; @@ -124,12 +123,9 @@ void CellInfo::movePortTo(IdString port, CellInfo *other, IdString other_port) } } else if (rep.type == PORT_IN) { if (rep.net != nullptr) { - for (PortRef &load : rep.net->users) { - if (load.cell == this && load.port == port) { - load.cell = other; - load.port = other_port; - } - } + auto &load = rep.net->users.at(rep.user_idx); + load.cell = other; + load.port = other_port; } } else { NPNR_ASSERT(false); @@ -144,9 +140,8 @@ void CellInfo::renamePort(IdString old_name, IdString new_name) if (pi.net != nullptr) { if (pi.net->driver.cell == this && pi.net->driver.port == old_name) pi.net->driver.port = new_name; - for (auto &usr : pi.net->users) - if (usr.cell == this && usr.port == old_name) - usr.port = new_name; + if (pi.user_idx) + pi.net->users.at(pi.user_idx).port = new_name; } ports.erase(old_name); pi.name = new_name; diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h index 4e5432cedd..c21182cc97 100644 --- a/common/nextpnr_types.h +++ b/common/nextpnr_types.h @@ -130,7 +130,7 @@ struct NetInfo : ArchNetInfo int32_t udata = 0; PortRef driver; - std::vector users; + indexed_store users; dict attrs; // wire -> uphill_pip @@ -155,6 +155,7 @@ struct PortInfo IdString name; NetInfo *net; PortType type; + store_index user_idx{}; }; struct Context; diff --git a/common/placer1.cc b/common/placer1.cc index 6de035b444..a6ba389504 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -91,7 +91,7 @@ class SAPlacer decltype(NetInfo::udata) n = 0; for (auto &net : ctx->nets) { old_udata.emplace_back(net.second->udata); - net_arc_tcost.at(n).resize(net.second->users.size()); + net_arc_tcost.at(n).resize(net.second->users.capacity()); net.second->udata = n++; net_by_udata.push_back(net.second.get()); } @@ -118,7 +118,6 @@ class SAPlacer } region_bounds[r->name] = bb; } - build_port_index(); for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->cluster == ClusterId()) @@ -858,7 +857,7 @@ class SAPlacer } // Get the timing cost for an arc of a net - inline double get_timing_cost(NetInfo *net, size_t user) + inline double get_timing_cost(NetInfo *net, const PortRef &user) { int cc; if (net->driver.cell == nullptr) @@ -866,11 +865,11 @@ class SAPlacer if (ctx->getPortTimingClass(net->driver.cell, net->driver.port, cc) == TMG_IGNORE) return 0; if (cfg.budgetBased) { - double delay = ctx->getDelayNS(ctx->predictArcDelay(net, net->users.at(user))); - return std::min(10.0, std::exp(delay - ctx->getDelayNS(net->users.at(user).budget) / 10)); + double delay = ctx->getDelayNS(ctx->predictArcDelay(net, user)); + return std::min(10.0, std::exp(delay - ctx->getDelayNS(user.budget) / 10)); } else { - float crit = tmg.get_criticality(CellPortKey(net->users.at(user))); - double delay = ctx->getDelayNS(ctx->predictArcDelay(net, net->users.at(user))); + float crit = tmg.get_criticality(CellPortKey(user)); + double delay = ctx->getDelayNS(ctx->predictArcDelay(net, user)); return delay * std::pow(crit, crit_exp); } } @@ -883,9 +882,9 @@ class SAPlacer if (ignore_net(ni)) continue; net_bounds[ni->udata] = get_net_bounds(ni); - if (cfg.timing_driven && int(ni->users.size()) < cfg.timingFanoutThresh) - for (size_t i = 0; i < ni->users.size(); i++) - net_arc_tcost[ni->udata][i] = get_timing_cost(ni, i); + if (cfg.timing_driven && int(ni->users.entries()) < cfg.timingFanoutThresh) + for (auto usr : ni->users.enumerate()) + net_arc_tcost[ni->udata][usr.index.idx()] = get_timing_cost(ni, usr.value); } } @@ -923,13 +922,13 @@ class SAPlacer }; std::vector bounds_changed_nets_x, bounds_changed_nets_y; - std::vector> changed_arcs; + std::vector>> changed_arcs; std::vector already_bounds_changed_x, already_bounds_changed_y; std::vector> already_changed_arcs; std::vector new_net_bounds; - std::vector, double>> new_arc_costs; + std::vector>, double>> new_arc_costs; wirelen_t wirelen_delta = 0; double timing_delta = 0; @@ -940,7 +939,7 @@ class SAPlacer already_bounds_changed_y.resize(p->ctx->nets.size()); already_changed_arcs.resize(p->ctx->nets.size()); for (auto &net : p->ctx->nets) { - already_changed_arcs.at(net.second->udata).resize(net.second->users.size()); + already_changed_arcs.at(net.second->udata).resize(net.second->users.capacity()); } new_net_bounds = p->net_bounds; } @@ -956,7 +955,7 @@ class SAPlacer already_bounds_changed_y[bc] = NO_CHANGE; } for (const auto &tc : changed_arcs) - already_changed_arcs[tc.first][tc.second] = false; + already_changed_arcs[tc.first][tc.second.idx()] = false; bounds_changed_nets_x.clear(); bounds_changed_nets_y.clear(); changed_arcs.clear(); @@ -1100,22 +1099,22 @@ class SAPlacer } } - if (cfg.timing_driven && int(pn->users.size()) < cfg.timingFanoutThresh) { + if (cfg.timing_driven && int(pn->users.entries()) < cfg.timingFanoutThresh) { // Output ports - all arcs change timing if (port.second.type == PORT_OUT) { int cc; TimingPortClass cls = ctx->getPortTimingClass(cell, port.first, cc); if (cls != TMG_IGNORE) - for (size_t i = 0; i < pn->users.size(); i++) - if (!mc.already_changed_arcs[pn->udata][i]) { - mc.changed_arcs.emplace_back(std::make_pair(pn->udata, i)); - mc.already_changed_arcs[pn->udata][i] = true; + for (auto usr : pn->users.enumerate()) + if (!mc.already_changed_arcs[pn->udata][usr.index.idx()]) { + mc.changed_arcs.emplace_back(std::make_pair(pn->udata, usr.index)); + mc.already_changed_arcs[pn->udata][usr.index.idx()] = true; } } else if (port.second.type == PORT_IN) { - auto usr = fast_port_to_user.at(std::make_pair(cell->name, port.first)); - if (!mc.already_changed_arcs[pn->udata][usr]) { - mc.changed_arcs.emplace_back(std::make_pair(pn->udata, usr)); - mc.already_changed_arcs[pn->udata][usr] = true; + auto usr_idx = port.second.user_idx; + if (!mc.already_changed_arcs[pn->udata][usr_idx.idx()]) { + mc.changed_arcs.emplace_back(std::make_pair(pn->udata, usr_idx)); + mc.already_changed_arcs[pn->udata][usr_idx.idx()] = true; } } } @@ -1142,11 +1141,12 @@ class SAPlacer if (cfg.timing_driven) { for (const auto &tc : md.changed_arcs) { - double old_cost = net_arc_tcost.at(tc.first).at(tc.second); - double new_cost = get_timing_cost(net_by_udata.at(tc.first), tc.second); + double old_cost = net_arc_tcost.at(tc.first).at(tc.second.idx()); + double new_cost = + get_timing_cost(net_by_udata.at(tc.first), net_by_udata.at(tc.first)->users.at(tc.second)); md.new_arc_costs.emplace_back(std::make_pair(tc, new_cost)); md.timing_delta += (new_cost - old_cost); - md.already_changed_arcs[tc.first][tc.second] = false; + md.already_changed_arcs[tc.first][tc.second.idx()] = false; } } } @@ -1158,21 +1158,10 @@ class SAPlacer for (const auto &bc : md.bounds_changed_nets_y) net_bounds[bc] = md.new_net_bounds[bc]; for (const auto &tc : md.new_arc_costs) - net_arc_tcost[tc.first.first].at(tc.first.second) = tc.second; + net_arc_tcost[tc.first.first].at(tc.first.second.idx()) = tc.second; curr_wirelen_cost += md.wirelen_delta; curr_timing_cost += md.timing_delta; } - // Build the cell port -> user index - void build_port_index() - { - for (auto &net : ctx->nets) { - NetInfo *ni = net.second.get(); - for (size_t i = 0; i < ni->users.size(); i++) { - auto &usr = ni->users.at(i); - fast_port_to_user[std::make_pair(usr.cell->name, usr.port)] = i; - } - } - } // Simple routeability driven placement const int large_cell_thresh = 50; @@ -1240,9 +1229,6 @@ class SAPlacer // Map net arcs to their timing cost (criticality * delay ns) std::vector> net_arc_tcost; - // Fast lookup for cell port to net user index - dict, size_t> fast_port_to_user; - // Fast lookup for cell to clusters dict> cluster2cell; diff --git a/common/placer_heap.cc b/common/placer_heap.cc index f8385cef33..5b43dc7257 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -655,9 +655,9 @@ class HeAPPlacer template void foreach_port(NetInfo *net, Tf func) { if (net->driver.cell != nullptr) - func(net->driver, -1); - for (size_t i = 0; i < net->users.size(); i++) - func(net->users.at(i), i); + func(net->driver, store_index()); + for (auto usr : net->users.enumerate()) + func(usr.value, usr.index); } // Build the system of equations for either X or Y @@ -682,7 +682,7 @@ class HeAPPlacer // Find the bounds of the net in this axis, and the ports that correspond to these bounds PortRef *lbport = nullptr, *ubport = nullptr; int lbpos = std::numeric_limits::max(), ubpos = std::numeric_limits::min(); - foreach_port(ni, [&](PortRef &port, int user_idx) { + foreach_port(ni, [&](PortRef &port, store_index user_idx) { int pos = cell_pos(port.cell); if (pos < lbpos) { lbpos = pos; @@ -713,17 +713,17 @@ class HeAPPlacer }; // Add all relevant connections to the matrix - foreach_port(ni, [&](PortRef &port, int user_idx) { + foreach_port(ni, [&](PortRef &port, store_index user_idx) { int this_pos = cell_pos(port.cell); auto process_arc = [&](PortRef *other) { if (other == &port) return; int o_pos = cell_pos(other->cell); - double weight = 1.0 / (ni->users.size() * + double weight = 1.0 / (ni->users.entries() * std::max(1, (yaxis ? cfg.hpwl_scale_y : cfg.hpwl_scale_x) * std::abs(o_pos - this_pos))); - if (user_idx != -1) { + if (user_idx) { weight *= (1.0 + cfg.timingWeight * std::pow(tmg.get_criticality(CellPortKey(port)), cfg.criticalityExponent)); } diff --git a/common/pybindings.cc b/common/pybindings.cc index eef460ce11..9a783eb41d 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -220,7 +220,7 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) readwrite_wrapper, pass_through>::def_wrap(pi_cls, "type"); - typedef std::vector PortRefVector; + typedef indexed_store PortRefVector; typedef dict WireMap; typedef pool BelSet; typedef pool WireSet; @@ -288,7 +288,7 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) WRAP_MAP(m, WireMap, wrap_context, "WireMap"); WRAP_MAP_UPTR(m, RegionMap, "RegionMap"); - WRAP_VECTOR(m, PortRefVector, wrap_context); + WRAP_INDEXSTORE(m, PortRefVector, wrap_context); typedef dict ClockFmaxMap; WRAP_MAP(m, ClockFmaxMap, pass_through, "ClockFmaxMap"); diff --git a/common/pycontainers.h b/common/pycontainers.h index a93230ab78..ff49c34cbf 100644 --- a/common/pycontainers.h +++ b/common/pycontainers.h @@ -186,6 +186,63 @@ struct vector_wrapper #define WRAP_VECTOR(m, t, conv) vector_wrapper().wrap(m, #t, #t "Iterator") +template > +struct indexed_store_wrapper +{ + typedef decltype(std::declval().begin()) iterator_t; + typedef decltype(*(std::declval())) value_t; + typedef typename PythonConversion::ContextualWrapper wrapped_vector; + typedef typename PythonConversion::ContextualWrapper> wrapped_pair; + using return_t = typename value_conv::ret_type; + static wrapped_pair iter(wrapped_vector &range) + { + return wrapped_pair(range.ctx, std::make_pair(range.base.begin(), range.base.end())); + } + + static std::string repr(wrapped_vector &range) + { + PythonConversion::string_converter conv; + bool first = true; + std::stringstream ss; + ss << "["; + for (const auto &item : range.base) { + if (!first) + ss << ", "; + ss << "'" << conv.to_str(range.ctx, item) << "'"; + first = false; + } + ss << "]"; + return ss.str(); + } + + static int len(wrapped_vector &range) { return range.base.capacity(); } + + static py::object getitem(wrapped_vector &range, int i) + { + store_index> idx(i); + if (!range.base.count(idx)) + throw py::none(); + return py::cast(value_conv()(range.ctx, boost::ref(range.base.at(idx)))); + } + + static void wrap(py::module &m, const char *range_name, const char *iter_name) + { + py::class_(m, range_name) + .def("__iter__", iter) + .def("__repr__", repr) + .def("__len__", len) + .def("__getitem__", getitem); + + iterator_wrapper().wrap(m, iter_name); + } + + typedef iterator_wrapper iter_wrap; +}; + +#define WRAP_INDEXSTORE(m, t, conv) \ + indexed_store_wrapper().wrap(m, #t, #t "Iterator") + /* Wrapper for a pair, allows accessing either using C++-style members (.first and .second) or as a Python iterable and indexable object diff --git a/common/router1.cc b/common/router1.cc index f387aee1c5..981321163f 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -34,7 +34,7 @@ struct arc_key { NetInfo *net_info; // logical user cell port index - int user_idx; + store_index user_idx; // physical index into cell->bel pin mapping (usually 0) unsigned phys_idx; @@ -52,7 +52,7 @@ struct arc_key unsigned int hash() const { std::size_t seed = std::hash()(net_info); - seed ^= std::hash()(user_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= user_idx.hash() + 0x9e3779b9 + (seed << 6) + (seed >> 2); seed ^= std::hash()(phys_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } @@ -157,7 +157,7 @@ struct Router1 return; NetInfo *net_info = arc.net_info; - int user_idx = arc.user_idx; + auto user_idx = arc.user_idx; unsigned phys_idx = arc.phys_idx; auto src_wire = ctx->getNetinfoSourceWire(net_info); @@ -318,14 +318,14 @@ struct Router1 auto src_wire = ctx->getNetinfoSourceWire(net_info); log_assert(src_wire != WireId()); - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { + for (auto user : net_info->users.enumerate()) { unsigned phys_idx = 0; - for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, net_info->users[user_idx])) { + for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, user.value)) { log_assert(dst_wire != WireId()); arc_key arc; arc.net_info = net_info; - arc.user_idx = user_idx; + arc.user_idx = user.index; arc.phys_idx = phys_idx++; valid_arcs.insert(arc); #if 0 @@ -391,28 +391,29 @@ struct Router1 if (dst_to_arc.count(src_wire)) log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n", ctx->nameOfWire(src_wire), ctx->nameOf(net_info), - ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx); + ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx.idx()); - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { + for (auto user : net_info->users.enumerate()) { unsigned phys_idx = 0; - for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, net_info->users[user_idx])) { + for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, user.value)) { arc_key arc; arc.net_info = net_info; - arc.user_idx = user_idx; + arc.user_idx = user.index; arc.phys_idx = phys_idx++; if (dst_to_arc.count(dst_wire)) { if (dst_to_arc.at(dst_wire).net_info == net_info) continue; log_error("Found two arcs with same sink wire %s: %s (%d) vs %s (%d)\n", - ctx->nameOfWire(dst_wire), ctx->nameOf(net_info), user_idx, - ctx->nameOf(dst_to_arc.at(dst_wire).net_info), dst_to_arc.at(dst_wire).user_idx); + ctx->nameOfWire(dst_wire), ctx->nameOf(net_info), user.index.idx(), + ctx->nameOf(dst_to_arc.at(dst_wire).net_info), + dst_to_arc.at(dst_wire).user_idx.idx()); } if (src_to_net.count(dst_wire)) log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n", ctx->nameOfWire(dst_wire), ctx->nameOf(src_to_net.at(dst_wire)), - ctx->nameOf(net_info), user_idx); + ctx->nameOf(net_info), user.index.idx()); dst_to_arc[dst_wire] = arc; @@ -441,9 +442,8 @@ struct Router1 // TODO: this matches the situation before supporting multiple cell->bel pins, but do we want to keep // this invariant? if (phys_idx == 0) - log_warning("No wires found for port %s on destination cell %s.\n", - ctx->nameOf(net_info->users[user_idx].port), - ctx->nameOf(net_info->users[user_idx].cell)); + log_warning("No wires found for port %s on destination cell %s.\n", ctx->nameOf(user.value.port), + ctx->nameOf(user.value.cell)); } src_to_net[src_wire] = net_info; @@ -463,7 +463,7 @@ struct Router1 { NetInfo *net_info = arc.net_info; - int user_idx = arc.user_idx; + auto user_idx = arc.user_idx; auto src_wire = ctx->getNetinfoSourceWire(net_info); auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], arc.phys_idx); @@ -472,8 +472,8 @@ struct Router1 float crit = tmg.get_criticality(CellPortKey(net_info->users.at(user_idx))); if (ctx->debug) { - log("Routing arc %d on net %s (%d arcs total):\n", user_idx, ctx->nameOf(net_info), - int(net_info->users.size())); + log("Routing arc %d on net %s (%d arcs total):\n", user_idx.idx(), ctx->nameOf(net_info), + int(net_info->users.capacity())); log(" source ... %s\n", ctx->nameOfWire(src_wire)); log(" sink ..... %s\n", ctx->nameOfWire(dst_wire)); } @@ -805,8 +805,7 @@ struct Router1 NetInfo *ni = net.second.get(); if (skip_net(ni)) continue; - for (size_t i = 0; i < ni->users.size(); i++) { - auto &usr = ni->users.at(i); + for (auto &usr : ni->users) { ++arc_count; delay_t slack = tmg.get_setup_slack(CellPortKey(usr)); if (slack == std::numeric_limits::min()) @@ -825,8 +824,7 @@ struct Router1 NetInfo *ni = net.second.get(); if (skip_net(ni)) continue; - for (size_t i = 0; i < ni->users.size(); i++) { - auto &usr = ni->users.at(i); + for (auto &usr : ni->users) { delay_t slack = tmg.get_setup_slack(CellPortKey(usr)); if (slack == std::numeric_limits::min()) continue; @@ -912,7 +910,8 @@ bool router1(Context *ctx, const Router1Cfg &cfg) arc_key arc = router.arc_queue_pop(); if (!router.route_arc(arc, true)) { - log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx, ctx->nameOf(arc.net_info)); + log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx.idx(), + ctx->nameOf(arc.net_info)); #ifndef NDEBUG router.check(); ctx->check(); @@ -937,8 +936,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg) } if (is_locked) continue; - for (size_t i = 0; i < ni->users.size(); i++) { - auto &usr = ni->users.at(i); + for (auto &usr : ni->users) { delay_t slack = router.tmg.get_setup_slack(CellPortKey(usr)); if (slack == std::numeric_limits::min()) continue; @@ -1051,15 +1049,15 @@ bool Context::checkRoutedDesign() const found_unrouted = true; } - dict dest_wires; - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, net_info->users[user_idx])) { + dict> dest_wires; + for (auto user : net_info->users.enumerate()) { + for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, user.value)) { log_assert(dst_wire != WireId()); - dest_wires[dst_wire] = user_idx; + dest_wires[dst_wire] = user.index; if (net_info->wires.count(dst_wire) == 0) { if (ctx->debug) - log(" sink %d (%s) not bound to net\n", user_idx, ctx->nameOfWire(dst_wire)); + log(" sink %d (%s) not bound to net\n", user.index.idx(), ctx->nameOfWire(dst_wire)); found_unrouted = true; } } @@ -1086,7 +1084,7 @@ bool Context::checkRoutedDesign() const if (db_entry.children.empty()) { if (dest_wires.count(w) != 0) { if (ctx->debug) - log(" %*s=> sink %d\n", 2 * num, "", dest_wires.at(w)); + log(" %*s=> sink %d\n", 2 * num, "", dest_wires.at(w).idx()); } else { if (ctx->debug) log(" %*s=> stub\n", 2 * num, ""); diff --git a/common/router2.cc b/common/router2.cc index c76e1f6153..e943e493bc 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -117,7 +117,7 @@ struct Router2 NetInfo *ni = net.second.get(); ni->udata = i; nets_by_udata.at(i) = ni; - nets.at(i).arcs.resize(ni->users.size()); + nets.at(i).arcs.resize(ni->users.capacity()); // Start net bounding box at overall min/max nets.at(i).bb.x0 = std::numeric_limits::max(); @@ -133,10 +133,9 @@ struct Router2 nets.at(i).cy += drv_loc.y; } - for (size_t j = 0; j < ni->users.size(); j++) { - auto &usr = ni->users.at(j); + for (auto usr : ni->users.enumerate()) { WireId src_wire = ctx->getNetinfoSourceWire(ni); - for (auto &dst_wire : ctx->getNetinfoSinkWires(ni, usr)) { + for (auto &dst_wire : ctx->getNetinfoSinkWires(ni, usr.value)) { nets.at(i).src_wire = src_wire; if (ni->driver.cell == nullptr) src_wire = dst_wire; @@ -146,10 +145,10 @@ struct Router2 log_error("No wire found for port %s on source cell %s.\n", ctx->nameOf(ni->driver.port), ctx->nameOf(ni->driver.cell)); if (dst_wire == WireId()) - log_error("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.port), - ctx->nameOf(usr.cell)); - nets.at(i).arcs.at(j).emplace_back(); - auto &ad = nets.at(i).arcs.at(j).back(); + log_error("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.value.port), + ctx->nameOf(usr.value.cell)); + nets.at(i).arcs.at(usr.index.idx()).emplace_back(); + auto &ad = nets.at(i).arcs.at(usr.index.idx()).back(); ad.sink_wire = dst_wire; // Set bounding box for this arc ad.bb = ctx->getRouteBoundingBox(src_wire, dst_wire); @@ -160,14 +159,14 @@ struct Router2 nets.at(i).bb.y1 = std::max(nets.at(i).bb.y1, ad.bb.y1); } // Add location to centroid sum - Loc usr_loc = ctx->getBelLocation(usr.cell->bel); + Loc usr_loc = ctx->getBelLocation(usr.value.cell->bel); nets.at(i).cx += usr_loc.x; nets.at(i).cy += usr_loc.y; } nets.at(i).hpwl = std::max( std::abs(nets.at(i).bb.y1 - nets.at(i).bb.y0) + std::abs(nets.at(i).bb.x1 - nets.at(i).bb.x0), 1); - nets.at(i).cx /= int(ni->users.size() + 1); - nets.at(i).cy /= int(ni->users.size() + 1); + nets.at(i).cx /= int(ni->users.entries() + 1); + nets.at(i).cy /= int(ni->users.entries() + 1); if (ctx->debug) log_info("%s: bb=(%d, %d)->(%d, %d) c=(%d, %d) hpwl=%d\n", ctx->nameOf(ni), nets.at(i).bb.x0, nets.at(i).bb.y0, nets.at(i).bb.x1, nets.at(i).bb.y1, nets.at(i).cx, nets.at(i).cy, @@ -218,11 +217,11 @@ struct Router2 for (auto &net_pair : ctx->nets) { auto *net = net_pair.second.get(); auto &nd = nets.at(net->udata); - for (size_t usr = 0; usr < net->users.size(); usr++) { - auto &ad = nd.arcs.at(usr); + for (auto usr : net->users.enumerate()) { + auto &ad = nd.arcs.at(usr.index.idx()); for (size_t phys_pin = 0; phys_pin < ad.size(); phys_pin++) { - if (check_arc_routing(net, usr, phys_pin)) { - record_prerouted_net(net, usr, phys_pin); + if (check_arc_routing(net, usr.index, phys_pin)) { + record_prerouted_net(net, usr.index, phys_pin); } } } @@ -261,7 +260,7 @@ struct Router2 // Nets that failed routing std::vector failed_nets; - std::vector> route_arcs; + std::vector, size_t>> route_arcs; std::priority_queue, QueuedWire::Greater> fwd_queue, bwd_queue; // Special case where one net has multiple logical arcs to the same physical sink @@ -305,7 +304,7 @@ struct Router2 log(__VA_ARGS__); \ } while (0) - void bind_pip_internal(PerNetData &net, size_t user, int wire, PipId pip) + void bind_pip_internal(PerNetData &net, store_index user, int wire, PipId pip) { auto &wd = flat_wires.at(wire); auto found = net.wires.find(wd.w); @@ -323,7 +322,7 @@ struct Router2 } } - void unbind_pip_internal(PerNetData &net, size_t user, WireId wire) + void unbind_pip_internal(PerNetData &net, store_index user, WireId wire) { auto &wd = wire_data(wire); auto &b = net.wires.at(wd.w); @@ -335,10 +334,10 @@ struct Router2 } } - void ripup_arc(NetInfo *net, size_t user, size_t phys_pin) + void ripup_arc(NetInfo *net, store_index user, size_t phys_pin) { auto &nd = nets.at(net->udata); - auto &ad = nd.arcs.at(user).at(phys_pin); + auto &ad = nd.arcs.at(user.idx()).at(phys_pin); if (!ad.routed) return; WireId src = nets.at(net->udata).src_wire; @@ -351,7 +350,8 @@ struct Router2 ad.routed = false; } - float score_wire_for_arc(NetInfo *net, size_t user, size_t phys_pin, WireId wire, PipId pip, float crit_weight) + float score_wire_for_arc(NetInfo *net, store_index user, size_t phys_pin, WireId wire, PipId pip, + float crit_weight) { auto &wd = wire_data(wire); auto &nd = nets.at(net->udata); @@ -367,13 +367,14 @@ struct Router2 float present_cost = 1.0f + overuse * curr_cong_weight * crit_weight; if (pip != PipId()) { Loc pl = ctx->getPipLocation(pip); - bias_cost = cfg.bias_cost_factor * (base_cost / int(net->users.size())) * + bias_cost = cfg.bias_cost_factor * (base_cost / int(net->users.entries())) * ((std::abs(pl.x - nd.cx) + std::abs(pl.y - nd.cy)) / float(nd.hpwl)); } return base_cost * hist_cost * present_cost / (1 + (source_uses * crit_weight)) + bias_cost; } - float get_togo_cost(NetInfo *net, size_t user, int wire, WireId src_sink, float crit_weight, bool bwd = false) + float get_togo_cost(NetInfo *net, store_index user, int wire, WireId src_sink, float crit_weight, + bool bwd = false) { auto &nd = nets.at(net->udata); auto &wd = flat_wires[wire]; @@ -386,10 +387,10 @@ struct Router2 return (ctx->getDelayNS(est_delay) / (1 + source_uses * crit_weight)) + cfg.ipin_cost_adder; } - bool check_arc_routing(NetInfo *net, size_t usr, size_t phys_pin) + bool check_arc_routing(NetInfo *net, store_index usr, size_t phys_pin) { auto &nd = nets.at(net->udata); - auto &ad = nd.arcs.at(usr).at(phys_pin); + auto &ad = nd.arcs.at(usr.idx()).at(phys_pin); WireId src_wire = nets.at(net->udata).src_wire; WireId cursor = ad.sink_wire; while (nd.wires.count(cursor)) { @@ -404,10 +405,10 @@ struct Router2 return (cursor == src_wire); } - void record_prerouted_net(NetInfo *net, size_t usr, size_t phys_pin) + void record_prerouted_net(NetInfo *net, store_index usr, size_t phys_pin) { auto &nd = nets.at(net->udata); - auto &ad = nd.arcs.at(usr).at(phys_pin); + auto &ad = nd.arcs.at(usr.idx()).at(phys_pin); ad.routed = true; WireId src = nets.at(net->udata).src_wire; @@ -449,7 +450,7 @@ struct Router2 } // Find all the wires that must be used to route a given arc - bool reserve_wires_for_arc(NetInfo *net, size_t i) + bool reserve_wires_for_arc(NetInfo *net, store_index i) { bool did_something = false; WireId src = ctx->getNetinfoSourceWire(net); @@ -459,7 +460,7 @@ struct Router2 WireId cursor = sink; bool done = false; if (ctx->debug) - log("reserving wires for arc %d (%s.%s) of net %s\n", int(i), ctx->nameOf(usr.cell), + log("reserving wires for arc %d (%s.%s) of net %s\n", i.idx(), ctx->nameOf(usr.cell), ctx->nameOf(usr.port), ctx->nameOf(net)); while (!done) { auto &wd = wire_data(cursor); @@ -501,8 +502,8 @@ struct Router2 WireId src = ctx->getNetinfoSourceWire(net); if (src == WireId()) continue; - for (size_t i = 0; i < net->users.size(); i++) - did_something |= reserve_wires_for_arc(net, i); + for (auto usr : net->users.enumerate()) + did_something |= reserve_wires_for_arc(net, usr.index); } } while (did_something); } @@ -529,12 +530,12 @@ struct Router2 return false; } - void update_wire_by_loc(ThreadContext &t, NetInfo *net, size_t i, size_t phys_pin, bool is_mt) + void update_wire_by_loc(ThreadContext &t, NetInfo *net, store_index i, size_t phys_pin, bool is_mt) { if (is_pseudo_const_net(net)) return; auto &nd = nets.at(net->udata); - auto &ad = nd.arcs.at(i).at(phys_pin); + auto &ad = nd.arcs.at(i.idx()).at(phys_pin); WireId cursor = ad.sink_wire; if (!nd.wires.count(cursor)) return; @@ -571,28 +572,29 @@ struct Router2 bool was_visited_fwd(int wire) { return flat_wires.at(wire).visited_fwd; } bool was_visited_bwd(int wire) { return flat_wires.at(wire).visited_bwd; } - float get_arc_crit(NetInfo *net, size_t i) + float get_arc_crit(NetInfo *net, store_index i) { if (!timing_driven) return 0; return tmg.get_criticality(CellPortKey(net->users.at(i))); } - bool arc_failed_slack(NetInfo *net, size_t usr_idx) + bool arc_failed_slack(NetInfo *net, store_index usr_idx) { return timing_driven_ripup && (tmg.get_setup_slack(CellPortKey(net->users.at(usr_idx))) < (2 * ctx->getDelayEpsilon())); } - ArcRouteResult route_arc(ThreadContext &t, NetInfo *net, size_t i, size_t phys_pin, bool is_mt, bool is_bb = true) + ArcRouteResult route_arc(ThreadContext &t, NetInfo *net, store_index i, size_t phys_pin, bool is_mt, + bool is_bb = true) { // Do some initial lookups and checks auto arc_start = std::chrono::high_resolution_clock::now(); auto &nd = nets[net->udata]; - auto &ad = nd.arcs.at(i).at(phys_pin); + auto &ad = nd.arcs.at(i.idx()).at(phys_pin); auto &usr = net->users.at(i); - ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", int(i), ctx->nameOf(net), ad.bb.x0, ad.bb.y0, - ad.bb.x1, ad.bb.y1); + ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", i.idx(), ctx->nameOf(net), ad.bb.x0, + ad.bb.y0, ad.bb.x1, ad.bb.y1); WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, phys_pin); if (src_wire == WireId()) ARC_LOG_ERR("No wire found for port %s on source cell %s.\n", ctx->nameOf(net->driver.port), @@ -614,7 +616,7 @@ struct Router2 // 0. starting within a small range of existing routing // 1. expanding from all routing int mode = 0; - if (net->users.size() < 4 || nd.wires.empty() || (crit > 0.95)) + if (net->users.entries() < 4 || nd.wires.empty() || (crit > 0.95)) mode = 1; // This records the point where forwards and backwards routing met @@ -844,11 +846,11 @@ struct Router2 t.processed_sinks.insert(dst_wire); ad.routed = true; auto arc_end = std::chrono::high_resolution_clock::now(); - ROUTE_LOG_DBG("Routing arc %d of net '%s' (is_bb = %d) took %02fs\n", int(i), ctx->nameOf(net), is_bb, + ROUTE_LOG_DBG("Routing arc %d of net '%s' (is_bb = %d) took %02fs\n", i.idx(), ctx->nameOf(net), is_bb, std::chrono::duration(arc_end - arc_start).count()); } else { auto arc_end = std::chrono::high_resolution_clock::now(); - ROUTE_LOG_DBG("Failed routing arc %d of net '%s' (is_bb = %d) took %02fs\n", int(i), ctx->nameOf(net), + ROUTE_LOG_DBG("Failed routing arc %d of net '%s' (is_bb = %d) took %02fs\n", i.idx(), ctx->nameOf(net), is_bb, std::chrono::duration(arc_end - arc_start).count()); result = ARC_RETRY_WITHOUT_BB; } @@ -880,26 +882,26 @@ struct Router2 t.in_wire_by_loc.clear(); auto &nd = nets.at(net->udata); bool failed_slack = false; - for (size_t i = 0; i < net->users.size(); i++) - failed_slack |= arc_failed_slack(net, i); - for (size_t i = 0; i < net->users.size(); i++) { - auto &ad = nd.arcs.at(i); + for (auto usr : net->users.enumerate()) + failed_slack |= arc_failed_slack(net, usr.index); + for (auto usr : net->users.enumerate()) { + auto &ad = nd.arcs.at(usr.index.idx()); for (size_t j = 0; j < ad.size(); j++) { // Ripup failed arcs to start with // Check if arc is already legally routed - if (!failed_slack && check_arc_routing(net, i, j)) { - update_wire_by_loc(t, net, i, j, true); + if (!failed_slack && check_arc_routing(net, usr.index, j)) { + update_wire_by_loc(t, net, usr.index, j, true); continue; } // Ripup arc to start with - ripup_arc(net, i, j); - t.route_arcs.emplace_back(i, j); + ripup_arc(net, usr.index, j); + t.route_arcs.emplace_back(usr.index, j); } } // Route most critical arc first std::stable_sort(t.route_arcs.begin(), t.route_arcs.end(), - [&](std::pair a, std::pair b) { + [&](std::pair, size_t> a, std::pair, size_t> b) { return get_arc_crit(net, a.first) > get_arc_crit(net, b.first); }); for (auto a : t.route_arcs) { @@ -913,7 +915,7 @@ struct Router2 } else { // Attempt a re-route without the bounding box constraint ROUTE_LOG_DBG("Rerouting arc %d.%d of net '%s' without bounding box, possible tricky routing...\n", - int(a.first), int(a.second), ctx->nameOf(net)); + a.first.idx(), int(a.second), ctx->nameOf(net)); auto res2 = route_arc(t, net, a.first, a.second, is_mt, false); // If this also fails, no choice but to give up if (res2 != ARC_SUCCESS) { @@ -926,7 +928,7 @@ struct Router2 log("\n"); } } - log_error("Failed to route arc %d.%d of net '%s', from %s to %s.\n", int(a.first), + log_error("Failed to route arc %d.%d of net '%s', from %s to %s.\n", a.first.idx(), int(a.second), ctx->nameOf(net), ctx->nameOfWire(ctx->getNetinfoSourceWire(net)), ctx->nameOfWire(ctx->getNetinfoSinkWire(net, net->users.at(a.first), a.second))); } @@ -991,7 +993,7 @@ struct Router2 } } - bool bind_and_check(NetInfo *net, int usr_idx, int phys_pin) + bool bind_and_check(NetInfo *net, store_index usr_idx, int phys_pin) { #ifdef ARCH_ECP5 if (net->is_global) @@ -999,7 +1001,7 @@ struct Router2 #endif bool success = true; auto &nd = nets.at(net->udata); - auto &ad = nd.arcs.at(usr_idx).at(phys_pin); + auto &ad = nd.arcs.at(usr_idx.idx()).at(phys_pin); auto &usr = net->users.at(usr_idx); WireId src = ctx->getNetinfoSourceWire(net); // Skip routes with no source @@ -1043,7 +1045,8 @@ struct Router2 if (!nd.wires.count(cursor)) { log("Failure details:\n"); log(" Cursor: %s\n", ctx->nameOfWire(cursor)); - log_error("Internal error; incomplete route tree for arc %d of net %s.\n", usr_idx, ctx->nameOf(net)); + log_error("Internal error; incomplete route tree for arc %d of net %s.\n", usr_idx.idx(), + ctx->nameOf(net)); } PipId p = nd.wires.at(cursor).first; if (ctx->checkPipAvailForNet(p, net)) { @@ -1104,9 +1107,9 @@ struct Router2 } // Bind the arcs using the routes we have discovered - for (size_t i = 0; i < net->users.size(); i++) { - for (size_t phys_pin = 0; phys_pin < nets.at(net->udata).arcs.at(i).size(); phys_pin++) { - if (!bind_and_check(net, i, phys_pin)) { + for (auto usr : net->users.enumerate()) { + for (size_t phys_pin = 0; phys_pin < nets.at(net->udata).arcs.at(usr.index.idx()).size(); phys_pin++) { + if (!bind_and_check(net, usr.index, phys_pin)) { ++arch_fail; success = false; } @@ -1313,10 +1316,10 @@ struct Router2 route_net(tcs.at(N), fail, false); } - delay_t get_route_delay(int net, int usr_idx, int phys_idx) + delay_t get_route_delay(int net, store_index usr_idx, int phys_idx) { auto &nd = nets.at(net); - auto &ad = nd.arcs.at(usr_idx).at(phys_idx); + auto &ad = nd.arcs.at(usr_idx.idx()).at(phys_idx); WireId cursor = ad.sink_wire; if (cursor == WireId() || nd.src_wire == WireId()) return 0; @@ -1344,11 +1347,11 @@ struct Router2 continue; #endif auto &nd = nets.at(net); - for (int i = 0; i < int(nd.arcs.size()); i++) { + for (auto usr : ni->users.enumerate()) { delay_t arc_delay = 0; - for (int j = 0; j < int(nd.arcs.at(i).size()); j++) - arc_delay = std::max(arc_delay, get_route_delay(net, i, j)); - tmg.set_route_delay(CellPortKey(ni->users.at(i)), DelayPair(arc_delay)); + for (int j = 0; j < int(nd.arcs.at(usr.index.idx()).size()); j++) + arc_delay = std::max(arc_delay, get_route_delay(net, usr.index, j)); + tmg.set_route_delay(CellPortKey(usr.value), DelayPair(arc_delay)); } } } @@ -1416,8 +1419,8 @@ struct Router2 if (timing_driven_ripup && iter < 500) { for (size_t i = 0; i < nets_by_udata.size(); i++) { NetInfo *ni = nets_by_udata.at(i); - for (size_t j = 0; j < ni->users.size(); j++) { - if (arc_failed_slack(ni, j)) { + for (auto usr : ni->users.enumerate()) { + if (arc_failed_slack(ni, usr.index)) { failed_nets.insert(i); ++tmgfail; } @@ -1451,7 +1454,7 @@ struct Router2 log_info("1000 slowest nets by runtime:\n"); for (int i = 0; i < std::min(int(nets_by_runtime.size()), 1000); i++) { log(" %80s %6d %.1fms\n", nets_by_runtime.at(i).second.c_str(ctx), - int(ctx->nets.at(nets_by_runtime.at(i).second)->users.size()), + int(ctx->nets.at(nets_by_runtime.at(i).second)->users.entries()), nets_by_runtime.at(i).first / 1000.0); } } diff --git a/common/timing.cc b/common/timing.cc index f30d4fc531..834785fbe3 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -60,14 +60,6 @@ void TimingAnalyser::init_ports() data.cell_port = CellPortKey(ci->name, port.first); } } - // Cell port to net port mapping - for (auto &net : ctx->nets) { - NetInfo *ni = net.second.get(); - if (ni->driver.cell != nullptr) - ports[CellPortKey(ni->driver)].net_port = NetPortKey(ni->name); - for (size_t i = 0; i < ni->users.size(); i++) - ports[CellPortKey(ni->users.at(i))].net_port = NetPortKey(ni->name, i); - } } void TimingAnalyser::get_cell_delays() @@ -79,7 +71,7 @@ void TimingAnalyser::get_cell_delays() IdString name = port.first.port; // Ignore dangling ports altogether for timing purposes - if (pd.net_port.net == IdString()) + if (!pi.net) continue; pd.cell_arcs.clear(); int clkInfoCount = 0; diff --git a/common/timing.h b/common/timing.h index b34fd63614..fe1bcaa84c 100644 --- a/common/timing.h +++ b/common/timing.h @@ -44,28 +44,6 @@ struct CellPortKey } }; -struct NetPortKey -{ - IdString net; - size_t idx; - NetPortKey(){}; - explicit NetPortKey(IdString net) : net(net), idx(DRIVER_IDX){}; // driver - explicit NetPortKey(IdString net, size_t user) : net(net), idx(user){}; // user - - static const size_t DRIVER_IDX = std::numeric_limits::max(); - - inline bool is_driver() const { return (idx == DRIVER_IDX); } - inline size_t user_idx() const - { - NPNR_ASSERT(idx != DRIVER_IDX); - return idx; - } - - unsigned int hash() const { return mkhash(net.hash(), idx); } - - inline bool operator==(const NetPortKey &other) const { return (net == other.net) && (idx == other.idx); } -}; - struct ClockDomainKey { IdString clock; @@ -194,7 +172,6 @@ struct TimingAnalyser struct PerPort { CellPortKey cell_port; - NetPortKey net_port; PortType type; // per domain timings dict arrival; diff --git a/common/timing_opt.cc b/common/timing_opt.cc index a73a70cf0f..f9246292d5 100644 --- a/common/timing_opt.cc +++ b/common/timing_opt.cc @@ -73,8 +73,7 @@ class TimingOptimiser for (auto usr : ni->users) { max_net_delay[std::make_pair(usr.cell->name, usr.port)] = std::numeric_limits::max(); } - for (size_t i = 0; i < ni->users.size(); i++) { - auto &usr = ni->users.at(i); + for (auto usr : ni->users) { delay_t net_delay = ctx->getNetinfoRouteDelay(ni, usr); delay_t slack = tmg.get_setup_slack(CellPortKey(usr)); delay_t domain_slack = tmg.get_domain_setup_slack(CellPortKey(usr)); @@ -234,7 +233,7 @@ class TimingOptimiser std::vector> find_crit_paths(float crit_thresh, size_t max_count) { std::vector> crit_paths; - std::vector> crit_nets; + std::vector>> crit_nets; std::vector netnames; std::transform(ctx->nets.begin(), ctx->nets.end(), std::back_inserter(netnames), [](const std::pair> &kv) { return kv.first; }); @@ -243,28 +242,19 @@ class TimingOptimiser if (crit_nets.size() >= max_count) break; float highest_crit = 0; - size_t crit_user_idx = 0; + store_index crit_user_idx{}; NetInfo *ni = ctx->nets.at(net).get(); - for (size_t i = 0; i < ni->users.size(); i++) { - float crit = tmg.get_criticality(CellPortKey(ni->users.at(i))); + for (auto usr : ni->users.enumerate()) { + float crit = tmg.get_criticality(CellPortKey(usr.value)); if (crit > highest_crit) { highest_crit = crit; - crit_user_idx = i; + crit_user_idx = usr.index; } } if (highest_crit > crit_thresh) - crit_nets.push_back(std::make_pair(ni, crit_user_idx)); + crit_nets.emplace_back(ni, crit_user_idx); } - auto port_user_index = [](CellInfo *cell, PortInfo &port) -> size_t { - NPNR_ASSERT(port.net != nullptr); - for (size_t i = 0; i < port.net->users.size(); i++) { - auto &usr = port.net->users.at(i); - if (usr.cell == cell && usr.port == port.name) - return i; - } - NPNR_ASSERT_FALSE("port user not found on net"); - }; pool used_ports; for (auto crit_net : crit_nets) { @@ -280,7 +270,7 @@ class TimingOptimiser NetInfo *back_cursor = crit_net.first; while (back_cursor != nullptr) { float max_crit = 0; - std::pair crit_sink{nullptr, 0}; + std::pair> crit_sink{nullptr, {}}; CellInfo *cell = back_cursor->driver.cell; if (cell == nullptr) break; @@ -298,13 +288,12 @@ class TimingOptimiser bool is_path = ctx->getCellDelay(cell, port.first, back_cursor->driver.port, combDelay); if (!is_path) continue; - size_t user_idx = port_user_index(cell, port.second); float usr_crit = tmg.get_criticality(CellPortKey(cell->name, port.first)); - if (used_ports.count(&(pn->users.at(user_idx)))) + if (used_ports.count(&(pn->users.at(port.second.user_idx)))) continue; if (usr_crit >= max_crit) { max_crit = usr_crit; - crit_sink = std::make_pair(pn, user_idx); + crit_sink = std::make_pair(pn, port.second.user_idx); } } @@ -319,7 +308,7 @@ class TimingOptimiser while (fwd_cursor != nullptr) { crit_path.push_back(fwd_cursor); float max_crit = 0; - std::pair crit_sink{nullptr, 0}; + std::pair> crit_sink{nullptr, {}}; CellInfo *cell = fwd_cursor->cell; for (auto port : cell->ports) { if (port.second.type != PORT_OUT) @@ -336,13 +325,13 @@ class TimingOptimiser bool is_path = ctx->getCellDelay(cell, fwd_cursor->port, port.first, combDelay); if (!is_path) continue; - for (size_t i = 0; i < pn->users.size(); i++) { - if (used_ports.count(&(pn->users.at(i)))) + for (auto usr : pn->users.enumerate()) { + if (used_ports.count(&(pn->users.at(usr.index)))) continue; - float crit = tmg.get_criticality(CellPortKey(pn->users.at(i))); + float crit = tmg.get_criticality(CellPortKey(usr.value)); if (crit >= max_crit) { max_crit = crit; - crit_sink = std::make_pair(pn, i); + crit_sink = std::make_pair(pn, usr.index); } } } @@ -409,14 +398,10 @@ class TimingOptimiser delay_t original_delay = 0; for (size_t i = 0; i < path.size(); i++) { - NetInfo *pn = path.at(i)->cell->ports.at(path.at(i)->port).net; - for (size_t j = 0; j < pn->users.size(); j++) { - auto &usr = pn->users.at(j); - if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) { - original_delay += ctx->predictArcDelay(pn, usr); - break; - } - } + auto &port = path.at(i)->cell->ports.at(path.at(i)->port); + NetInfo *pn = port.net; + if (port.user_idx) + original_delay += ctx->predictArcDelay(pn, pn->users.at(port.user_idx)); } IdString last_cell; @@ -493,14 +478,10 @@ class TimingOptimiser delay_t total_delay = 0; for (size_t i = 0; i < path.size(); i++) { - NetInfo *pn = path.at(i)->cell->ports.at(path.at(i)->port).net; - for (size_t j = 0; j < pn->users.size(); j++) { - auto &usr = pn->users.at(j); - if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) { - total_delay += ctx->predictArcDelay(pn, usr); - break; - } - } + auto &port = path.at(i)->cell->ports.at(path.at(i)->port); + NetInfo *pn = port.net; + if (port.user_idx) + total_delay += ctx->predictArcDelay(pn, pn->users.at(port.user_idx)); if (path.at(i)->cell == next_cell) break; } diff --git a/ecp5/cells.cc b/ecp5/cells.cc index a5d484ff23..2c5f96d3fe 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -212,10 +212,7 @@ static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellI NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net); NetInfo *ffnet = ff->ports.at(ff_port).net; if (ffnet != nullptr) - ffnet->users.erase( - std::remove_if(ffnet->users.begin(), ffnet->users.end(), - [ff, ff_port](PortRef port) { return port.cell == ff && port.port == ff_port; }), - ffnet->users.end()); + ffnet->users.remove(ff->ports.at(ff_port).user_idx); } else { ff->movePortTo(ff_port, lc, lc_port); } @@ -477,7 +474,7 @@ void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vectorconnectPorts(id_Z, trio, id_T); created_cells.push_back(std::move(inv_lut)); - if (donet->users.size() > 1) { + if (donet->users.entries() > 1) { for (auto user : donet->users) log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); log_error("unsupported tristate IO pattern for IO buffer '%s', " diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 7b48e69388..71188aa0fb 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -472,17 +472,15 @@ class Ecp5GlobalRouter } else if (is_logic_port(user)) { keep_users.push_back(user); } else { - glbptr->users.push_back(user); user.cell->ports.at(user.port).net = glbptr; + user.cell->ports.at(user.port).user_idx = glbptr->users.add(user); } } - net->users = keep_users; + net->users.clear(); + for (auto &usr : keep_users) + usr.cell->ports.at(usr.port).user_idx = net->users.add(usr); - dcc->ports[id_CLKI].net = net; - PortRef clki_pr; - clki_pr.port = id_CLKI; - clki_pr.cell = dcc.get(); - net->users.push_back(clki_pr); + dcc->connectPort(id_CLKI, net); if (net->clkconstr) { glbptr->clkconstr = std::unique_ptr(new ClockConstraint()); glbptr->clkconstr->low = net->clkconstr->low; @@ -556,9 +554,13 @@ class Ecp5GlobalRouter if (ci->type == id_DCCA || ci->type == id_DCSC) { NetInfo *clock = ci->ports.at((ci->type == id_DCSC) ? id_DCSOUT : id_CLKO).net; NPNR_ASSERT(clock != nullptr); - bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(), - [this](const PortRef &port) { return !is_clock_port(port); }); - + bool drives_fabric = false; + for (auto &usr : clock->users) { + if (!is_clock_port(usr)) { + drives_fabric = true; + break; + } + } int glbid; if (drives_fabric) { if (fab_globals.empty()) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 2b069db098..8c442843d1 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -326,14 +326,14 @@ class Ecp5Packer } // Pack LUTs feeding the same CCU2, RAM or DFF into a SLICE - if (znet != nullptr && znet->users.size() < 10) { + if (znet != nullptr && znet->users.entries() < 10) { for (auto user : znet->users) { if (is_lc(ctx, user.cell) || user.cell->type == id_DP16KD || is_ff(ctx, user.cell)) { for (auto port : user.cell->ports) { if (port.second.type != PORT_IN || port.second.net == nullptr || port.second.net == znet) continue; - if (port.second.net->users.size() > 10) + if (port.second.net->users.entries() > 10) continue; CellInfo *drv = port.second.net->driver.cell; if (drv == nullptr) @@ -355,11 +355,11 @@ class Ecp5Packer if (!ci->ports.count(ctx->id(inp))) continue; NetInfo *innet = ci->ports.at(ctx->id(inp)).net; - if (innet != nullptr && innet->users.size() < 5 && innet->users.size() > 1) + if (innet != nullptr && innet->users.entries() < 5 && innet->users.entries() > 1) inpnets.push_back(innet); } std::sort(inpnets.begin(), inpnets.end(), - [&](const NetInfo *a, const NetInfo *b) { return a->users.size() < b->users.size(); }); + [&](const NetInfo *a, const NetInfo *b) { return a->users.entries() < b->users.entries(); }); for (auto inet : inpnets) { for (auto &user : inet->users) { if (user.cell == nullptr || user.cell == ci || !is_lut(ctx, user.cell)) @@ -412,7 +412,7 @@ class Ecp5Packer return false; for (auto user : net->users) { if (is_top_port(user)) { - if (net->users.size() > 1) + if (net->users.entries() > 1) log_error(" port %s.%s must be connected to (and only to) a top level pin\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); tp = user; @@ -420,7 +420,7 @@ class Ecp5Packer } } if (net->driver.cell != nullptr && is_top_port(net->driver)) { - if (net->users.size() > 1) + if (net->users.entries() > 1) log_error(" port %s.%s must be connected to (and only to) a top level pin\n", net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx)); tp = net->driver; @@ -460,9 +460,9 @@ class Ecp5Packer NetInfo *net = trio->ports.at(id_B).net; if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) && - net->users.size() > 1) || + net->users.entries() > 1) || (ci->type == ctx->id("$nextpnr_obuf") && - (net->users.size() > 2 || net->driver.cell != nullptr)) || + (net->users.entries() > 2 || net->driver.cell != nullptr)) || (ci->type == ctx->id("$nextpnr_iobuf") && ci->ports.at(id_I).net != nullptr && ci->ports.at(id_I).net->driver.cell != nullptr)) log_error("Pin B of %s '%s' connected to more than a single top level IO.\n", @@ -742,16 +742,14 @@ class Ecp5Packer feedin->params[id_INJECT1_0] = std::string("NO"); feedin->params[id_INJECT1_1] = std::string("YES"); - carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), - [chain_in](const PortRef &user) { - return user.port == chain_in.port && user.cell == chain_in.cell; - }), - carry->users.end()); + carry->users.remove(chain_in.cell->ports.at(chain_in.port).user_idx); feedin->connectPort(id_A0, carry); NetInfo *new_carry = ctx->createNet(ctx->id(feedin->name.str(ctx) + "$COUT")); feedin->connectPort(id_COUT, new_carry); chain_in.cell->ports[chain_in.port].net = nullptr; + chain_in.cell->ports[chain_in.port].user_idx = {}; + chain_in.cell->connectPort(chain_in.port, new_carry); CellInfo *feedin_ptr = feedin.get(); @@ -782,12 +780,8 @@ class Ecp5Packer if (chain_next) { // Loop back into LUT4_1 for feedthrough feedout->connectPort(id_A1, carry); - - carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), - [chain_next](const PortRef &user) { - return user.port == chain_next->port && user.cell == chain_next->cell; - }), - carry->users.end()); + if (chain_next->cell && chain_next->cell->ports.at(chain_next->port).user_idx) + carry->users.remove(chain_next->cell->ports.at(chain_next->port).user_idx); NetInfo *new_cout = ctx->createNet(ctx->id(feedout->name.str(ctx) + "$COUT")); feedout->connectPort(id_COUT, new_cout); @@ -833,7 +827,7 @@ class Ecp5Packer } else { NetInfo *carry_net = cell->ports.at(id_COUT).net; bool at_end = (curr_cell == carryc.cells.end() - 1); - if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) { + if (carry_net != nullptr && (carry_net->users.entries() > 1 || at_end)) { boost::optional nextport; if (!at_end) { auto next_cell = *(curr_cell + 1); @@ -1123,7 +1117,7 @@ class Ecp5Packer if (pn == nullptr) continue; // Skip high-fanout nets that are unlikely to be relevant - if (pn->users.size() > 25) + if (pn->users.entries() > 25) continue; // Add other ports on this net if not already visited auto visit_port = [&](const PortRef &port) { @@ -1304,11 +1298,11 @@ class Ecp5Packer } else { // Not allowed to change to a tie-high uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } } else { uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } } else if (is_ff(ctx, uc) && user.port == id_LSR && ((!constval && str_or_default(uc->params, id_LSRMUX, "LSR") == "LSR") || @@ -1335,7 +1329,7 @@ class Ecp5Packer user.port.str(ctx).substr(0, 6) == "SOURCE" || user.port.str(ctx).substr(0, 6) == "SIGNED" || user.port.str(ctx).substr(0, 2) == "OP") { uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } else { // Connected to CIB ABCD. Default state is bitstream configurable uc->params[ctx->id(user.port.str(ctx) + "MUX")] = std::string(constval ? "1" : "0"); @@ -1343,7 +1337,7 @@ class Ecp5Packer } } else { uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } } } @@ -2037,7 +2031,7 @@ class Ecp5Packer CellInfo *ci = cell.second.get(); if (ci->type == id_DQSBUFM) { CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_DQSI).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(id_DQSI).net->users.size() > 1) + if (pio == nullptr || ci->ports.at(id_DQSI).net->users.entries() > 1) log_error("DQSBUFM '%s' DQSI input must be connected only to a top level input\n", ci->name.c_str(ctx)); if (!pio->attrs.count(id_BEL)) @@ -2273,7 +2267,7 @@ class Ecp5Packer CellInfo *i_pio = net_driven_by(ctx, ci->ports.at(id_A).net, is_trellis_io, id_O); CellInfo *o_pio = net_only_drives(ctx, ci->ports.at(id_Z).net, is_trellis_io, id_I, true); CellInfo *iol = nullptr; - if (i_pio != nullptr && ci->ports.at(id_A).net->users.size() == 1) { + if (i_pio != nullptr && ci->ports.at(id_A).net->users.entries() == 1) { iol = create_pio_iologic(i_pio, ci); set_iologic_mode(iol, "IREG_OREG"); bool drives_iologic = false; @@ -2356,7 +2350,7 @@ class Ecp5Packer CellInfo *ci = cell.second.get(); if (ci->type == id_IDDRX1F) { CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1) + if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1) log_error("IDDRX1F '%s' D input must be connected only to a top level input\n", ci->name.c_str(ctx)); CellInfo *iol; @@ -2438,7 +2432,7 @@ class Ecp5Packer packed_cells.insert(cell.first); } else if (ci->type == id_IDDRX2F || ci->type == id_IDDR71B) { CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1) + if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1) log_error("%s '%s' D input must be connected only to a top level input\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); CellInfo *iol; @@ -2530,7 +2524,7 @@ class Ecp5Packer packed_cells.insert(cell.first); } else if (ci->type == id_IDDRX2DQA) { CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); - if (pio == nullptr || ci->ports.at(id_D).net->users.size() > 1) + if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1) log_error("IDDRX2DQA '%s' D input must be connected only to a top level input\n", ci->name.c_str(ctx)); CellInfo *iol; @@ -2597,7 +2591,7 @@ class Ecp5Packer // See if it can be packed as an input ff NetInfo *d = ci->getPort(id_DI); CellInfo *pio = net_driven_by(ctx, d, is_trellis_io, id_O); - if (pio != nullptr && d->users.size() == 1) { + if (pio != nullptr && d->users.entries() == 1) { // Input FF CellInfo *iol; if (pio_iologic.count(pio->name)) diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 917af85e92..a5e802d303 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -1377,7 +1377,7 @@ void Arch::merge_constant_nets() } NPNR_ASSERT(net->driver.port == gnd_cell_port); - std::vector users_copy = net->users; + indexed_store users_copy = net->users; for (const PortRef &port_ref : users_copy) { IdString cell = port_ref.cell->name; disconnectPort(cell, port_ref.port); @@ -1400,7 +1400,7 @@ void Arch::merge_constant_nets() } NPNR_ASSERT(net->driver.port == vcc_cell_port); - std::vector users_copy = net->users; + indexed_store users_copy = net->users; for (const PortRef &port_ref : users_copy) { IdString cell = port_ref.cell->name; disconnectPort(cell, port_ref.port); diff --git a/fpga_interchange/arch_pack_clusters.cc b/fpga_interchange/arch_pack_clusters.cc index 31e0522bf9..97a3e1a595 100644 --- a/fpga_interchange/arch_pack_clusters.cc +++ b/fpga_interchange/arch_pack_clusters.cc @@ -945,10 +945,10 @@ void Arch::prepare_cluster(const ClusterPOD *cluster, uint32_t index) if (port_info.type == PORT_OUT) { exclude_nets.insert(port_info.net->name); auto &users = port_info.net->users; - if (users.size() != 1) + if (users.entries() != 1) continue; - CellInfo *user_cell = users[0].cell; + CellInfo *user_cell = (*users.begin()).cell; if (user_cell == nullptr) continue; @@ -978,7 +978,7 @@ void Arch::prepare_cluster(const ClusterPOD *cluster, uint32_t index) } else if (port_info.type == PORT_IN) { auto &driver = port_info.net->driver; auto &users = port_info.net->users; - if (users.size() != 1) + if (users.entries() != 1) continue; CellInfo *driver_cell = driver.cell; diff --git a/fpga_interchange/globals.cc b/fpga_interchange/globals.cc index ed9f73a65e..6efd1d8994 100644 --- a/fpga_interchange/globals.cc +++ b/fpga_interchange/globals.cc @@ -41,8 +41,8 @@ struct GlobalVist // This is our main global routing implementation. It is used both to actually route globals; and also to discover if // global buffers have available short routes from their source for auto-placement -static int route_global_arc(Context *ctx, NetInfo *net, size_t usr_idx, size_t phys_port_idx, int max_hops, - bool dry_run) +static int route_global_arc(Context *ctx, NetInfo *net, store_index usr_idx, size_t phys_port_idx, + int max_hops, bool dry_run) { auto &usr = net->users.at(usr_idx); WireId src = ctx->getNetinfoSourceWire(net); @@ -51,7 +51,7 @@ static int route_global_arc(Context *ctx, NetInfo *net, size_t usr_idx, size_t p if (dry_run) return -1; else - log_error("Arc %d.%d (%s.%s) of net %s has no sink wire!\n", int(usr_idx), int(phys_port_idx), + log_error("Arc %d.%d (%s.%s) of net %s has no sink wire!\n", usr_idx.idx(), int(phys_port_idx), ctx->nameOf(usr.cell), ctx->nameOf(usr.port), ctx->nameOf(net)); } // Consider any existing routing put in place by the site router, etc @@ -188,14 +188,6 @@ void Arch::place_globals() // Ignore if there is no driver; or the driver is not placed if (net->driver.cell == nullptr || net->driver.cell->bel == BelId()) continue; - size_t user_idx = 0; - bool found_user = false; - for (user_idx = 0; user_idx < net->users.size(); user_idx++) - if (net->users.at(user_idx).cell == ci && net->users.at(user_idx).port == pin_name) { - found_user = true; - break; - } - NPNR_ASSERT(found_user); // TODO: substantial performance improvements are probably possible, although of questionable benefit given // the low number of globals in a typical device... @@ -213,7 +205,7 @@ void Arch::place_globals() if (!isBelLocationValid(bel)) goto fail; // Check distance - distance = route_global_arc(ctx, net, user_idx, 0, pin.max_hops, true); + distance = route_global_arc(ctx, net, port.user_idx, 0, pin.max_hops, true); if (distance != -1 && distance < shortest_distance) { best_bel = bel; shortest_distance = distance; @@ -262,16 +254,16 @@ void Arch::route_globals() int total_sinks = 0; int global_sinks = 0; - for (size_t i = 0; i < net->users.size(); i++) { - auto &usr = net->users.at(i); - for (size_t j = 0; j < ctx->getNetinfoSinkWireCount(net, usr); j++) { - int result = route_global_arc(ctx, net, i, j, pin.max_hops, false); + for (auto usr : net->users.enumerate()) { + for (size_t j = 0; j < ctx->getNetinfoSinkWireCount(net, usr.value); j++) { + int result = route_global_arc(ctx, net, usr.index, j, pin.max_hops, false); ++total_sinks; if (result != -1) ++global_sinks; if ((result == -1) && pin.force_routing) log_error("Failed to route arc %d.%d (%s.%s) of net %s using dedicated global routing!\n", - int(i), int(j), ctx->nameOf(usr.cell), ctx->nameOf(usr.port), ctx->nameOf(net)); + usr.index.idx(), int(j), ctx->nameOf(usr.value.cell), ctx->nameOf(usr.value.port), + ctx->nameOf(net)); } } diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h index 8ac61bbea6..a2ac219c45 100644 --- a/frontend/frontend_base.h +++ b/frontend/frontend_base.h @@ -690,7 +690,7 @@ template struct GenericFrontend // Combine users for (auto &usr : mergee->users) { usr.cell->ports[usr.port].net = base; - base->users.push_back(usr); + usr.cell->ports[usr.port].user_idx = base->users.add(usr); } // Point aliases to the new net for (IdString alias : mergee->aliases) { diff --git a/generic/cells.cc b/generic/cells.cc index 76d6474f70..e4a24767f5 100644 --- a/generic/cells.cc +++ b/generic/cells.cc @@ -121,7 +121,7 @@ void nxio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &to tbuf->movePortTo(ctx->id("A"), iob, ctx->id("I")); tbuf->movePortTo(ctx->id("E"), iob, ctx->id("EN")); - if (donet->users.size() > 1) { + if (donet->users.entries() > 1) { for (auto user : donet->users) log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); log_error("unsupported tristate IO pattern for IO buffer '%s', " diff --git a/generic/pack.cc b/generic/pack.cc index cb3f58974b..428d6642b6 100644 --- a/generic/pack.cc +++ b/generic/pack.cc @@ -124,9 +124,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx)); if ((is_lut(ctx, uc) || is_lc(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') && !constval) { uc->ports[user.port].net = nullptr; + uc->ports[user.port].user_idx = {}; } else { uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } } } @@ -224,8 +225,8 @@ static void pack_io(Context *ctx) ci->type.c_str(ctx), ci->name.c_str(ctx)); NetInfo *net = iob->ports.at(ctx->id("PAD")).net; if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) && - net->users.size() > 1) || - (ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr))) + net->users.entries() > 1) || + (ci->type == ctx->id("$nextpnr_obuf") && (net->users.entries() > 2 || net->driver.cell != nullptr))) log_error("PAD of %s '%s' connected to more than a single top level IO.\n", iob->type.c_str(ctx), iob->name.c_str(ctx)); diff --git a/generic/viaduct/okami/okami.cc b/generic/viaduct/okami/okami.cc index bcb34e8472..864bdb456d 100644 --- a/generic/viaduct/okami/okami.cc +++ b/generic/viaduct/okami/okami.cc @@ -511,7 +511,7 @@ struct OkamiImpl : ViaductAPI const auto &ff_data = fast_cell_info.at(ff->flat_index); // In our example arch; the FF D can either be driven from LUT F or LUT I3 // so either; FF D must equal LUT F or LUT I3 must be unused - if (ff_data.ff_d == lut_data.lut_f && lut_data.lut_f->users.size() == 1) + if (ff_data.ff_d == lut_data.lut_f && lut_data.lut_f->users.entries() == 1) return true; // Can't route FF and LUT output separately return false; diff --git a/generic/viaduct_helpers.cc b/generic/viaduct_helpers.cc index 10c3b8020a..a92d0de1b2 100644 --- a/generic/viaduct_helpers.cc +++ b/generic/viaduct_helpers.cc @@ -96,7 +96,7 @@ int ViaductHelpers::constrain_cell_pairs(const pool &src_ports, co continue; if (!src_ports.count(CellTypePort(ci.type, port.first))) continue; - if (!allow_fanout && port.second.net->users.size() > 1) + if (!allow_fanout && port.second.net->users.entries() > 1) continue; for (auto &usr : port.second.net->users) { if (!sink_ports.count(CellTypePort(usr))) @@ -151,7 +151,7 @@ void ViaductHelpers::replace_constants(CellTypePort vcc_driver, CellTypePort gnd NetInfo *replace = (ni.driver.cell->type == ctx->id("VCC")) ? vcc_net : gnd_net; for (auto &usr : ni.users) { usr.cell->ports.at(usr.port).net = replace; - replace->users.push_back(usr); + usr.cell->ports.at(usr.port).user_idx = replace->users.add(usr); } trim_cells.push_back(ni.driver.cell->name); trim_nets.push_back(ni.name); diff --git a/gowin/pack.cc b/gowin/pack.cc index cc71586436..c17a20c7c5 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -68,7 +68,7 @@ static void pack_alus(Context *ctx) continue; } - if (!is_alu(ctx, cin_ci) || cin->users.size() > 1) { + if (!is_alu(ctx, cin_ci) || cin->users.entries() > 1) { if (ctx->verbose) { log_info("ALU head found %s. CIN net is %s\n", ctx->nameOf(ci), ctx->nameOf(cin)); } @@ -177,9 +177,9 @@ static void pack_alus(Context *ctx) new_cells.push_back(std::move(packed)); - if (cout != nullptr && cout->users.size() > 0) { + if (cout != nullptr && cout->users.entries() > 0) { // if COUT used by logic - if ((cout->users.size() > 1) || (!is_alu(ctx, cout->users.at(0).cell))) { + if ((cout->users.entries() > 1) || (!is_alu(ctx, (*cout->users.begin()).cell))) { if (ctx->verbose) { log_info("COUT is used by logic\n"); } @@ -204,7 +204,7 @@ static void pack_alus(Context *ctx) break; } // next ALU - ci = cout->users.at(0).cell; + ci = (*cout->users.begin()).cell; // if ALU is too big if (alu_idx == (ctx->gridDimX - 2) * 6 - 1) { log_error("ALU %s is the %dth in the chain. Such long chains are not supported.\n", ctx->nameOf(ci), @@ -596,9 +596,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne log_info("%s user %s\n", ctx->nameOf(orig), ctx->nameOf(uc)); if ((is_lut(ctx, uc) || is_lc(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') && !constval) { uc->ports[user.port].net = nullptr; + uc->ports[user.port].user_idx = {}; } else { uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } } } diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 89f8426207..48fbc1320b 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -1146,7 +1146,7 @@ bool read_asc(Context *ctx, std::istream &in) if (port.second.type == PORT_OUT) net->driver = ref; else - net->users.push_back(ref); + port.second.user_idx = net->users.add(ref); } } } diff --git a/ice40/cells.cc b/ice40/cells.cc index b5f759b22c..9166b41b8d 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -466,7 +466,7 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &to tbuf->movePortTo(id_A, sbio, id_D_OUT_0); tbuf->movePortTo(id_E, sbio, id_OUTPUT_ENABLE); - if (donet->users.size() > 1) { + if (donet->users.entries() > 1) { for (auto user : donet->users) log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); log_error("unsupported tristate IO pattern for IO buffer '%s', " diff --git a/ice40/chains.cc b/ice40/chains.cc index e6a37044fa..6ea261de49 100644 --- a/ice40/chains.cc +++ b/ice40/chains.cc @@ -73,8 +73,8 @@ class ChainConstrainer } else { NetInfo *carry_net = cell->ports.at(id_COUT).net; bool at_end = (curr_cell == carryc.cells.end() - 1); - if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) { - if (carry_net->users.size() > 2 || + if (carry_net != nullptr && (carry_net->users.entries() > 1 || at_end)) { + if (carry_net->users.entries() > 2 || (net_only_drives(ctx, carry_net, is_lc, id_I3, false) != net_only_drives(ctx, carry_net, is_lc, id_CIN, false)) || (at_end && !net_only_drives(ctx, carry_net, is_lc, id_I3, true))) { @@ -116,15 +116,11 @@ class ChainConstrainer lc->ports.at(id_O).net = cout_port.net; NetInfo *co_i3_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$I3")); co_i3_net->driver = cout_port.net->driver; - PortRef i3_r; - i3_r.port = id_I3; - i3_r.cell = lc.get(); - co_i3_net->users.push_back(i3_r); + lc->connectPort(id_I3, co_i3_net); PortRef o_r; o_r.port = id_O; o_r.cell = lc.get(); cout_port.net->driver = o_r; - lc->ports.at(id_I3).net = co_i3_net; cout_port.net = co_i3_net; // If COUT also connects to a CIN; preserve the carry chain @@ -133,34 +129,21 @@ class ChainConstrainer // Connect I1 to 1 to preserve carry chain NetInfo *vcc = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get(); - lc->ports.at(id_I1).net = vcc; - PortRef i1_r; - i1_r.port = id_I1; - i1_r.cell = lc.get(); - vcc->users.push_back(i1_r); + lc->connectPort(id_I1, vcc); // Connect co_cin_net to the COUT of the LC - PortRef co_r; - co_r.port = id_COUT; - co_r.cell = lc.get(); - co_cin_net->driver = co_r; - lc->ports.at(id_COUT).net = co_cin_net; + lc->connectPort(id_COUT, co_cin_net); // Find the user corresponding to the next CIN int replaced_ports = 0; if (ctx->debug) log_info("cell: %s\n", cin_cell->name.c_str(ctx)); for (auto port : {id_CIN, id_I3}) { - auto &usr = lc->ports.at(id_O).net->users; - if (ctx->debug) - for (auto user : usr) - log_info("%s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); - auto fnd_user = std::find_if(usr.begin(), usr.end(), - [&](const PortRef &pr) { return pr.cell == cin_cell && pr.port == port; }); - if (fnd_user != usr.end()) { - co_cin_net->users.push_back(*fnd_user); - usr.erase(fnd_user); - cin_cell->ports.at(port).net = co_cin_net; + NetInfo *out_net = lc->ports.at(id_O).net; + auto &cin_p = cin_cell->ports.at(port); + if (cin_p.net == out_net) { + cin_cell->disconnectPort(port); + cin_cell->connectPort(port, co_cin_net); ++replaced_ports; } } @@ -181,30 +164,16 @@ class ChainConstrainer lc->params[id_CARRY_ENABLE] = Property::State::S1; lc->params[id_CIN_CONST] = Property::State::S1; lc->params[id_CIN_SET] = Property::State::S1; - lc->ports.at(id_I1).net = cin_port.net; - cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(), - [cin_cell, cin_port](const PortRef &usr) { - return usr.cell == cin_cell && usr.port == cin_port.name; - })); - PortRef i1_ref; - i1_ref.cell = lc.get(); - i1_ref.port = id_I1; - lc->ports.at(id_I1).net->users.push_back(i1_ref); + lc->connectPort(id_I1, cin_port.net); + cin_port.net->users.remove(cin_port.user_idx); NetInfo *out_net = ctx->createNet(ctx->id(lc->name.str(ctx) + "$O")); - PortRef drv_ref; - drv_ref.port = id_COUT; - drv_ref.cell = lc.get(); - out_net->driver = drv_ref; - lc->ports.at(id_COUT).net = out_net; + lc->connectPort(id_COUT, out_net); - PortRef usr_ref; - usr_ref.port = cin_port.name; - usr_ref.cell = cin_cell; - out_net->users.push_back(usr_ref); - cin_cell->ports.at(cin_port.name).net = out_net; + cin_port.net = nullptr; + cin_cell->connectPort(cin_port.name, out_net); IdString name = lc->name; ctx->assignCellInfo(lc.get()); diff --git a/ice40/pack.cc b/ice40/pack.cc index 4244f1921b..c32e346fcc 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -214,14 +214,14 @@ static void pack_carries(Context *ctx) PortRef pr; pr.cell = created_lc.get(); pr.port = id_I1; - i0_net->users.push_back(pr); + created_lc->ports.at(id_I1).user_idx = i0_net->users.add(pr); } created_lc->ports.at(id_I2).net = i1_net; if (i1_net) { PortRef pr; pr.cell = created_lc.get(); pr.port = id_I2; - i1_net->users.push_back(pr); + created_lc->ports.at(id_I2).user_idx = i1_net->users.add(pr); } new_cells.push_back(std::move(created_lc)); ++carry_only; @@ -230,16 +230,12 @@ static void pack_carries(Context *ctx) ci->movePortTo(id_CI, carry_lc, id_CIN); ci->movePortTo(id_CO, carry_lc, id_COUT); if (i0_net) { - auto &i0_usrs = i0_net->users; - i0_usrs.erase(std::remove_if(i0_usrs.begin(), i0_usrs.end(), [ci, ctx](const PortRef &pr) { - return pr.cell == ci && pr.port == id_I0; - })); + if (ci->ports.count(id_I0) && ci->ports.at(id_I0).user_idx) + i0_net->users.remove(ci->ports.at(id_I0).user_idx); } if (i1_net) { - auto &i1_usrs = i1_net->users; - i1_usrs.erase(std::remove_if(i1_usrs.begin(), i1_usrs.end(), [ci, ctx](const PortRef &pr) { - return pr.cell == ci && pr.port == id_I1; - })); + if (ci->ports.count(id_I1) && ci->ports.at(id_I1).user_idx) + i1_net->users.remove(ci->ports.at(id_I1).user_idx); } // Check for constant driver on CIN @@ -251,10 +247,7 @@ static void pack_carries(Context *ctx) cin_net == ctx->id("$PACKER_VCC_NET") ? Property::State::S1 : Property::State::S0; carry_lc->ports.at(id_CIN).net = nullptr; auto &cin_users = ctx->nets.at(cin_net)->users; - cin_users.erase( - std::remove_if(cin_users.begin(), cin_users.end(), [carry_lc, ctx](const PortRef &pr) { - return pr.cell == carry_lc && pr.port == id_CIN; - })); + cin_users.remove(carry_lc->ports.at(id_CIN).user_idx); } } exhausted_cells.insert(carry_lc->name); @@ -326,17 +319,20 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') && !constval) { uc->ports[user.port].net = nullptr; + uc->ports[user.port].user_idx = {}; } else if ((is_sb_mac16(ctx, uc) || uc->type == id_ICESTORM_DSP) && (user.port != id_CLK && ((constval && user.port == id_CE) || (!constval && user.port != id_CE)))) { uc->ports[user.port].net = nullptr; + uc->ports[user.port].user_idx = {}; } else if (is_ram(ctx, uc) && !constval && user.port != id_RCLK && user.port != id_RCLKN && user.port != id_WCLK && user.port != id_WCLKN && user.port != id_RCLKE && user.port != id_WCLKE) { uc->ports[user.port].net = nullptr; + uc->ports[user.port].user_idx = {}; } else { uc->ports[user.port].net = constnet; - constnet->users.push_back(user); + uc->ports[user.port].user_idx = constnet->users.add(user); } } } @@ -486,8 +482,8 @@ static void pack_io(Context *ctx) ci->type.c_str(ctx), ci->name.c_str(ctx)); NetInfo *net = sb->ports.at(id_PACKAGE_PIN).net; if (((ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) && - net->users.size() > 1) || - (ci->type == ctx->id("$nextpnr_obuf") && (net->users.size() > 2 || net->driver.cell != nullptr))) + net->users.entries() > 1) || + (ci->type == ctx->id("$nextpnr_obuf") && (net->users.entries() > 2 || net->driver.cell != nullptr))) log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", sb->type.c_str(ctx), sb->name.c_str(ctx)); @@ -531,9 +527,9 @@ static void pack_io(Context *ctx) sb->attrs[attr.first] = attr.second; } else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) { NetInfo *net = ci->ports.at(id_PACKAGE_PIN).net; - if ((net != nullptr) && ((net->users.size() > 2) || + if ((net != nullptr) && ((net->users.entries() > 2) || (net->driver.cell != nullptr && - net->driver.cell->type == ctx->id("$nextpnr_obuf") && net->users.size() > 1))) + net->driver.cell->type == ctx->id("$nextpnr_obuf") && net->users.entries() > 1))) log_error("PACKAGE_PIN of %s '%s' connected to more than a single top level IO.\n", ci->type.c_str(ctx), ci->name.c_str(ctx)); } @@ -554,11 +550,11 @@ static void pack_io(Context *ctx) NetInfo *net_in0 = ci->ports.count(id_D_IN_0) ? ci->ports[id_D_IN_0].net : nullptr; NetInfo *net_in1 = ci->ports.count(id_D_IN_1) ? ci->ports[id_D_IN_1].net : nullptr; - if (net_in0 != nullptr && net_in0->users.size() == 0) { + if (net_in0 != nullptr && net_in0->users.entries() == 0) { delete_nets.insert(net_in0->name); ci->ports[id_D_IN_0].net = nullptr; } - if (net_in1 != nullptr && net_in1->users.size() == 0) { + if (net_in1 != nullptr && net_in1->users.entries() == 0) { delete_nets.insert(net_in1->name); ci->ports[id_D_IN_1].net = nullptr; } @@ -591,28 +587,23 @@ static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk")); std::unique_ptr gb = create_ice_cell(ctx, id_SB_GB, "$gbuf_" + glb_name); - gb->ports[id_USER_SIGNAL_TO_GLOBAL_BUFFER].net = net; - PortRef pr; - pr.cell = gb.get(); - pr.port = id_USER_SIGNAL_TO_GLOBAL_BUFFER; - net->users.push_back(pr); - - pr.cell = gb.get(); - pr.port = id_GLOBAL_BUFFER_OUTPUT; + gb->connectPort(id_USER_SIGNAL_TO_GLOBAL_BUFFER, net); NetInfo *glbnet = ctx->createNet(ctx->id(glb_name)); - glbnet->driver = pr; - gb->ports[id_GLOBAL_BUFFER_OUTPUT].net = glbnet; + gb->connectPort(id_GLOBAL_BUFFER_OUTPUT, glbnet); + std::vector keep_users; for (auto user : net->users) { if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) || (is_cen && is_enable_port(ctx, user)) || (is_logic && is_logic_port(ctx, user))) { user.cell->ports[user.port].net = glbnet; - glbnet->users.push_back(user); + user.cell->ports[user.port].user_idx = glbnet->users.add(user); } else { keep_users.push_back(user); } } - net->users = keep_users; + net->users.clear(); + for (auto &user : keep_users) + user.cell->ports[user.port].user_idx = net->users.add(user); if (net->clkconstr) { glbnet->clkconstr = std::unique_ptr(new ClockConstraint()); @@ -809,7 +800,7 @@ static void place_plls(Context *ctx) log_error("PLL '%s' has a PACKAGEPIN driven by an %s, should be directly connected to an input " "SB_IO.D_IN_0 port\n", ci->name.c_str(ctx), io_cell->type.c_str(ctx)); - if (ni->users.size() != 1) + if (ni->users.entries() != 1) log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx), ni->name.c_str(ctx)); if (!io_cell->attrs.count(id_BEL)) log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx), @@ -911,10 +902,10 @@ static void place_plls(Context *ctx) // Used global connections bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) && - (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0); + (ci->ports[id_PLLOUT_A_GLOBAL].net->users.entries() > 0); bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) && (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) && - (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0); + (ci->ports[id_PLLOUT_B_GLOBAL].net->users.entries() > 0); // Check for conflict BelPin pll_io_a, pll_io_b; @@ -954,15 +945,15 @@ static void place_plls(Context *ctx) // Used global connections bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) && - (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0); + (ci->ports[id_PLLOUT_A_GLOBAL].net->users.entries() > 0); bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) && (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) && - (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0); + (ci->ports[id_PLLOUT_B_GLOBAL].net->users.entries() > 0); // Could this be a PAD PLL ? bool could_be_pad = false; BelId pad_bel; - if (ni->users.size() == 1 && is_sb_io(ctx, ni->driver.cell) && ni->driver.cell->attrs.count(id_BEL)) + if (ni->users.entries() == 1 && is_sb_io(ctx, ni->driver.cell) && ni->driver.cell->attrs.count(id_BEL)) pad_bel = ctx->getBelByNameStr(ni->driver.cell->attrs[id_BEL].as_string()); // Find a BEL for it @@ -1035,7 +1026,7 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString PortRef pr; pr.cell = user.cell; pr.port = user.port; - out_net->users.push_back(pr); + user.cell->ports[user.port].user_idx = out_net->users.add(pr); } // Add LUT to new users. @@ -1045,8 +1036,10 @@ static std::unique_ptr spliceLUT(Context *ctx, CellInfo *ci, IdString new_users.push_back(pr); pt->ports.at(id_I3).net = port.net; - // Replace users of the original net. - port.net->users = new_users; + // Replace users of the original net + port.net->users.clear(); + for (auto &usr : new_users) + usr.cell->ports.at(usr.port).user_idx = port.net->users.add(usr); return pt; } @@ -1235,7 +1228,7 @@ static void pack_special(Context *ctx) if ((pi.name != id_RGB0) && (pi.name != id_RGB1) && (pi.name != id_RGB2)) continue; - if (net->users.size() > 0) + if (net->users.entries() > 0) log_error("SB_RGB_DRV/SB_RGBA_DRV port connected to more than just package pin !\n"); ctx->nets.erase(net->name); @@ -1585,7 +1578,7 @@ void pack_plls(Context *ctx) // Only if there is actually a net ... if (pi.net != nullptr) { // ... and it's used - if (pi.net->users.size() > 0) { + if (pi.net->users.entries() > 0) { std::unique_ptr gb = create_padin_gbuf(ctx, packed.get(), pi.name, "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a")); diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 5051a98123..56efb63fe5 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -160,7 +160,7 @@ static void set_net_constant(Context *ctx, NetInfo *orig, NetInfo *constnet, boo uc->ports[user.port].net = constnet; } - constnet->users.push_back(user); + user.cell->ports.at(user.port).user_idx = constnet->users.add(user); } } orig->users.clear(); diff --git a/mistral/pack.cc b/mistral/pack.cc index c4b3afe357..703fa386ac 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -200,10 +200,10 @@ struct MistralPacker NetInfo *o = ci->getPort(id_O); if (o == nullptr) ; - else if (o->users.size() > 1) + else if (o->users.entries() > 1) log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first)); - else if (o->users.size() == 1) - top_port = o->users.at(0); + else if (o->users.entries() == 1) + top_port = *o->users.begin(); } if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { // Might have an output buffer (OB etc) connected to it @@ -215,9 +215,9 @@ struct MistralPacker top_port = i->driver; } // Edge case of a bidirectional buffer driving an output pin - if (i->users.size() > 2) { + if (i->users.entries() > 2) { log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); - } else if (i->users.size() == 2) { + } else if (i->users.entries() == 2) { if (top_port.cell != nullptr) log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); for (auto &usr : i->users) { @@ -300,9 +300,9 @@ struct MistralPacker const NetInfo *co = cursor->getPort(id_CO); if (co == nullptr || co->users.empty()) break; - if (co->users.size() > 1) + if (co->users.entries() > 1) log_error("Carry net %s has more than one sink!\n", ctx->nameOf(co)); - auto &usr = co->users.at(0); + auto &usr = *co->users.begin(); if (usr.port != id_CI) log_error("Carry net %s drives port %s, expected CI\n", ctx->nameOf(co), ctx->nameOf(usr.port)); cursor = usr.cell; diff --git a/nexus/global.cc b/nexus/global.cc index 1c763a3100..31bf0a6b7a 100644 --- a/nexus/global.cc +++ b/nexus/global.cc @@ -72,7 +72,7 @@ struct NexusGlobalRouter // Dedicated backwards BFS routing for global networks template - bool backwards_bfs_route(NetInfo *net, size_t user_idx, int iter_limit, bool strict, Tfilt pip_filter) + bool backwards_bfs_route(NetInfo *net, store_index user_idx, int iter_limit, bool strict, Tfilt pip_filter) { // Queue of wires to visit std::queue visit; @@ -178,9 +178,9 @@ struct NexusGlobalRouter void route_clk_net(NetInfo *net) { - for (size_t i = 0; i < net->users.size(); i++) - backwards_bfs_route(net, i, 1000000, true, [&](PipId pip) { - return (is_relaxed_sink(net->users.at(i)) || global_pip_filter(pip)) && routeability_pip_filter(pip); + for (auto usr : net->users.enumerate()) + backwards_bfs_route(net, usr.index, 1000000, true, [&](PipId pip) { + return (is_relaxed_sink(usr.value) || global_pip_filter(pip)) && routeability_pip_filter(pip); }); log_info(" routed net '%s' using global resources\n", ctx->nameOf(net)); } diff --git a/nexus/pack.cc b/nexus/pack.cc index 5509d997e1..a769c05adc 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -489,10 +489,10 @@ struct NexusPacker NetInfo *o = ci->getPort(id_O); if (o == nullptr) ; - else if (o->users.size() > 1) + else if (o->users.entries() > 1) log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first)); - else if (o->users.size() == 1) - top_port = o->users.at(0); + else if (o->users.entries() == 1) + top_port = *o->users.begin(); } if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { // Might have an output buffer (OB etc) connected to it @@ -504,9 +504,9 @@ struct NexusPacker top_port = i->driver; } // Edge case of a bidirectional buffer driving an output pin - if (i->users.size() > 2) { + if (i->users.entries() > 2) { log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); - } else if (i->users.size() == 2) { + } else if (i->users.entries() == 2) { if (top_port.cell != nullptr) log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); for (auto &usr : i->users) { @@ -834,13 +834,15 @@ struct NexusPacker for (auto &usr : net->users) { if (pred(usr)) { usr.cell->ports[usr.port].net = buffered_net; - buffered_net->users.push_back(usr); + usr.cell->ports[usr.port].user_idx = buffered_net->users.add(usr); } else { remaining_users.push_back(usr); } } - std::swap(net->users, remaining_users); + net->users.clear(); + for (auto &usr : remaining_users) + usr.cell->ports.at(usr.port).user_idx = net->users.add(usr); // Connect buffer input to original net buffer->connectPort(i, net); @@ -1195,11 +1197,11 @@ struct NexusPacker if (di != nullptr && di->driver.cell != nullptr) iob = di->driver.cell; NetInfo *dout = ci->getPort(id_DOUT); - if (dout != nullptr && dout->users.size() == 1) - iob = dout->users.at(0).cell; + if (dout != nullptr && dout->users.entries() == 1) + iob = (*dout->users.begin()).cell; NetInfo *tout = ci->getPort(id_TOUT); - if (tout != nullptr && tout->users.size() == 1) - iob = tout->users.at(0).cell; + if (tout != nullptr && tout->users.entries() == 1) + iob = (*tout->users.begin()).cell; if (iob == nullptr || (iob->type != id_SEIO18_CORE && iob->type != id_SEIO33_CORE && iob->type != id_DIFFIO18_CORE)) log_error("Failed to find associated IOB for IOLOGIC %s\n", ctx->nameOf(ci)); @@ -1358,11 +1360,12 @@ struct NexusPacker NetInfo *fco = combs[1]->getPort(id_FCO); ci = nullptr; if (fco != nullptr) { - if (fco->users.size() > 1) + if (fco->users.entries() > 1) log_error("Carry cell '%s' has multiple fanout on FCO\n", ctx->nameOf(combs[1])); - else if (fco->users.size() == 1) { - NPNR_ASSERT(fco->users.at(0).port == id_CIN); - ci = fco->users.at(0).cell; + else if (fco->users.entries() == 1) { + auto &u0 = *fco->users.begin(); + NPNR_ASSERT(u0.port == id_CIN); + ci = u0.cell; } } } while (ci != nullptr); @@ -2161,13 +2164,13 @@ struct NexusPacker isIDDR = true; } NetInfo *dout = iol->getPort(id_DOUT); - if (dout != nullptr && dout->users.size() == 1) { - iob = dout->users.at(0).cell; + if (dout != nullptr && dout->users.entries() == 1) { + iob = (*dout->users.begin()).cell; isODDR = true; } NetInfo *tout = iol->getPort(id_TOUT); - if (tout != nullptr && tout->users.size() == 1) { - iob = tout->users.at(0).cell; + if (tout != nullptr && tout->users.entries() == 1) { + iob = (*tout->users.begin()).cell; isODDR = true; // FIXME: Not sure } NPNR_ASSERT(iob != nullptr); @@ -2359,7 +2362,7 @@ struct NexusPacker } // Skip if there are multiple sinks on that net - if (di->users.size() != 1) { + if (di->users.entries() != 1) { continue; } From 9b3e687eda12bc6ac2ce6208d046f36ca853aa67 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 28 Feb 2022 13:10:00 +0000 Subject: [PATCH 076/712] ecp5: Fix PDPW16K clock param renaming Signed-off-by: gatecat --- ecp5/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 2b069db098..e657e60cda 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -1454,9 +1454,9 @@ class Ecp5Packer rename_param(ci, "CLKWMUX", "CLKAMUX"); if (str_or_default(ci->params, id_CLKAMUX) == "CLKW") ci->params[id_CLKAMUX] = std::string("CLKA"); + rename_param(ci, "CLKRMUX", "CLKBMUX"); if (str_or_default(ci->params, id_CLKBMUX) == "CLKR") ci->params[id_CLKBMUX] = std::string("CLKB"); - rename_param(ci, "CLKRMUX", "CLKRMUX"); rename_param(ci, "CSDECODE_W", "CSDECODE_A"); rename_param(ci, "CSDECODE_R", "CSDECODE_B"); std::string outreg = str_or_default(ci->params, id_REGMODE, "NOREG"); From cc9f99a80c9aa9b69bc2a54060b5fbcec799f145 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 19 Dec 2021 19:29:44 +0000 Subject: [PATCH 077/712] parallel_refine: New, parallelised placement refinement pass --- common/command.cc | 11 + common/parallel_refine.cc | 946 ++++++++++++++++++++++++++++++++++++++ common/parallel_refine.h | 43 ++ common/placer_heap.cc | 13 +- common/placer_heap.h | 1 + 5 files changed, 1012 insertions(+), 2 deletions(-) create mode 100644 common/parallel_refine.cc create mode 100644 common/parallel_refine.h diff --git a/common/command.cc b/common/command.cc index d4279a581f..5632784710 100644 --- a/common/command.cc +++ b/common/command.cc @@ -109,6 +109,7 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("debug", "debug output"); general.add_options()("debug-placer", "debug output from placer only"); general.add_options()("debug-router", "debug output from router only"); + general.add_options()("threads", po::value(), "number of threads for passes where this is configurable"); general.add_options()("force,f", "keep running after errors"); #ifndef NO_GUI @@ -173,6 +174,8 @@ po::options_description CommandHandler::getGeneralOptions() "placer heap criticality exponent (int, default: 2)"); general.add_options()("placer-heap-timingweight", po::value(), "placer heap timing weight (int, default: 10)"); + general.add_options()("parallel-refine", "use new experimental parallelised engine for placement refinement"); + general.add_options()("router2-heatmap", po::value(), "prefix for router2 resource congestion heatmaps"); @@ -225,6 +228,10 @@ void CommandHandler::setupContext(Context *ctx) ctx->rngseed(vm["seed"].as()); } + if (vm.count("threads")) { + ctx->settings[ctx->id("threads")] = vm["threads"].as(); + } + if (vm.count("randomize-seed")) { std::random_device randDev{}; std::uniform_int_distribution distrib{1}; @@ -298,6 +305,10 @@ void CommandHandler::setupContext(Context *ctx) if (vm.count("placer-heap-timingweight")) ctx->settings[ctx->id("placerHeap/timingWeight")] = std::to_string(vm["placer-heap-timingweight"].as()); + + if (vm.count("parallel-refine")) + ctx->settings[ctx->id("placerHeap/parallelRefine")] = true; + if (vm.count("router2-heatmap")) ctx->settings[ctx->id("router2/heatmap")] = vm["router2-heatmap"].as(); if (vm.count("tmg-ripup") || vm.count("router2-tmg-ripup")) diff --git a/common/parallel_refine.cc b/common/parallel_refine.cc new file mode 100644 index 0000000000..ea97b8521a --- /dev/null +++ b/common/parallel_refine.cc @@ -0,0 +1,946 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 "parallel_refine.h" +#include "fast_bels.h" +#include "log.h" +#include "timing.h" +#include "scope_lock.h" + +#include +#include +#include +#include +#include + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct Partition +{ + int x0, y0, x1, y1; + std::vector cells; + Partition() = default; + explicit Partition(Context *ctx) + { + x0 = ctx->getGridDimX(); + y0 = ctx->getGridDimY(); + x1 = 0; + y1 = 0; + for (auto &cell : ctx->cells) { + Loc l = ctx->getBelLocation(cell.second->bel); + x0 = std::min(x0, l.x); + x1 = std::max(x1, l.x); + y0 = std::min(y0, l.y); + y1 = std::max(y1, l.y); + cells.push_back(cell.second.get()); + } + } + void split(Context *ctx, bool yaxis, float pivot, Partition &l, Partition &r) + { + std::sort(cells.begin(), cells.end(), [&](CellInfo *a, CellInfo *b) { + Loc l0 = ctx->getBelLocation(a->bel), l1 = ctx->getBelLocation(b->bel); + return yaxis ? (l0.y < l1.y) : (l0.x < l1.x); + }); + size_t pivot_point = size_t(cells.size() * pivot); + l.cells.clear(); + r.cells.clear(); + l.cells.reserve(pivot_point); + r.cells.reserve(cells.size() - pivot_point); + int pivot_coord = (pivot_point == 0) ? (yaxis ? y1 : x1) + : (yaxis ? ctx->getBelLocation(cells.at(pivot_point - 1)->bel).y + : ctx->getBelLocation(cells.at(pivot_point - 1)->bel).x); + for (size_t i = 0; i < cells.size(); i++) { + Loc loc = ctx->getBelLocation(cells.at(i)->bel); + ((yaxis ? loc.y : loc.x) <= pivot_coord ? l.cells : r.cells).push_back(cells.at(i)); + } + if (yaxis) { + l.x0 = r.x0 = x0; + l.x1 = r.x1 = x1; + l.y0 = y0; + l.y1 = pivot_coord; + r.y0 = (pivot_coord == y1) ? y1 : (pivot_coord + 1); + r.y1 = y1; + } else { + l.y0 = r.y0 = y0; + l.y1 = r.y1 = y1; + l.x0 = x0; + l.x1 = pivot_coord; + r.x0 = (pivot_coord == x1) ? x1 : (pivot_coord + 1); + r.x1 = x1; + } + } +}; + +typedef int64_t wirelen_t; + +struct NetBB +{ + // Actual bounding box + int x0 = 0, x1 = 0, y0 = 0, y1 = 0; + // Number of cells at each extremity + int nx0 = 0, nx1 = 0, ny0 = 0, ny1 = 0; + wirelen_t hpwl(const ParallelRefineCfg &cfg) const + { + return wirelen_t(cfg.hpwl_scale_x * (x1 - x0) + cfg.hpwl_scale_y * (y1 - y0)); + } + static NetBB compute(const Context *ctx, const NetInfo *net, const dict *cell2bel = nullptr) + { + NetBB result{}; + if (!net->driver.cell) + return result; + auto bel_loc = [&](const CellInfo *cell) { + BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel; + return ctx->getBelLocation(bel); + }; + result.nx0 = result.nx1 = result.ny0 = result.ny1 = 1; + Loc drv_loc = bel_loc(net->driver.cell); + result.x0 = result.x1 = drv_loc.x; + result.y0 = result.y1 = drv_loc.y; + for (auto &usr : net->users) { + Loc l = bel_loc(usr.cell); + if (l.x == result.x0) + ++result.nx0; // on the edge + else if (l.x < result.x0) { + result.x0 = l.x; // extends the edge + result.nx0 = 1; + } + if (l.x == result.x1) + ++result.nx1; // on the edge + else if (l.x > result.x1) { + result.x1 = l.x; // extends the edge + result.nx1 = 1; + } + if (l.y == result.y0) + ++result.ny0; // on the edge + else if (l.y < result.y0) { + result.y0 = l.y; // extends the edge + result.ny0 = 1; + } + if (l.y == result.y1) + ++result.ny1; // on the edge + else if (l.y > result.y1) { + result.y1 = l.y; // extends the edge + result.ny1 = 1; + } + } + return result; + } +}; + +struct GlobalState +{ + explicit GlobalState(Context *ctx, ParallelRefineCfg cfg) : ctx(ctx), cfg(cfg), bels(ctx, false, 64), tmg(ctx){}; + Context *ctx; + ParallelRefineCfg cfg; + FastBels bels; + std::vector flat_nets; // flat array of all nets in the design for fast referencing by index + std::vector last_bounds; + std::vector> last_tmg_costs; + dict region_bounds; + TimingAnalyser tmg; + + std::shared_timed_mutex archapi_mutex; + + double temperature = 1e-7; + int radius = 3; + + wirelen_t total_wirelen = 0; + double total_timing_cost = 0; + + double get_timing_cost(const NetInfo *net, store_index user, + const dict *cell2bel = nullptr) + { + if (!net->driver.cell) + return 0; + const auto &sink = net->users.at(user); + IdString driver_pin, sink_pin; + // Pick the first pin for a prediction; assume all will be similar enouhg + for (auto pin : ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port)) { + driver_pin = pin; + break; + } + for (auto pin : ctx->getBelPinsForCellPin(sink.cell, sink.port)) { + sink_pin = pin; + break; + } + float crit = tmg.get_criticality(CellPortKey(sink)); + BelId src_bel = cell2bel ? cell2bel->at(net->driver.cell->name) : net->driver.cell->bel; + BelId dst_bel = cell2bel ? cell2bel->at(sink.cell->name) : sink.cell->bel; + double delay = ctx->getDelayNS(ctx->predictDelay(src_bel, driver_pin, dst_bel, sink_pin)); + return delay * std::pow(crit, cfg.crit_exp); + } + + bool skip_net(const NetInfo *net) const + { + if (!net->driver.cell) + return true; + if (ctx->getBelGlobalBuf(net->driver.cell->bel)) + return true; + return false; + } + bool timing_skip_net(const NetInfo *net) const + { + if (!net->driver.cell) + return true; + int cc; + auto cls = ctx->getPortTimingClass(net->driver.cell, net->driver.port, cc); + if (cls == TMG_IGNORE || cls == TMG_GEN_CLOCK) + return true; + return false; + } + // .... +}; + +struct ThreadState +{ + Context *ctx; // Nextpnr context pointer + GlobalState &g; // Refinement engine state + int idx; // Index of the thread + DeterministicRNG rng; // Local RNG + // The cell partition that the thread works on + Partition p; + // Mapping from design-wide net index to thread-wide net index -- not all nets are in all partitions, so we can + // optimise + std::vector thread_net_idx; + // List of nets inside the partition; and their committed bounding boxes & timing costs from the thread's + // perspective + std::vector thread_nets; + std::vector net_bounds; + std::vector> arc_tmg_cost; + std::vector ignored_nets, tmg_ignored_nets; + bool arch_state_dirty = false; + // Our local cell-bel map; that won't be affected by out-of-partition moves + dict local_cell2bel; + + // Data on an inflight move + dict> moved_cells; // cell -> (old; new) + // For cluster moves only + std::vector> cell_rel; + // For incremental wirelength and delay updates + wirelen_t wirelen_delta = 0; + double timing_delta = 0; + + // Total made and accepted moved + int n_move = 0, n_accept = 0; + + enum BoundChange + { + NO_CHANGE, + CELL_MOVED_INWARDS, + CELL_MOVED_OUTWARDS, + FULL_RECOMPUTE + }; + // Wirelen related are handled on a per-axis basis to reduce + struct AxisChanges + { + std::vector bounds_changed_nets; + std::vector already_bounds_changed; + }; + std::array axes; + std::vector new_net_bounds; + + std::vector> already_timing_changed; + std::vector>> timing_changed_arcs; + std::vector new_timing_costs; + + ThreadState(Context *ctx, GlobalState &g, int idx) : ctx(ctx), g(g), idx(idx){}; + void set_partition(const Partition &part) + { + p = part; + thread_nets.clear(); + thread_net_idx.resize(g.flat_nets.size()); + std::fill(thread_net_idx.begin(), thread_net_idx.end(), -1); + // Determine the set of nets that are within the thread; and therefore we care about + for (auto thread_cell : part.cells) { + for (auto &port : thread_cell->ports) { + if (!port.second.net) + continue; + int global_idx = port.second.net->udata; + auto &thread_idx = thread_net_idx.at(global_idx); + // Already added to the set + if (thread_idx != -1) + continue; + thread_idx = thread_nets.size(); + thread_nets.push_back(port.second.net); + } + } + tmg_ignored_nets.clear(); + ignored_nets.clear(); + for (auto tn : thread_nets) { + ignored_nets.push_back(g.skip_net(tn)); + tmg_ignored_nets.push_back(g.timing_skip_net(tn)); + } + // Set up the original cell-bel map for all nets inside the thread + local_cell2bel.clear(); + for (NetInfo *net : thread_nets) { + if (net->driver.cell) + local_cell2bel[net->driver.cell->name] = net->driver.cell->bel; + for (auto &usr : net->users) + local_cell2bel[usr.cell->name] = usr.cell->bel; + } + } + void setup_initial_state() + { + // Setup initial net bounding boxes and timing costs + net_bounds.clear(); + arc_tmg_cost.clear(); + for (auto tn : thread_nets) { + net_bounds.push_back(g.last_bounds.at(tn->udata)); + arc_tmg_cost.push_back(g.last_tmg_costs.at(tn->udata)); + } + new_net_bounds = net_bounds; + for (int j = 0; j < 2; j++) { + auto &a = axes.at(j); + a.already_bounds_changed.resize(net_bounds.size()); + } + already_timing_changed.clear(); + already_timing_changed.resize(net_bounds.size()); + for (size_t i = 0; i < thread_nets.size(); i++) + already_timing_changed.at(i) = std::vector(thread_nets.at(i)->users.capacity()); + } + bool bounds_check(BelId bel) + { + Loc l = ctx->getBelLocation(bel); + if (l.x < p.x0 || l.x > p.x1 || l.y < p.y0 || l.y > p.y1) + return false; + return true; + } + bool bind_move() + { + std::unique_lock l(g.archapi_mutex); + for (auto &entry : moved_cells) { + ctx->unbindBel(entry.second.first); + } + bool success = true; + for (auto &entry : moved_cells) { + // Make sure targets are available before we bind them + if (!ctx->checkBelAvail(entry.second.second)) { + success = false; + break; + } + ctx->bindBel(entry.second.second, ctx->cells.at(entry.first).get(), STRENGTH_WEAK); + } + arch_state_dirty = true; + return success; + } + bool check_validity() + { + std::shared_lock l(g.archapi_mutex); + bool result = true; + for (auto e : moved_cells) { + if (!ctx->isBelLocationValid(e.second.first)) { + // Have to check old; too; as unbinding a bel could make a placement illegal by virtue of no longer + // enabling dedicated routes to be used + result = false; + break; + } + if (!ctx->isBelLocationValid(e.second.second)) { + result = false; + break; + } + } + return result; + } + void revert_move() + { + if (arch_state_dirty) { + // If changes to the arch state were made, revert them by restoring original cell bindings + std::unique_lock l(g.archapi_mutex); + for (auto &entry : moved_cells) { + BelId curr_bound = ctx->cells.at(entry.first)->bel; + if (curr_bound != BelId()) + ctx->unbindBel(curr_bound); + } + for (auto &entry : moved_cells) { + ctx->bindBel(entry.second.first, ctx->cells.at(entry.first).get(), STRENGTH_WEAK); + } + arch_state_dirty = false; + } + for (auto &entry : moved_cells) + local_cell2bel[entry.first] = entry.second.first; + } + void commit_move() + { + arch_state_dirty = false; + for (auto &axis : axes) { + for (auto bc : axis.bounds_changed_nets) { + // Commit updated net bounds + net_bounds.at(bc) = new_net_bounds.at(bc); + } + } + if (g.cfg.timing_driven) { + NPNR_ASSERT(timing_changed_arcs.size() == new_timing_costs.size()); + for (size_t i = 0; i < timing_changed_arcs.size(); i++) { + auto arc = timing_changed_arcs.at(i); + arc_tmg_cost.at(arc.first).at(arc.second.idx()) = new_timing_costs.at(i); + } + } + } + void compute_changes_for_cell(CellInfo *cell, BelId old_bel, BelId new_bel) + { + Loc new_loc = ctx->getBelLocation(new_bel); + Loc old_loc = ctx->getBelLocation(old_bel); + for (const auto &port : cell->ports) { + NetInfo *pn = port.second.net; + if (!pn) + continue; + int idx = thread_net_idx.at(pn->udata); + if (ignored_nets.at(idx)) + continue; + NetBB &new_bounds = new_net_bounds.at(idx); + // For the x-axis (i=0) and y-axis (i=1) + for (int i = 0; i < 2; i++) { + auto &axis = axes.at(i); + // New and old on this axis + int new_pos = i ? new_loc.y : new_loc.x, old_pos = i ? old_loc.y : old_loc.x; + // References to updated bounding box entries + auto &b0 = i ? new_bounds.y0 : new_bounds.x0; + auto &n0 = i ? new_bounds.ny0 : new_bounds.nx0; + auto &b1 = i ? new_bounds.y1 : new_bounds.x1; + auto &n1 = i ? new_bounds.ny1 : new_bounds.nx1; + auto &change = axis.already_bounds_changed.at(idx); + // Lower bound + if (new_pos < b0) { + // Further out than current lower bound + b0 = new_pos; + n0 = 1; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (new_pos == b0 && old_pos > b0) { + // Moved from inside into current bound + ++n0; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (old_pos == b0 && new_pos > b0) { + // Moved from current bound to inside + if (change == NO_CHANGE) + axis.bounds_changed_nets.push_back(idx); + if (n0 == 1) { + // Was the last cell on the bound; have to do a full recompute + change = FULL_RECOMPUTE; + } else { + --n0; + if (change == NO_CHANGE) + change = CELL_MOVED_INWARDS; + } + } + // Upper bound + if (new_pos > b1) { + // Further out than current upper bound + b1 = new_pos; + n1 = new_pos; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (new_pos == b1 && old_pos < b1) { + // Moved onto current bound + ++n1; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (old_pos == b1 && new_pos < b1) { + // Moved from current bound to inside + if (change == NO_CHANGE) + axis.bounds_changed_nets.push_back(idx); + if (n1 == 1) { + // Was the last cell on the bound; have to do a full recompute + change = FULL_RECOMPUTE; + } else { + --n1; + if (change == NO_CHANGE) + change = CELL_MOVED_INWARDS; + } + } + } + // Timing updates if timing driven + if (g.cfg.timing_driven && !tmg_ignored_nets.at(idx)) { + if (port.second.type == PORT_OUT) { + int cc; + TimingPortClass cls = ctx->getPortTimingClass(cell, port.first, cc); + if (cls != TMG_IGNORE) { + for (auto usr : pn->users.enumerate()) + if (!already_timing_changed.at(idx).at(usr.index.idx())) { + timing_changed_arcs.emplace_back(std::make_pair(idx, usr.index)); + already_timing_changed.at(idx).at(usr.index.idx()) = true; + } + } + } else { + auto usr = port.second.user_idx; + if (!already_timing_changed.at(idx).at(usr.idx())) { + timing_changed_arcs.emplace_back(std::make_pair(idx, usr)); + already_timing_changed.at(idx).at(usr.idx()) = true; + } + } + } + } + } + void compute_total_change() + { + auto &xa = axes.at(0), &ya = axes.at(1); + for (auto &bc : xa.bounds_changed_nets) + if (xa.already_bounds_changed.at(bc) == FULL_RECOMPUTE) + new_net_bounds.at(bc) = NetBB::compute(ctx, thread_nets.at(bc), &local_cell2bel); + for (auto &bc : ya.bounds_changed_nets) + if (xa.already_bounds_changed.at(bc) != FULL_RECOMPUTE && + ya.already_bounds_changed.at(bc) == FULL_RECOMPUTE) + new_net_bounds.at(bc) = NetBB::compute(ctx, thread_nets.at(bc), &local_cell2bel); + for (auto &bc : xa.bounds_changed_nets) + wirelen_delta += (new_net_bounds.at(bc).hpwl(g.cfg) - net_bounds.at(bc).hpwl(g.cfg)); + for (auto &bc : ya.bounds_changed_nets) + if (xa.already_bounds_changed.at(bc) == NO_CHANGE) + wirelen_delta += (new_net_bounds.at(bc).hpwl(g.cfg) - net_bounds.at(bc).hpwl(g.cfg)); + if (g.cfg.timing_driven) { + NPNR_ASSERT(new_timing_costs.empty()); + for (auto arc : timing_changed_arcs) { + double new_cost = g.get_timing_cost(thread_nets.at(arc.first), arc.second, &local_cell2bel); + timing_delta += (new_cost - arc_tmg_cost.at(arc.first).at(arc.second.idx())); + new_timing_costs.push_back(new_cost); + } + } + } + void reset_move_state() + { + moved_cells.clear(); + cell_rel.clear(); + for (auto &axis : axes) { + for (auto bc : axis.bounds_changed_nets) { + new_net_bounds.at(bc) = net_bounds.at(bc); + axis.already_bounds_changed[bc] = NO_CHANGE; + } + axis.bounds_changed_nets.clear(); + } + for (auto &arc : timing_changed_arcs) { + already_timing_changed.at(arc.first).at(arc.second.idx()) = false; + } + timing_changed_arcs.clear(); + new_timing_costs.clear(); + wirelen_delta = 0; + timing_delta = 0; + } + + bool accept_move() + { + double delta = g.cfg.lambda * (timing_delta / std::max(epsilon, g.total_timing_cost)) + + (1.0 - g.cfg.lambda) * (double(wirelen_delta) / std::max(epsilon, g.total_wirelen)); + return delta < 0 || + (g.temperature > 1e-8 && (rng.rng() / float(0x3fffffff)) <= std::exp(-delta / g.temperature)); + } + + bool add_to_move(CellInfo *cell, BelId old_bel, BelId new_bel) + { + if (!bounds_check(old_bel) || !bounds_check(new_bel)) + return false; + if (!ctx->isValidBelForCellType(cell->type, new_bel)) + return false; + NPNR_ASSERT(!moved_cells.count(cell->name)); + moved_cells[cell->name] = std::make_pair(old_bel, new_bel); + local_cell2bel[cell->name] = new_bel; + compute_changes_for_cell(cell, old_bel, new_bel); + return true; + } + + static constexpr double epsilon = 1e-20; + bool single_cell_swap(CellInfo *cell, BelId new_bel) + { + NPNR_ASSERT(moved_cells.empty()); + BelId old_bel = cell->bel; + CellInfo *bound = ctx->getBoundBelCell(new_bel); + if (bound && (bound->belStrength > STRENGTH_STRONG || bound->cluster != ClusterId())) + return false; + if (!add_to_move(cell, old_bel, new_bel)) + goto fail; + if (bound && !add_to_move(bound, new_bel, old_bel)) + goto fail; + compute_total_change(); + // SA acceptance criteria + + if (!accept_move()) { + // SA fail + goto fail; + } + // Check validity rules + if (!bind_move()) + goto fail; + if (!check_validity()) + goto fail; + // Accepted! + commit_move(); + reset_move_state(); + return true; + fail: + revert_move(); + reset_move_state(); + return false; + } + + bool chain_swap(CellInfo *root_cell, BelId new_root_bel) + { + NPNR_ASSERT(moved_cells.empty()); + std::queue> displaced_clusters; + pool used_bels; + displaced_clusters.emplace(root_cell->cluster, new_root_bel); + while (!displaced_clusters.empty()) { + std::vector> dest_bels; + auto cursor = displaced_clusters.front(); + displaced_clusters.pop(); + if (!ctx->getClusterPlacement(cursor.first, cursor.second, dest_bels)) + goto fail; + for (const auto &db : dest_bels) { + BelId old_bel = db.first->bel; + if (moved_cells.count(db.first->name)) + goto fail; + if (!add_to_move(db.first, old_bel, db.second)) + goto fail; + if (used_bels.count(db.second)) + goto fail; + used_bels.insert(db.second); + + CellInfo *bound = ctx->getBoundBelCell(db.second); + if (bound) { + if (moved_cells.count(bound->name)) { + // Don't move a cell multiple times in the same go + goto fail; + } else if (bound->belStrength > STRENGTH_STRONG) { + goto fail; + } else if (bound->cluster != ClusterId()) { + // Displace the entire cluster + Loc old_loc = ctx->getBelLocation(old_bel); + Loc bound_loc = ctx->getBelLocation(bound->bel); + Loc root_loc = ctx->getBelLocation(ctx->getClusterRootCell(bound->cluster)->bel); + BelId new_root = ctx->getBelByLocation(Loc(old_loc.x + (root_loc.x - bound_loc.x), + old_loc.y + (root_loc.y - bound_loc.y), + old_loc.z + (root_loc.z - bound_loc.z))); + if (new_root == BelId()) + goto fail; + displaced_clusters.emplace(bound->cluster, new_root); + } else { + // Single cell swap + if (used_bels.count(old_bel)) + goto fail; + used_bels.insert(old_bel); + if (!add_to_move(bound, bound->bel, old_bel)) + goto fail; + } + } else if (!ctx->checkBelAvail(db.second)) { + goto fail; + } + } + } + compute_total_change(); + // SA acceptance criteria + + if (!accept_move()) { + // SA fail + goto fail; + } + // Check validity rules + if (!bind_move()) + goto fail; + if (!check_validity()) + goto fail; + // Accepted! + commit_move(); + reset_move_state(); + return true; + fail: + revert_move(); + reset_move_state(); + return false; + } + + BelId random_bel_for_cell(CellInfo *cell, int force_z = -1) + { + IdString targetType = cell->type; + Loc curr_loc = ctx->getBelLocation(cell->bel); + int count = 0; + + int dx = g.radius, dy = g.radius; + if (cell->region != nullptr && cell->region->constr_bels) { + dx = std::min(g.cfg.hpwl_scale_x * g.radius, + (g.region_bounds[cell->region->name].x1 - g.region_bounds[cell->region->name].x0) + 1); + dy = std::min(g.cfg.hpwl_scale_y * g.radius, + (g.region_bounds[cell->region->name].y1 - g.region_bounds[cell->region->name].y0) + 1); + // Clamp location to within bounds + curr_loc.x = std::max(g.region_bounds[cell->region->name].x0, curr_loc.x); + curr_loc.x = std::min(g.region_bounds[cell->region->name].x1, curr_loc.x); + curr_loc.y = std::max(g.region_bounds[cell->region->name].y0, curr_loc.y); + curr_loc.y = std::min(g.region_bounds[cell->region->name].y1, curr_loc.y); + } + + FastBels::FastBelsData *bel_data; + auto type_cnt = g.bels.getBelsForCellType(targetType, &bel_data); + + while (true) { + int nx = rng.rng(2 * dx + 1) + std::max(curr_loc.x - dx, 0); + int ny = rng.rng(2 * dy + 1) + std::max(curr_loc.y - dy, 0); + if (type_cnt < 64) + nx = ny = 0; + if (nx >= int(bel_data->size())) + continue; + if (ny >= int(bel_data->at(nx).size())) + continue; + const auto &fb = bel_data->at(nx).at(ny); + if (fb.size() == 0) + continue; + BelId bel = fb.at(rng.rng(int(fb.size()))); + if (!bounds_check(bel)) + continue; + if (force_z != -1) { + Loc loc = ctx->getBelLocation(bel); + if (loc.z != force_z) + continue; + } + if (!cell->testRegion(bel)) + continue; + count++; + return bel; + } + } + + void run_iter() + { + setup_initial_state(); + n_accept = 0; + n_move = 0; + for (int m = 0; m < g.cfg.inner_iters; m++) { + for (auto cell : p.cells) { + if (cell->belStrength > STRENGTH_STRONG) + continue; + if (cell->cluster != ClusterId()) { + if (cell != ctx->getClusterRootCell(cell->cluster)) + continue; // only move cluster root + Loc old_loc = ctx->getBelLocation(cell->bel); + BelId new_root = random_bel_for_cell(cell, old_loc.z); + if (new_root == BelId() || new_root == cell->bel) + continue; + ++n_move; + if (chain_swap(cell, new_root)) + ++n_accept; + } else { + BelId new_bel = random_bel_for_cell(cell); + if (new_bel == BelId() || new_bel == cell->bel) + continue; + ++n_move; + if (single_cell_swap(cell, new_bel)) + ++n_accept; + } + } + } + } +}; + +struct ParallelRefine +{ + Context *ctx; + GlobalState g; + std::vector t; + ParallelRefine(Context *ctx, ParallelRefineCfg cfg) : ctx(ctx), g(ctx, cfg) + { + g.flat_nets.reserve(ctx->nets.size()); + for (auto &net : ctx->nets) { + net.second->udata = g.flat_nets.size(); + g.flat_nets.push_back(net.second.get()); + } + // Setup per thread context + for (int i = 0; i < cfg.threads; i++) { + t.emplace_back(ctx, g, i); + } + // Setup region bounds + for (auto ®ion : ctx->region) { + Region *r = region.second.get(); + NetBB bb; + if (r->constr_bels) { + bb.x0 = std::numeric_limits::max(); + bb.x1 = std::numeric_limits::min(); + bb.y0 = std::numeric_limits::max(); + bb.y1 = std::numeric_limits::min(); + for (auto bel : r->bels) { + Loc loc = ctx->getBelLocation(bel); + bb.x0 = std::min(bb.x0, loc.x); + bb.x1 = std::max(bb.x1, loc.x); + bb.y0 = std::min(bb.y0, loc.y); + bb.y1 = std::max(bb.y1, loc.y); + } + } else { + bb.x0 = 0; + bb.y0 = 0; + bb.x1 = ctx->getGridDimX(); + bb.y1 = ctx->getGridDimY(); + } + g.region_bounds[r->name] = bb; + } + // Setup fast bels map + pool cell_types_in_use; + for (auto &cell : ctx->cells) { + IdString cell_type = cell.second->type; + cell_types_in_use.insert(cell_type); + } + + for (auto cell_type : cell_types_in_use) { + g.bels.addCellType(cell_type); + } + }; + std::vector parts; + void do_partition() + { + parts.clear(); + parts.emplace_back(ctx); + bool yaxis = false; + while (parts.size() < t.size()) { + std::vector next(parts.size() * 2); + for (size_t i = 0; i < parts.size(); i++) { + // Randomly permute pivot every iteration so we get different thread boundaries + const float delta = 0.1; + float pivot = (0.5 - (delta / 2)) + (delta / 2) * (ctx->rng(10000) / 10000.0f); + parts.at(i).split(ctx, yaxis, pivot, next.at(i * 2), next.at(i * 2 + 1)); + } + std::swap(parts, next); + yaxis = !yaxis; + } + + NPNR_ASSERT(parts.size() == t.size()); + // TODO: thread pool to make this worthwhile... + std::vector workers; + for (size_t i = 0; i < t.size(); i++) { + workers.emplace_back([i, this]() { t.at(i).set_partition(parts.at(i)); }); + } + for (auto &w : workers) + w.join(); + } + + void update_global_costs() + { + g.last_bounds.resize(g.flat_nets.size()); + g.last_tmg_costs.resize(g.flat_nets.size()); + g.total_wirelen = 0; + g.total_timing_cost = 0; + for (size_t i = 0; i < g.flat_nets.size(); i++) { + NetInfo *ni = g.flat_nets.at(i); + if (g.skip_net(ni)) + continue; + g.last_bounds.at(i) = NetBB::compute(ctx, ni); + g.total_wirelen += g.last_bounds.at(i).hpwl(g.cfg); + if (!g.timing_skip_net(ni)) { + auto &tc = g.last_tmg_costs.at(i); + tc.resize(ni->users.capacity()); + for (auto usr : ni->users.enumerate()) { + tc.at(usr.index.idx()) = g.get_timing_cost(ni, usr.index); + g.total_timing_cost += tc.at(usr.index.idx()); + } + } + } + } + void run() + { + + ScopeLock lock(ctx); + auto refine_start = std::chrono::high_resolution_clock::now(); + + g.tmg.setup_only = true; + g.tmg.setup(); + do_partition(); + log_info("Running parallel refinement with %d threads.\n", int(t.size())); + int iter = 1; + bool done = false; + update_global_costs(); + double avg_wirelen = g.total_wirelen; + wirelen_t min_wirelen = g.total_wirelen; + while (true) { + if (iter > 1) { + if (g.total_wirelen >= min_wirelen) { + done = true; + } else if (g.total_wirelen < min_wirelen) { + min_wirelen = g.total_wirelen; + } + int n_accept = 0, n_move = 0; + for (auto &t_data : t) { + n_accept += t_data.n_accept; + n_move += t_data.n_move; + } + double r_accept = n_accept / double(n_move); + if (g.total_wirelen < (0.95 * avg_wirelen) && g.total_wirelen > 0) { + avg_wirelen = 0.8 * avg_wirelen + 0.2 * g.total_wirelen; + } else { + if (r_accept > 0.15 && g.radius > 1) { + g.temperature *= 0.95; + } else { + g.temperature *= 0.8; + } + } + if ((iter % 10) == 0 && g.radius > 1) + --g.radius; + } + + if ((iter == 1) || ((iter % 5) == 0) || done) + log_info(" at iteration #%d: temp = %f, timing cost = " + "%.0f, wirelen = %.0f\n", + iter, g.temperature, double(g.total_timing_cost), double(g.total_wirelen)); + + if (done) + break; + + do_partition(); + + std::vector workers; + workers.reserve(t.size()); + for (int j = 0; j < int(t.size()); j++) + workers.emplace_back([this, j]() { t.at(j).run_iter(); }); + for (auto &w : workers) + w.join(); + g.tmg.run(); + update_global_costs(); + iter++; + ctx->yield(); + } + auto refine_end = std::chrono::high_resolution_clock::now(); + log_info("Placement refine time %.02fs\n", std::chrono::duration(refine_end - refine_start).count()); + } +}; +} // namespace + +ParallelRefineCfg::ParallelRefineCfg(Context *ctx) +{ + timing_driven = ctx->setting("timing_driven"); + threads = ctx->setting("threads", 8); + // snap to nearest power of two; and minimum thread size + int actual_threads = 1; + while ((actual_threads * 2) <= threads && (int(ctx->cells.size()) / (actual_threads * 2)) >= min_thread_size) + actual_threads *= 2; + threads = actual_threads; + hpwl_scale_x = 1; + hpwl_scale_y = 1; +} + +bool parallel_refine(Context *ctx, ParallelRefineCfg cfg) +{ + // TODO + ParallelRefine refine(ctx, cfg); + refine.run(); + timing_analysis(ctx); + return true; +} + +NEXTPNR_NAMESPACE_END diff --git a/common/parallel_refine.h b/common/parallel_refine.h new file mode 100644 index 0000000000..556317cd19 --- /dev/null +++ b/common/parallel_refine.h @@ -0,0 +1,43 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 gatecat + * + * 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 PARALLEL_REFINE_H +#define PARALLEL_REFINE_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct ParallelRefineCfg +{ + ParallelRefineCfg(Context *ctx); + bool timing_driven; + int threads; + int hpwl_scale_x, hpwl_scale_y; + double lambda = 0.5f; + float crit_exp = 8; + int inner_iters = 15; + int min_thread_size = 500; +}; + +bool parallel_refine(Context *ctx, ParallelRefineCfg cfg); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/common/placer_heap.cc b/common/placer_heap.cc index 5b43dc7257..5ffb764254 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -46,6 +46,7 @@ #include "fast_bels.h" #include "log.h" #include "nextpnr.h" +#include "parallel_refine.h" #include "place_common.h" #include "placer1.h" #include "scope_lock.h" @@ -346,8 +347,14 @@ class HeAPPlacer ctx->check(); lock.unlock_early(); - if (!placer1_refine(ctx, Placer1Cfg(ctx))) { - return false; + if (cfg.parallelRefine) { + if (!parallel_refine(ctx, ParallelRefineCfg(ctx))) { + return false; + } + } else { + if (!placer1_refine(ctx, Placer1Cfg(ctx))) { + return false; + } } return true; @@ -1786,6 +1793,8 @@ PlacerHeapCfg::PlacerHeapCfg(Context *ctx) beta = ctx->setting("placerHeap/beta"); criticalityExponent = ctx->setting("placerHeap/criticalityExponent"); timingWeight = ctx->setting("placerHeap/timingWeight"); + parallelRefine = ctx->setting("placerHeap/parallelRefine", false); + timing_driven = ctx->setting("timing_driven"); solverTolerance = 1e-5; placeAllAtOnce = false; diff --git a/common/placer_heap.h b/common/placer_heap.h index bb40a12669..9c62869e9d 100644 --- a/common/placer_heap.h +++ b/common/placer_heap.h @@ -41,6 +41,7 @@ struct PlacerHeapCfg bool timing_driven; float solverTolerance; bool placeAllAtOnce; + bool parallelRefine; int hpwl_scale_x, hpwl_scale_y; int spread_scale_x, spread_scale_y; From c93a3f35aca4399c290840f150f713aa81a515bb Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 4 Mar 2022 16:19:16 +1000 Subject: [PATCH 078/712] gowin: BUGFIX gui crash Signed-off-by: YRabbit --- gowin/arch.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index af85146799..e6ee826720 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -209,6 +209,16 @@ void Arch::addWire(IdString name, IdString type, int x, int y) wi.y = y; wire_ids.push_back(name); + + // Needed to ensure empty tile bel locations + if (int(bels_by_tile.size()) <= x) + bels_by_tile.resize(x + 1); + if (int(bels_by_tile[x].size()) <= y) + bels_by_tile[x].resize(y + 1); + if (int(tileBelDimZ.size()) <= x) + tileBelDimZ.resize(x + 1); + if (int(tileBelDimZ[x].size()) <= y) + tileBelDimZ[x].resize(y + 1); } void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayQuad delay, Loc loc) From b5d6fc8ed7bc446b1d810c82029e7b327bea5049 Mon Sep 17 00:00:00 2001 From: Alessandro Comodi Date: Fri, 4 Mar 2022 16:53:03 +0100 Subject: [PATCH 079/712] interchange: lut map cache: remove hardcoded values Signed-off-by: Alessandro Comodi --- fpga_interchange/arch.cc | 9 +++++++++ fpga_interchange/arch.h | 5 +++++ fpga_interchange/site_lut_mapping_cache.cc | 7 ++++--- fpga_interchange/site_lut_mapping_cache.h | 12 +++--------- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index a5e802d303..e55c94afe9 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -224,10 +224,14 @@ Arch::Arch(ArchArgs args) : args(args), disallow_site_routing(false) // Initially LutElement vectors for each tile type. tile_type_index = 0; + max_lut_cells = 0; + max_lut_pins = 0; lut_elements.resize(chip_info->tile_types.size()); for (const TileTypeInfoPOD &tile_type : chip_info->tile_types) { std::vector &elements = lut_elements[tile_type_index++]; elements.reserve(tile_type.lut_elements.size()); + + int lut_cells_count = 0; for (auto &lut_element : tile_type.lut_elements) { elements.emplace_back(); @@ -252,10 +256,15 @@ Arch::Arch(ArchArgs args) : args(args), disallow_site_routing(false) } lut.output_pin = IdString(lut_bel.out_pin); + lut_cells_count++; + + max_lut_pins = std::max((int)lut_bel.pins.size(), max_lut_pins); } element.compute_pin_order(); } + + max_lut_cells = std::max(lut_cells_count, max_lut_cells); } // Map lut cell types to their LutCellPOD diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 8bb2e2d149..7887745283 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -1134,6 +1134,11 @@ struct Arch : ArchAPI IdString vcc_cell_pin; std::vector> lut_elements; dict lut_cells; + + // Defines the max number of LUT cells in a site and LUT pins + // to allow a correct functioning of the site lut mapping cache + int max_lut_cells; + int max_lut_pins; // Of the LUT cells, which is used for wires? // Note: May be null in arch's without wire LUT types. Assumption is diff --git a/fpga_interchange/site_lut_mapping_cache.cc b/fpga_interchange/site_lut_mapping_cache.cc index 0cf741f280..b7a71397c9 100644 --- a/fpga_interchange/site_lut_mapping_cache.cc +++ b/fpga_interchange/site_lut_mapping_cache.cc @@ -58,6 +58,7 @@ SiteLutMappingKey SiteLutMappingKey::create(const SiteInformation &siteInfo) key.tileType = siteInfo.tile_type; key.siteType = ctx->chip_info->sites[siteInfo.site].site_type; key.numCells = 0; + key.cells.resize(ctx->max_lut_cells); // Get bound nets. Store localized (to the LUT cluster) net indices only // to get always the same key for the same LUT port configuration even @@ -65,13 +66,13 @@ SiteLutMappingKey SiteLutMappingKey::create(const SiteInformation &siteInfo) dict netMap; for (CellInfo *cellInfo : lutCells) { - NPNR_ASSERT(key.numCells < SiteLutMappingKey::MAX_LUT_CELLS); + NPNR_ASSERT(key.numCells < key.cells.size()); auto &cell = key.cells[key.numCells++]; cell.type = cellInfo->type; cell.belIndex = cellInfo->bel.index; - cell.conns.fill(0); + cell.conns.resize(ctx->max_lut_pins, 0); size_t portId = 0; for (const auto &port : cellInfo->ports) { @@ -96,7 +97,7 @@ SiteLutMappingKey SiteLutMappingKey::create(const SiteInformation &siteInfo) } } - NPNR_ASSERT(portId < SiteLutMappingKey::MAX_LUT_INPUTS); + NPNR_ASSERT(portId < cell.conns.size()); cell.conns[portId++] = netId; } } diff --git a/fpga_interchange/site_lut_mapping_cache.h b/fpga_interchange/site_lut_mapping_cache.h index 7b1d60a42f..3efd040f8d 100644 --- a/fpga_interchange/site_lut_mapping_cache.h +++ b/fpga_interchange/site_lut_mapping_cache.h @@ -29,12 +29,6 @@ NEXTPNR_NAMESPACE_BEGIN // Key structure used in site LUT mapping cache struct SiteLutMappingKey { - - // Maximum number of LUT cells per site - static constexpr size_t MAX_LUT_CELLS = 8; - // Maximum number of LUT inputs per cell - static constexpr size_t MAX_LUT_INPUTS = 6; - // LUT Cell data struct Cell { @@ -44,7 +38,7 @@ struct SiteLutMappingKey // Port to net assignments. These are local net ids generated during // key creation. This is to abstract connections from actual design // net names. the Id 0 means unconnected. - std::array conns; + std::vector conns; bool operator==(const Cell &other) const { @@ -60,7 +54,7 @@ struct SiteLutMappingKey int32_t tileType; // Tile type int32_t siteType; // Site type in that tile type size_t numCells; // LUT cell count - std::array cells; // LUT cell data + std::vector cells; // LUT cell data unsigned int hash_; // Precomputed hash @@ -80,7 +74,7 @@ struct SiteLutMappingKey const auto &cell = cells[j]; hash_ = mkhash(hash_, cell.type.index); hash_ = mkhash(hash_, cell.belIndex); - for (size_t i = 0; i < MAX_LUT_INPUTS; ++i) { + for (size_t i = 0; i < cell.conns.size(); ++i) { hash_ = mkhash(hash_, cell.conns[i]); } } From aee35768f4002c0a7d96ba79e5aa78632fb41c65 Mon Sep 17 00:00:00 2001 From: Catherine Date: Sat, 5 Mar 2022 16:32:44 +0000 Subject: [PATCH 080/712] Disable parallel refinement on WebAssembly. --- common/command.cc | 2 ++ common/parallel_refine.cc | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/common/command.cc b/common/command.cc index 5632784710..2167deb92b 100644 --- a/common/command.cc +++ b/common/command.cc @@ -174,7 +174,9 @@ po::options_description CommandHandler::getGeneralOptions() "placer heap criticality exponent (int, default: 2)"); general.add_options()("placer-heap-timingweight", po::value(), "placer heap timing weight (int, default: 10)"); +#if !defined(__wasm) general.add_options()("parallel-refine", "use new experimental parallelised engine for placement refinement"); +#endif general.add_options()("router2-heatmap", po::value(), "prefix for router2 resource congestion heatmaps"); diff --git a/common/parallel_refine.cc b/common/parallel_refine.cc index ea97b8521a..c2905480ca 100644 --- a/common/parallel_refine.cc +++ b/common/parallel_refine.cc @@ -18,8 +18,11 @@ */ #include "parallel_refine.h" -#include "fast_bels.h" #include "log.h" + +#if !defined(__wasm) + +#include "fast_bels.h" #include "timing.h" #include "scope_lock.h" @@ -944,3 +947,16 @@ bool parallel_refine(Context *ctx, ParallelRefineCfg cfg) } NEXTPNR_NAMESPACE_END + +#else /* !defined(__wasm) */ + +NEXTPNR_NAMESPACE_BEGIN + +bool parallel_refine(Context *ctx, ParallelRefineCfg cfg) +{ + log_abort(); +} + +NEXTPNR_NAMESPACE_END + +#endif From 626eccdb89884b9e4792bdf3c74f72cbb363e9b9 Mon Sep 17 00:00:00 2001 From: Catherine Date: Tue, 8 Mar 2022 17:24:29 +0000 Subject: [PATCH 081/712] Add missing part of commit aee35768. --- common/placer_heap.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/placer_heap.cc b/common/placer_heap.cc index 5ffb764254..4c9ffb2326 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -347,11 +347,14 @@ class HeAPPlacer ctx->check(); lock.unlock_early(); +#if !defined(__wasm) if (cfg.parallelRefine) { if (!parallel_refine(ctx, ParallelRefineCfg(ctx))) { return false; } - } else { + } else +#endif + { if (!placer1_refine(ctx, Placer1Cfg(ctx))) { return false; } From 191be632e23b26fa9eae484b04dde0ad5af35e39 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Wed, 9 Mar 2022 14:13:29 +0100 Subject: [PATCH 082/712] nexus: DCCs cannot be cascaded This commit solves implicit cascading when clock signal drives DCC and logic Signed-off-by: Maciej Dudek --- nexus/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index a769c05adc..bff50d8906 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -883,7 +883,7 @@ struct NexusPacker for (size_t i = 0; i < std::min(clk_fanout.size(), available_globals); i++) { NetInfo *net = ctx->nets.at(clk_fanout.at(i).second).get(); log_info(" promoting clock net '%s'\n", ctx->nameOf(net)); - insert_buffer(net, id_DCC, "glb_clk", id_CLKI, id_CLKO, [](const PortRef &port) { return true; }); + insert_buffer(net, id_DCC, "glb_clk", id_CLKI, id_CLKO, [&](const PortRef &port) { return port.cell->type != id_DCC; }); } } From df7e26c1aa9b8727bacf56ab0d592df5cef63bb5 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 9 Mar 2022 17:12:59 +0000 Subject: [PATCH 083/712] clangformat Signed-off-by: gatecat --- common/parallel_refine.cc | 7 ++----- fpga_interchange/arch.h | 2 +- fpga_interchange/site_lut_mapping_cache.h | 6 +++--- nexus/pack.cc | 3 ++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/common/parallel_refine.cc b/common/parallel_refine.cc index c2905480ca..bc665cd3ca 100644 --- a/common/parallel_refine.cc +++ b/common/parallel_refine.cc @@ -23,8 +23,8 @@ #if !defined(__wasm) #include "fast_bels.h" -#include "timing.h" #include "scope_lock.h" +#include "timing.h" #include #include @@ -952,10 +952,7 @@ NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_BEGIN -bool parallel_refine(Context *ctx, ParallelRefineCfg cfg) -{ - log_abort(); -} +bool parallel_refine(Context *ctx, ParallelRefineCfg cfg) { log_abort(); } NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 7887745283..789b188e65 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -1134,7 +1134,7 @@ struct Arch : ArchAPI IdString vcc_cell_pin; std::vector> lut_elements; dict lut_cells; - + // Defines the max number of LUT cells in a site and LUT pins // to allow a correct functioning of the site lut mapping cache int max_lut_cells; diff --git a/fpga_interchange/site_lut_mapping_cache.h b/fpga_interchange/site_lut_mapping_cache.h index 3efd040f8d..31e7e5e124 100644 --- a/fpga_interchange/site_lut_mapping_cache.h +++ b/fpga_interchange/site_lut_mapping_cache.h @@ -51,9 +51,9 @@ struct SiteLutMappingKey } }; - int32_t tileType; // Tile type - int32_t siteType; // Site type in that tile type - size_t numCells; // LUT cell count + int32_t tileType; // Tile type + int32_t siteType; // Site type in that tile type + size_t numCells; // LUT cell count std::vector cells; // LUT cell data unsigned int hash_; // Precomputed hash diff --git a/nexus/pack.cc b/nexus/pack.cc index bff50d8906..66d897a866 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -883,7 +883,8 @@ struct NexusPacker for (size_t i = 0; i < std::min(clk_fanout.size(), available_globals); i++) { NetInfo *net = ctx->nets.at(clk_fanout.at(i).second).get(); log_info(" promoting clock net '%s'\n", ctx->nameOf(net)); - insert_buffer(net, id_DCC, "glb_clk", id_CLKI, id_CLKO, [&](const PortRef &port) { return port.cell->type != id_DCC; }); + insert_buffer(net, id_DCC, "glb_clk", id_CLKI, id_CLKO, + [&](const PortRef &port) { return port.cell->type != id_DCC; }); } } From af6735bdf413d1b3663d085b573f2aaf3e018e44 Mon Sep 17 00:00:00 2001 From: Lofty Date: Tue, 21 Dec 2021 17:10:45 +0000 Subject: [PATCH 084/712] mistral: add M10K bel --- mistral/arch.cc | 3 ++ mistral/arch.h | 1 + mistral/constids.inc | 6 +++- mistral/m10k.cc | 69 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 mistral/m10k.cc diff --git a/mistral/arch.cc b/mistral/arch.cc index e79a391038..6ba1693965 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -122,6 +122,9 @@ Arch::Arch(ArchArgs args) CycloneV::pos2y(hps_pos[CycloneV::I_HPS_MPU_GENERAL_PURPOSE])); } + for (auto m10k_pos : cyclonev->m10k_get_pos()) + create_m10k(CycloneV::pos2x(m10k_pos), CycloneV::pos2y(m10k_pos)); + // This import takes about 5s, perhaps long term we can speed it up, e.g. defer to Mistral more... log_info("Initialising routing graph...\n"); int pip_count = 0; diff --git a/mistral/arch.h b/mistral/arch.h index e17be33125..8fcea2915d 100644 --- a/mistral/arch.h +++ b/mistral/arch.h @@ -467,6 +467,7 @@ struct Arch : BaseArch bool has_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const; void create_lab(int x, int y, bool is_mlab); // lab.cc + void create_m10k(int x, int y); // m10k.cc void create_gpio(int x, int y); // io.cc void create_clkbuf(int x, int y); // globals.cc void create_control(int x, int y); // globals.cc diff --git a/mistral/constids.inc b/mistral/constids.inc index d241c8ef2d..82cd9ecc13 100644 --- a/mistral/constids.inc +++ b/mistral/constids.inc @@ -98,10 +98,14 @@ X(WE_INV) X(cyclonev_oscillator) X(cyclonev_hps_interface_mpu_general_purpose) +X(MISTRAL_M10K) +X(ADDRSTALLA) +X(ADDRSTALLB) + X(clkout) X(clkout1) X(compress_rbf) X(oscena) X(placer) X(router) -X(step) +X(step) \ No newline at end of file diff --git a/mistral/m10k.cc b/mistral/m10k.cc new file mode 100644 index 0000000000..4ab35096e5 --- /dev/null +++ b/mistral/m10k.cc @@ -0,0 +1,69 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 Lofty + * + * 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 "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +void Arch::create_m10k(int x, int y) +{ + BelId bel = add_bel(x, y, id_MISTRAL_M10K, id_MISTRAL_M10K); + add_bel_pin(bel, id_ADDRSTALLA, PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLA, 0)); + add_bel_pin(bel, id_ADDRSTALLB, PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLB, 0)); + for (int z = 0; z < 2; z++) { + add_bel_pin(bel, id(stringf("BYTEENABLEA[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::BYTEENABLEA, z)); + add_bel_pin(bel, id(stringf("BYTEENABLEB[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::BYTEENABLEB, z)); + add_bel_pin(bel, id(stringf("ACLR[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::ACLR, z)); + add_bel_pin(bel, id(stringf("RDEN[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::RDEN, z)); + add_bel_pin(bel, id(stringf("WREN[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::WREN, z)); + add_bel_pin(bel, id(stringf("CLKIN[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z)); + add_bel_pin(bel, id(stringf("CLKIN[%d]", z+6)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z+6)); + } + for (int z = 0; z < 4; z++) { + add_bel_pin(bel, id(stringf("ENABLE[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::ENABLE, 0)); + } + for (int z = 0; z < 12; z++) { + add_bel_pin(bel, id(stringf("ADDRA[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRA, z)); + add_bel_pin(bel, id(stringf("ADDRB[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRB, z)); + } + for (int z = 0; z < 20; z++) { + add_bel_pin(bel, id(stringf("DATAAIN[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::DATAAIN, z)); + add_bel_pin(bel, id(stringf("DATABIN[%d]", z)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::DATABIN, z)); + add_bel_pin(bel, id(stringf("DATAAOUT[%d]", z)), PORT_OUT, + get_port(CycloneV::M10K, x, y, -1, CycloneV::DATAAOUT, z)); + add_bel_pin(bel, id(stringf("DATABOUT[%d]", z)), PORT_OUT, + get_port(CycloneV::M10K, x, y, -1, CycloneV::DATABOUT, z)); + } +} + +NEXTPNR_NAMESPACE_END \ No newline at end of file From 33e031a2841de813cae420becd8ece4286e08e0a Mon Sep 17 00:00:00 2001 From: Lofty Date: Tue, 21 Dec 2021 21:04:05 +0000 Subject: [PATCH 085/712] mistral: M10K cell function --- mistral/constids.inc | 4 ++- mistral/m10k.cc | 73 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/mistral/constids.inc b/mistral/constids.inc index 82cd9ecc13..e032bf5353 100644 --- a/mistral/constids.inc +++ b/mistral/constids.inc @@ -101,6 +101,8 @@ X(cyclonev_hps_interface_mpu_general_purpose) X(MISTRAL_M10K) X(ADDRSTALLA) X(ADDRSTALLB) +X(CFG_ABITS) +X(CFG_DBITS) X(clkout) X(clkout1) @@ -108,4 +110,4 @@ X(compress_rbf) X(oscena) X(placer) X(router) -X(step) \ No newline at end of file +X(step) diff --git a/mistral/m10k.cc b/mistral/m10k.cc index 4ab35096e5..d6aa1ed28d 100644 --- a/mistral/m10k.cc +++ b/mistral/m10k.cc @@ -46,7 +46,7 @@ void Arch::create_m10k(int x, int y) } for (int z = 0; z < 4; z++) { add_bel_pin(bel, id(stringf("ENABLE[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ENABLE, 0)); + get_port(CycloneV::M10K, x, y, -1, CycloneV::ENABLE, z)); } for (int z = 0; z < 12; z++) { add_bel_pin(bel, id(stringf("ADDRA[%d]", z)), PORT_IN, @@ -66,4 +66,75 @@ void Arch::create_m10k(int x, int y) } } +static void assign_lab_pins(Context *ctx, CellInfo *cell) +{ + auto abits = cell->attrs[id_CFG_ABITS].as_int64(); + auto dbits = cell->attrs[id_CFG_DBITS].as_int64(); + NPNR_ASSERT(abits >= 7 && abits <= 13); + NPNR_ASSERT(dbits == 1 || dbits == 2 || dbits == 5 || dbits == 10 || dbits == 20); + + // Quartus doesn't seem to generate ADDRSTALL[AB], BYTEENABLE[AB][01]. + + // It *does* generate ACLR[01] but leaves them unconnected if unused. + + // Enables. + // RDEN[0] and WREN[1] are left unconnected. + cell->pin_data[ctx->id("A1EN")].bel_pins = {ctx->id("RDEN[1]")}; + cell->pin_data[ctx->id("B1EN")].bel_pins = {ctx->id("WREN[0]")}; + + // Clocks. + cell->pin_data[ctx->id("CLK1")].bel_pins = {ctx->id("CLKIN[0]")}; + + // Enables left unconnected. + + // Address lines. + int addr_offset = std::max(12 - std::max(abits, int64_t{9}), 0l); + int bit_offset = 0; + if (abits == 13) { + cell->pin_data[ctx->id("A1ADDR[0]")].bel_pins = {ctx->id("DATAAIN[4]")}; + cell->pin_data[ctx->id("B1ADDR[0]")].bel_pins = {ctx->id("DATABIN[19]")}; + bit_offset = 1; + } + for (int bit = bit_offset; bit < abits; bit++) { + cell->pin_data[ctx->id(stringf("A1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRA[%d]", bit + addr_offset))}; + cell->pin_data[ctx->id(stringf("B1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRB[%d]", bit + addr_offset))}; + } + + // Data lines + std::vector offsets; + offsets.push_back(0); + if (abits >= 10 && dbits <= 10) { + offsets.push_back(10); + } + if (abits >= 11 && dbits <= 5) { + offsets.push_back(5); + offsets.push_back(15); + } + if (abits >= 12 && dbits <= 2) { + offsets.push_back(2); + offsets.push_back(7); + offsets.push_back(12); + offsets.push_back(17); + } + if (abits == 13 && dbits == 1) { + offsets.push_back(1); + offsets.push_back(3); + offsets.push_back(6); + offsets.push_back(8); + offsets.push_back(11); + offsets.push_back(13); + offsets.push_back(16); + offsets.push_back(18); + } + for (int bit = 0; bit < dbits; bit++) { + for (int offset : offsets) { + cell->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back(ctx->id(stringf("DATAAIN[%d]", bit + offset))); + } + } + + for (int bit = 0; bit < dbits; bit++) { + cell->pin_data[ctx->id(stringf("B1DATA[%d]", bit))].bel_pins = {ctx->id(stringf("DATABOUT[%d]", bit))}; + } +} + NEXTPNR_NAMESPACE_END \ No newline at end of file From 34b7cdb5338dd25637b6b8491ea29ac38af0f5b7 Mon Sep 17 00:00:00 2001 From: Lofty Date: Tue, 21 Dec 2021 21:34:28 +0000 Subject: [PATCH 086/712] mistral: move M10K code to pack --- mistral/m10k.cc | 71 --------------------------------------------- mistral/pack.cc | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 71 deletions(-) diff --git a/mistral/m10k.cc b/mistral/m10k.cc index d6aa1ed28d..6d51625e2f 100644 --- a/mistral/m10k.cc +++ b/mistral/m10k.cc @@ -66,75 +66,4 @@ void Arch::create_m10k(int x, int y) } } -static void assign_lab_pins(Context *ctx, CellInfo *cell) -{ - auto abits = cell->attrs[id_CFG_ABITS].as_int64(); - auto dbits = cell->attrs[id_CFG_DBITS].as_int64(); - NPNR_ASSERT(abits >= 7 && abits <= 13); - NPNR_ASSERT(dbits == 1 || dbits == 2 || dbits == 5 || dbits == 10 || dbits == 20); - - // Quartus doesn't seem to generate ADDRSTALL[AB], BYTEENABLE[AB][01]. - - // It *does* generate ACLR[01] but leaves them unconnected if unused. - - // Enables. - // RDEN[0] and WREN[1] are left unconnected. - cell->pin_data[ctx->id("A1EN")].bel_pins = {ctx->id("RDEN[1]")}; - cell->pin_data[ctx->id("B1EN")].bel_pins = {ctx->id("WREN[0]")}; - - // Clocks. - cell->pin_data[ctx->id("CLK1")].bel_pins = {ctx->id("CLKIN[0]")}; - - // Enables left unconnected. - - // Address lines. - int addr_offset = std::max(12 - std::max(abits, int64_t{9}), 0l); - int bit_offset = 0; - if (abits == 13) { - cell->pin_data[ctx->id("A1ADDR[0]")].bel_pins = {ctx->id("DATAAIN[4]")}; - cell->pin_data[ctx->id("B1ADDR[0]")].bel_pins = {ctx->id("DATABIN[19]")}; - bit_offset = 1; - } - for (int bit = bit_offset; bit < abits; bit++) { - cell->pin_data[ctx->id(stringf("A1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRA[%d]", bit + addr_offset))}; - cell->pin_data[ctx->id(stringf("B1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRB[%d]", bit + addr_offset))}; - } - - // Data lines - std::vector offsets; - offsets.push_back(0); - if (abits >= 10 && dbits <= 10) { - offsets.push_back(10); - } - if (abits >= 11 && dbits <= 5) { - offsets.push_back(5); - offsets.push_back(15); - } - if (abits >= 12 && dbits <= 2) { - offsets.push_back(2); - offsets.push_back(7); - offsets.push_back(12); - offsets.push_back(17); - } - if (abits == 13 && dbits == 1) { - offsets.push_back(1); - offsets.push_back(3); - offsets.push_back(6); - offsets.push_back(8); - offsets.push_back(11); - offsets.push_back(13); - offsets.push_back(16); - offsets.push_back(18); - } - for (int bit = 0; bit < dbits; bit++) { - for (int offset : offsets) { - cell->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back(ctx->id(stringf("DATAAIN[%d]", bit + offset))); - } - } - - for (int bit = 0; bit < dbits; bit++) { - cell->pin_data[ctx->id(stringf("B1DATA[%d]", bit))].bel_pins = {ctx->id(stringf("DATABOUT[%d]", bit))}; - } -} - NEXTPNR_NAMESPACE_END \ No newline at end of file diff --git a/mistral/pack.cc b/mistral/pack.cc index 703fa386ac..da1589f055 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -382,6 +382,83 @@ struct MistralPacker } } + void setup_m10ks() + { + for (auto& cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_MISTRAL_M10K) + continue; + + auto abits = ci->attrs[id_CFG_ABITS].as_int64(); + auto dbits = ci->attrs[id_CFG_DBITS].as_int64(); + NPNR_ASSERT(abits >= 7 && abits <= 13); + NPNR_ASSERT(dbits == 1 || dbits == 2 || dbits == 5 || dbits == 10 || dbits == 20); + + // Quartus doesn't seem to generate ADDRSTALL[AB], BYTEENABLE[AB][01]. + + // It *does* generate ACLR[01] but leaves them unconnected if unused. + + // Enables. + // RDEN[0] and WREN[1] are left unconnected. + ci->pin_data[ctx->id("A1EN")].bel_pins = {ctx->id("RDEN[1]")}; + ci->pin_data[ctx->id("B1EN")].bel_pins = {ctx->id("WREN[0]")}; + + // Clocks. + ci->pin_data[ctx->id("CLK1")].bel_pins = {ctx->id("CLKIN[0]")}; + + // Enables left unconnected. + + // Address lines. + int addr_offset = std::max(12 - std::max(abits, int64_t{9}), 0l); + int bit_offset = 0; + if (abits == 13) { + ci->pin_data[ctx->id("A1ADDR[0]")].bel_pins = {ctx->id("DATAAIN[4]")}; + ci->pin_data[ctx->id("B1ADDR[0]")].bel_pins = {ctx->id("DATABIN[19]")}; + bit_offset = 1; + } + for (int bit = bit_offset; bit < abits; bit++) { + ci->pin_data[ctx->id(stringf("A1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRA[%d]", bit + addr_offset))}; + ci->pin_data[ctx->id(stringf("B1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRB[%d]", bit + addr_offset))}; + } + + // Data lines + std::vector offsets; + offsets.push_back(0); + if (abits >= 10 && dbits <= 10) { + offsets.push_back(10); + } + if (abits >= 11 && dbits <= 5) { + offsets.push_back(5); + offsets.push_back(15); + } + if (abits >= 12 && dbits <= 2) { + offsets.push_back(2); + offsets.push_back(7); + offsets.push_back(12); + offsets.push_back(17); + } + if (abits == 13 && dbits == 1) { + offsets.push_back(1); + offsets.push_back(3); + offsets.push_back(6); + offsets.push_back(8); + offsets.push_back(11); + offsets.push_back(13); + offsets.push_back(16); + offsets.push_back(18); + } + for (int bit = 0; bit < dbits; bit++) { + for (int offset : offsets) { + ci->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back(ctx->id(stringf("DATAAIN[%d]", bit + offset))); + } + } + + for (int bit = 0; bit < dbits; bit++) { + ci->pin_data[ctx->id(stringf("B1DATA[%d]", bit))].bel_pins = {ctx->id(stringf("DATABOUT[%d]", bit))}; + } + } + } + void run() { init_constant_nets(); From 78474a5dec13236214c8e15906c40a05ae6ed4af Mon Sep 17 00:00:00 2001 From: Lofty Date: Wed, 22 Dec 2021 12:01:24 +0000 Subject: [PATCH 087/712] mistral: preliminary bitstream info --- mistral/bitstream.cc | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 35c1303a6e..3490a3d739 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -118,6 +118,45 @@ struct MistralBitgen cv->bmux_m_set(CycloneV::CMUXHG, pos, CycloneV::TESTSYN_ENOUT_SELECT, bi, CycloneV::PRE_SYNENB); } + void write_m10k_cell(CellInfo* ci, int x, int y, int bi) + { + auto pos = CycloneV::xy2pos(x, y); + + // Notes: + // DATA_FLOW_THRU is probably transparent reads. + + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::A_DATA_FLOW_THRU, bi, 1); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::A_DATA_WIDTH, bi, ci->attrs[id_CFG_DBITS].as_int64()); + cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_FAST_WRITE, bi, CycloneV::FAST); + cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_OUTPUT_SEL, bi, CycloneV::REG); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_SA_WREN_DELAY, bi, 1); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_SAEN_DELAY, bi, 2); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_WL_DELAY, bi, 2); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_WR_TIMER_PULSE, bi, 0x0b); + + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_DATA_FLOW_THRU, bi, 1); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::B_DATA_WIDTH, bi, ci->attrs[id_CFG_DBITS].as_int64()); + cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_FAST_WRITE, bi, CycloneV::FAST); + cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_OUTPUT_SEL, bi, CycloneV::REG); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_SA_WREN_DELAY, bi, 1); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_SAEN_DELAY, bi, 2); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_WL_DELAY, bi, 2); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_WR_TIMER_PULSE, bi, 0x0b); + + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::TOP_CLK_SEL, bi, 1); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::TOP_W_INV, bi, 1); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::TOP_W_SEL, bi, 1); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::BOT_CLK_INV, bi, 1); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::BOT_W_SEL, bi, 1); + + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::TRUE_DUAL_PORT, bi, 1); + + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::DISABLE_UNUSED, bi, 0); + + for (int bi = 0; bi < 256; bi++) + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::RAM, bi, 0xffffffffff); + } + void write_cells() { for (auto &cell : ctx->cells) { @@ -128,6 +167,8 @@ struct MistralBitgen write_io_cell(ci, loc.x, loc.y, bi); else if (ctx->is_clkbuf_cell(ci->type)) write_clkbuf_cell(ci, loc.x, loc.y, bi); + else if (ci->type == id_MISTRAL_M10K) + write_m10k_cell(ci, loc.x, loc.y, bi); } } From da65afc83b83f02efa96e9a7735f7044a1f1e6d1 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 22 Dec 2021 13:54:30 +0000 Subject: [PATCH 088/712] mistral: M10K pack fixes Signed-off-by: gatecat --- mistral/arch.cc | 2 ++ mistral/pack.cc | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/mistral/arch.cc b/mistral/arch.cc index 6ba1693965..46ed4f62a5 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -410,6 +410,8 @@ void Arch::add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire) void Arch::assign_default_pinmap(CellInfo *cell) { + if (cell->type == id_MISTRAL_M10K) + return; // M10Ks always have a custom pinmap for (auto &port : cell->ports) { auto &pinmap = cell->pin_data[port.first].bel_pins; if (!pinmap.empty()) diff --git a/mistral/pack.cc b/mistral/pack.cc index da1589f055..09fedf10e9 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -384,13 +384,13 @@ struct MistralPacker void setup_m10ks() { - for (auto& cell : ctx->cells) { + for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type != id_MISTRAL_M10K) continue; - auto abits = ci->attrs[id_CFG_ABITS].as_int64(); - auto dbits = ci->attrs[id_CFG_DBITS].as_int64(); + auto abits = ci->params.at(id_CFG_ABITS).as_int64(); + auto dbits = ci->params.at(id_CFG_DBITS).as_int64(); NPNR_ASSERT(abits >= 7 && abits <= 13); NPNR_ASSERT(dbits == 1 || dbits == 2 || dbits == 5 || dbits == 10 || dbits == 20); @@ -417,8 +417,10 @@ struct MistralPacker bit_offset = 1; } for (int bit = bit_offset; bit < abits; bit++) { - ci->pin_data[ctx->id(stringf("A1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRA[%d]", bit + addr_offset))}; - ci->pin_data[ctx->id(stringf("B1ADDR[%d]", bit))].bel_pins = {ctx->id(stringf("ADDRB[%d]", bit + addr_offset))}; + ci->pin_data[ctx->id(stringf("A1ADDR[%d]", bit))].bel_pins = { + ctx->id(stringf("ADDRA[%d]", bit + addr_offset))}; + ci->pin_data[ctx->id(stringf("B1ADDR[%d]", bit))].bel_pins = { + ctx->id(stringf("ADDRB[%d]", bit + addr_offset))}; } // Data lines @@ -449,7 +451,10 @@ struct MistralPacker } for (int bit = 0; bit < dbits; bit++) { for (int offset : offsets) { - ci->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back(ctx->id(stringf("DATAAIN[%d]", bit + offset))); + log_info("%s %s\n", ctx->nameOf(ctx->id(stringf("A1DATA[%d]", bit))), + ctx->nameOf(ctx->id(stringf("DATAAIN[%d]", bit + offset)))); + ci->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back( + ctx->id(stringf("DATAAIN[%d]", bit + offset))); } } @@ -466,6 +471,7 @@ struct MistralPacker pack_io(); constrain_carries(); constrain_lutram(); + setup_m10ks(); } }; }; // namespace From 9be65cd67c6a40ddc35460b97a014bbcd0af2f85 Mon Sep 17 00:00:00 2001 From: Lofty Date: Tue, 18 Jan 2022 12:15:01 +0000 Subject: [PATCH 089/712] mistral: some more M10K fixes --- mistral/bitstream.cc | 4 ++-- mistral/pack.cc | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 3490a3d739..660e367196 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -126,7 +126,7 @@ struct MistralBitgen // DATA_FLOW_THRU is probably transparent reads. cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::A_DATA_FLOW_THRU, bi, 1); - cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::A_DATA_WIDTH, bi, ci->attrs[id_CFG_DBITS].as_int64()); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::A_DATA_WIDTH, bi, ci->params.at(id_CFG_DBITS).as_int64()); cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_FAST_WRITE, bi, CycloneV::FAST); cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_OUTPUT_SEL, bi, CycloneV::REG); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_SA_WREN_DELAY, bi, 1); @@ -135,7 +135,7 @@ struct MistralBitgen cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_WR_TIMER_PULSE, bi, 0x0b); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_DATA_FLOW_THRU, bi, 1); - cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::B_DATA_WIDTH, bi, ci->attrs[id_CFG_DBITS].as_int64()); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::B_DATA_WIDTH, bi, ci->params.at(id_CFG_DBITS).as_int64()); cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_FAST_WRITE, bi, CycloneV::FAST); cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_OUTPUT_SEL, bi, CycloneV::REG); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_SA_WREN_DELAY, bi, 1); diff --git a/mistral/pack.cc b/mistral/pack.cc index 09fedf10e9..27ad3c92bd 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -399,9 +399,9 @@ struct MistralPacker // It *does* generate ACLR[01] but leaves them unconnected if unused. // Enables. - // RDEN[0] and WREN[1] are left unconnected. - ci->pin_data[ctx->id("A1EN")].bel_pins = {ctx->id("RDEN[1]")}; - ci->pin_data[ctx->id("B1EN")].bel_pins = {ctx->id("WREN[0]")}; + // RDEN[1] and WREN[0] are left unconnected. + ci->pin_data[ctx->id("A1EN")].bel_pins = {ctx->id("WREN[1]")}; + ci->pin_data[ctx->id("B1EN")].bel_pins = {ctx->id("RDEN[0]")}; // Clocks. ci->pin_data[ctx->id("CLK1")].bel_pins = {ctx->id("CLKIN[0]")}; @@ -451,8 +451,6 @@ struct MistralPacker } for (int bit = 0; bit < dbits; bit++) { for (int offset : offsets) { - log_info("%s %s\n", ctx->nameOf(ctx->id(stringf("A1DATA[%d]", bit))), - ctx->nameOf(ctx->id(stringf("DATAAIN[%d]", bit + offset)))); ci->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back( ctx->id(stringf("DATAAIN[%d]", bit + offset))); } From 3e688a3ac9ae35a894d5c2c19f2d9591b746d8da Mon Sep 17 00:00:00 2001 From: Lofty Date: Mon, 7 Mar 2022 18:22:56 +0000 Subject: [PATCH 090/712] mistral: fixes and debug info --- mistral/bitstream.cc | 21 +++++++++++---------- mistral/constids.inc | 1 + mistral/delay.cc | 36 ++++++++++++++++++++++++++++++++++++ mistral/m10k.cc | 30 +++++++++++------------------- 4 files changed, 59 insertions(+), 29 deletions(-) diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 660e367196..6a2c3825d4 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -118,7 +118,7 @@ struct MistralBitgen cv->bmux_m_set(CycloneV::CMUXHG, pos, CycloneV::TESTSYN_ENOUT_SELECT, bi, CycloneV::PRE_SYNENB); } - void write_m10k_cell(CellInfo* ci, int x, int y, int bi) + void write_m10k_cell(CellInfo *ci, int x, int y, int bi) { auto pos = CycloneV::xy2pos(x, y); @@ -128,31 +128,32 @@ struct MistralBitgen cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::A_DATA_FLOW_THRU, bi, 1); cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::A_DATA_WIDTH, bi, ci->params.at(id_CFG_DBITS).as_int64()); cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_FAST_WRITE, bi, CycloneV::FAST); - cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_OUTPUT_SEL, bi, CycloneV::REG); + cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::A_OUTPUT_SEL, bi, CycloneV::ASYNC); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_SA_WREN_DELAY, bi, 1); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_SAEN_DELAY, bi, 2); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_WL_DELAY, bi, 2); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::A_WR_TIMER_PULSE, bi, 0x0b); - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_DATA_FLOW_THRU, bi, 1); + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::B_DATA_FLOW_THRU, bi, 1); cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::B_DATA_WIDTH, bi, ci->params.at(id_CFG_DBITS).as_int64()); cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_FAST_WRITE, bi, CycloneV::FAST); - cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_OUTPUT_SEL, bi, CycloneV::REG); + cv->bmux_m_set(CycloneV::M10K, pos, CycloneV::B_OUTPUT_SEL, bi, CycloneV::ASYNC); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_SA_WREN_DELAY, bi, 1); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_SAEN_DELAY, bi, 2); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_WL_DELAY, bi, 2); cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::B_WR_TIMER_PULSE, bi, 0x0b); - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::TOP_CLK_SEL, bi, 1); - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::TOP_W_INV, bi, 1); - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::TOP_W_SEL, bi, 1); - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::BOT_CLK_INV, bi, 1); - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::BOT_W_SEL, bi, 1); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::TOP_CLK_SEL, bi, 1); + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::TOP_W_INV, bi, 1); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::TOP_W_SEL, bi, 1); + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::BOT_CLK_INV, bi, 1); + cv->bmux_n_set(CycloneV::M10K, pos, CycloneV::BOT_W_SEL, bi, 1); - cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::TRUE_DUAL_PORT, bi, 1); + cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::TRUE_DUAL_PORT, bi, 0); cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::DISABLE_UNUSED, bi, 0); + // Note for future us: the RAM init contents are inverted. for (int bi = 0; bi < 256; bi++) cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::RAM, bi, 0xffffffffff); } diff --git a/mistral/constids.inc b/mistral/constids.inc index e032bf5353..262fd12ab1 100644 --- a/mistral/constids.inc +++ b/mistral/constids.inc @@ -90,6 +90,7 @@ X(WE) X(MISTRAL_MLAB) X(CLK1) X(A1EN) +X(B1EN) X(A1DATA) X(B1DATA) X(WCLK_INV) diff --git a/mistral/delay.cc b/mistral/delay.cc index 4d123249e8..c40ac3f095 100644 --- a/mistral/delay.cc +++ b/mistral/delay.cc @@ -57,6 +57,17 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (port.in(id_B1DATA)) { return TMG_COMB_OUTPUT; } + } else if (cell->type == id_MISTRAL_M10K) { + if (port == id_CLK1) { + return TMG_CLOCK_INPUT; + } else if (port.in(id_A1DATA, id_A1EN, id_B1EN) || port.str(this).find("A1ADDR") == 0) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } else if (port.str(this).find("B1ADDR") == 0) { + return TMG_REGISTER_INPUT; + } else if (port.in(id_B1DATA)) { + return TMG_REGISTER_OUTPUT; + } } return TMG_IGNORE; } @@ -87,6 +98,31 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port timing.clockToQ = DelayQuad{}; } return timing; + } else if (cell->type == id_MISTRAL_M10K) { + timing.clock_port = id_CLK1; + timing.edge = RISING_EDGE; + if (port.str(this).find("A1ADDR") == 0 || port.str(this).find("B1ADDR") == 0) { + timing.setup = DelayPair{125, 125}; + timing.hold = DelayPair{42, 42}; + timing.clockToQ = DelayQuad{}; + } else if (port == id_A1DATA) { + timing.setup = DelayPair{97, 97}; + timing.hold = DelayPair{42, 42}; + timing.clockToQ = DelayQuad{}; + } else if (port == id_A1EN) { + timing.setup = DelayPair{140, 140}; + timing.hold = DelayPair{42, 42}; + timing.clockToQ = DelayQuad{}; + } else if (port == id_B1EN) { + timing.setup = DelayPair{161, 161}; + timing.hold = DelayPair{42, 42}; + timing.clockToQ = DelayQuad{}; + } else if (port == id_B1DATA) { + timing.setup = DelayPair{}; + timing.hold = DelayPair{}; + timing.clockToQ = DelayQuad{1004}; + } + return timing; } NPNR_ASSERT_FALSE("unreachable"); } diff --git a/mistral/m10k.cc b/mistral/m10k.cc index 6d51625e2f..4da1204fd8 100644 --- a/mistral/m10k.cc +++ b/mistral/m10k.cc @@ -24,35 +24,27 @@ NEXTPNR_NAMESPACE_BEGIN void Arch::create_m10k(int x, int y) { BelId bel = add_bel(x, y, id_MISTRAL_M10K, id_MISTRAL_M10K); - add_bel_pin(bel, id_ADDRSTALLA, PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLA, 0)); - add_bel_pin(bel, id_ADDRSTALLB, PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLB, 0)); + add_bel_pin(bel, id_ADDRSTALLA, PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLA, 0)); + add_bel_pin(bel, id_ADDRSTALLB, PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLB, 0)); for (int z = 0; z < 2; z++) { add_bel_pin(bel, id(stringf("BYTEENABLEA[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::BYTEENABLEA, z)); add_bel_pin(bel, id(stringf("BYTEENABLEB[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::BYTEENABLEB, z)); - add_bel_pin(bel, id(stringf("ACLR[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ACLR, z)); - add_bel_pin(bel, id(stringf("RDEN[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::RDEN, z)); - add_bel_pin(bel, id(stringf("WREN[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::WREN, z)); - add_bel_pin(bel, id(stringf("CLKIN[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z)); - add_bel_pin(bel, id(stringf("CLKIN[%d]", z+6)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z+6)); + add_bel_pin(bel, id(stringf("ACLR[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ACLR, z)); + add_bel_pin(bel, id(stringf("RDEN[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::RDEN, z)); + add_bel_pin(bel, id(stringf("WREN[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::WREN, z)); + add_bel_pin(bel, id(stringf("CLKIN[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z)); + add_bel_pin(bel, id(stringf("CLKIN[%d]", z + 6)), PORT_IN, + get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z + 6)); } for (int z = 0; z < 4; z++) { add_bel_pin(bel, id(stringf("ENABLE[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ENABLE, z)); } for (int z = 0; z < 12; z++) { - add_bel_pin(bel, id(stringf("ADDRA[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRA, z)); - add_bel_pin(bel, id(stringf("ADDRB[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRB, z)); + add_bel_pin(bel, id(stringf("ADDRA[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRA, z)); + add_bel_pin(bel, id(stringf("ADDRB[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRB, z)); } for (int z = 0; z < 20; z++) { add_bel_pin(bel, id(stringf("DATAAIN[%d]", z)), PORT_IN, @@ -66,4 +58,4 @@ void Arch::create_m10k(int x, int y) } } -NEXTPNR_NAMESPACE_END \ No newline at end of file +NEXTPNR_NAMESPACE_END From 8c0dbdb2184b17aaee42fb46efadda5e864cd865 Mon Sep 17 00:00:00 2001 From: Krzysztof Boronski Date: Fri, 11 Mar 2022 07:53:42 -0600 Subject: [PATCH 091/712] interchange: Don't hold reference to visit in global routing Signed-off-by: Krzysztof Boronski --- fpga_interchange/globals.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpga_interchange/globals.cc b/fpga_interchange/globals.cc index 6efd1d8994..eaff30e08f 100644 --- a/fpga_interchange/globals.cc +++ b/fpga_interchange/globals.cc @@ -76,7 +76,7 @@ static int route_global_arc(Context *ctx, NetInfo *net, store_index usr while (!visit_queue.empty()) { WireId cursor = visit_queue.front(); visit_queue.pop(); - auto &curr_visit = visits.at(cursor); + auto curr_visit = visits.at(cursor); // We're now at least one layer deeper than a valid visit, any further exploration is futile if (startpoint != WireId() && curr_visit.total_hops > best_visit.total_hops) break; From 2a3d0c1d29f99c96004564a74c0dc04f7713d870 Mon Sep 17 00:00:00 2001 From: Maya <96890070+VioletEternity@users.noreply.github.com> Date: Fri, 11 Mar 2022 23:31:23 +0000 Subject: [PATCH 092/712] ecp5: verify hex strings contain only valid characters. --- ecp5/bitstream.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index a23e4cd2e5..b1c60ccb06 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -149,6 +149,8 @@ std::vector parse_init_str(const Property &p, int length, const char *cell for (int i = 0; i < int(str.length()) - 2; i++) { char c = str.at((str.size() - i) - 1); int nibble = chtohex(c); + if (nibble == (int)std::string::npos) + log_error("hex string has invalid char '%c' at position %d.\n", c, i); result.at(i * 4) = nibble & 0x1; if (i * 4 + 1 < length) result.at(i * 4 + 1) = nibble & 0x2; @@ -582,13 +584,16 @@ static std::vector parse_config_str(const Property &p, int length) if (base == "0b") { for (int i = 0; i < int(str.length()) - 2; i++) { char c = str.at((str.size() - 1) - i); - NPNR_ASSERT(c == '0' || c == '1'); + if (!(c == '0' || c == '1')) + log_error("binary string has invalid char '%c' at position %d.\n", c, i); word.at(i) = (c == '1'); } } else if (base == "0x") { for (int i = 0; i < int(str.length()) - 2; i++) { char c = str.at((str.size() - i) - 1); int nibble = chtohex(c); + if (nibble == (int)std::string::npos) + log_error("hex string has invalid char '%c' at position %d.\n", c, i); word.at(i * 4) = nibble & 0x1; if (i * 4 + 1 < length) word.at(i * 4 + 1) = nibble & 0x2; From 9f53bd42788460d6bebd4f2f50af32a710667c93 Mon Sep 17 00:00:00 2001 From: Maya <96890070+VioletEternity@users.noreply.github.com> Date: Fri, 11 Mar 2022 23:34:45 +0000 Subject: [PATCH 093/712] ecp5: accept lowercase characters in hex strings. --- ecp5/bitstream.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index b1c60ccb06..40d843b965 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -131,7 +131,7 @@ static void tie_cib_signal(Context *ctx, ChipConfig &cc, WireId wire, bool value inline int chtohex(char c) { static const std::string hex = "0123456789ABCDEF"; - return hex.find(c); + return hex.find(std::toupper(c)); } std::vector parse_init_str(const Property &p, int length, const char *cellname) From 4a2aa6deb49b70ed69f386898bcde7a6b2572618 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 12 Mar 2022 23:05:42 +1000 Subject: [PATCH 094/712] gowin: Add the Global Set/Reset primitive GSR is added automatically if it was not instantiated by the user explicitly. Compatible with old apicula bases, the functionality does not work, but the crash does not happen --- just a warning. Signed-off-by: YRabbit --- gowin/arch.cc | 8 ++++++++ gowin/cells.cc | 2 ++ gowin/constids.inc | 5 +++++ gowin/pack.cc | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index e6ee826720..22874810fd 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -991,6 +991,14 @@ Arch::Arch(ArchArgs args) : args(args) int z = 0; bool dff = true; switch (static_cast(bel->type_id)) { + case ID_GSR0: + snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_GSR, Loc(col, row, 0), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_GSRI)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_GSRI, id(buf)); + break; // fall through the ++ case ID_LUT7: z++; diff --git a/gowin/cells.cc b/gowin/cells.cc index d862458c33..8e450b5140 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -63,6 +63,8 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addInput(id_I); new_cell->addInput(id_OEN); new_cell->addOutput(id_O); + } else if (type == id_GSR) { + new_cell->addInput(id_GSRI); } else { log_error("unable to create generic cell of type %s\n", type.c_str(ctx)); } diff --git a/gowin/constids.inc b/gowin/constids.inc index 3e72340abd..d32a987df2 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -739,6 +739,11 @@ X(IOBUF) X(TBUF) X(TLVDS_OBUF) +// global set/reset +X(GSR) +X(GSR0) +X(GSRI) + // primitive attributes X(INIT) X(FF_USED) diff --git a/gowin/pack.cc b/gowin/pack.cc index c17a20c7c5..24daee31d6 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -660,6 +660,41 @@ static void pack_constants(Context *ctx) } } +// Pack global set-reset +static void pack_gsr(Context *ctx) +{ + log_info("Packing GSR..\n"); + + bool user_gsr = false; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ctx->verbose) + log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); + if (ci->type == id_GSR) { + user_gsr = true; + break; + } + } + if (!user_gsr) { + // XXX + bool have_gsr_bel = false; + for (auto bi : ctx->bels) { + if (bi.second.type == id_GSR) { + have_gsr_bel = true; + break; + } + } + if (have_gsr_bel) { + // make default GSR + std::unique_ptr gsr_cell = create_generic_cell(ctx, id_GSR, "GSR"); + gsr_cell->connectPort(id_GSRI, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); + ctx->cells[gsr_cell->name] = std::move(gsr_cell); + } else { + log_info("No GSR in the chip base\n"); + } + } +} + static bool is_nextpnr_iob(const Context *ctx, CellInfo *cell) { return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") || @@ -857,6 +892,7 @@ bool Arch::pack() try { log_break(); pack_constants(ctx); + pack_gsr(ctx); pack_io(ctx); pack_diff_io(ctx); pack_wideluts(ctx); From 883f27480274cd6385bce118dc31d260ec9ae832 Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 10 Mar 2022 10:28:57 +0000 Subject: [PATCH 095/712] mistral: M10K initialisation support --- mistral/bitstream.cc | 19 +++++++++++++++++-- mistral/constids.inc | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 6a2c3825d4..c51050083f 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -153,9 +153,24 @@ struct MistralBitgen cv->bmux_b_set(CycloneV::M10K, pos, CycloneV::DISABLE_UNUSED, bi, 0); - // Note for future us: the RAM init contents are inverted. + auto permute_init = [](int64_t init) -> int64_t { + const int permutation[40] = {0, 20, 10, 30, 1, 21, 11, 31, 2, 22, 12, 32, 3, 23, 13, 33, 4, 24, 14, 34, + 5, 25, 15, 35, 6, 26, 16, 36, 7, 27, 17, 37, 8, 28, 18, 38, 9, 29, 19, 39}; + + int64_t output = 0; + for (int bit = 0; bit < 40; bit++) + output |= ((init >> permutation[bit]) & 1) << bit; + return ~output; // RAM init is inverted. + }; + + Property init; + if (ci->params.count(id_INIT) == 0) { + init = Property{0, 10240}; + } else { + init = ci->params.at(id_INIT); + } for (int bi = 0; bi < 256; bi++) - cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::RAM, bi, 0xffffffffff); + cv->bmux_r_set(CycloneV::M10K, pos, CycloneV::RAM, bi, permute_init(init.extract(bi * 40, 40).as_int64())); } void write_cells() diff --git a/mistral/constids.inc b/mistral/constids.inc index 262fd12ab1..754a96b367 100644 --- a/mistral/constids.inc +++ b/mistral/constids.inc @@ -100,6 +100,7 @@ X(cyclonev_oscillator) X(cyclonev_hps_interface_mpu_general_purpose) X(MISTRAL_M10K) +X(INIT) X(ADDRSTALLA) X(ADDRSTALLB) X(CFG_ABITS) From e0f55e6d69d76f62565a5cfacc41e7e2a18c04ee Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 14 Mar 2022 14:23:20 +1000 Subject: [PATCH 096/712] gowin: BUGFIX temporarily disable the budget Signed-off-by: YRabbit --- gui/gowin/mainwindow.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gui/gowin/mainwindow.cc b/gui/gowin/mainwindow.cc index c7ba44ab60..133a116bdf 100644 --- a/gui/gowin/mainwindow.cc +++ b/gui/gowin/mainwindow.cc @@ -74,6 +74,9 @@ void MainWindow::createMenu() menuDesign->addSeparator(); menuDesign->addAction(actionLoadCST); + + // XXX + actionAssignBudget->setEnabled(false); } void MainWindow::new_proj() {} @@ -99,5 +102,7 @@ void MainWindow::onUpdateActions() if (ctx->settings.find(ctx->id("pack")) != ctx->settings.end()) { actionLoadCST->setEnabled(false); } + // XXX + actionAssignBudget->setEnabled(false); } NEXTPNR_NAMESPACE_END From 4736acb3d552de7ad9251b519195c66f63913b6a Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 14 Mar 2022 17:42:50 +1000 Subject: [PATCH 097/712] fix identation Signed-off-by: YRabbit --- gui/gowin/mainwindow.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/gowin/mainwindow.cc b/gui/gowin/mainwindow.cc index 133a116bdf..8593bb4be4 100644 --- a/gui/gowin/mainwindow.cc +++ b/gui/gowin/mainwindow.cc @@ -75,8 +75,8 @@ void MainWindow::createMenu() menuDesign->addSeparator(); menuDesign->addAction(actionLoadCST); - // XXX - actionAssignBudget->setEnabled(false); + // XXX + actionAssignBudget->setEnabled(false); } void MainWindow::new_proj() {} @@ -102,7 +102,7 @@ void MainWindow::onUpdateActions() if (ctx->settings.find(ctx->id("pack")) != ctx->settings.end()) { actionLoadCST->setEnabled(false); } - // XXX - actionAssignBudget->setEnabled(false); + // XXX + actionAssignBudget->setEnabled(false); } NEXTPNR_NAMESPACE_END From 25d7e3ae8b409a1be550e25a2dbabc3c54f0d446 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 14 Mar 2022 21:41:57 +1000 Subject: [PATCH 098/712] gowin: support for locales other than en_US and C Specifically, those locales where the fractional part separator in floating point numbers is not a dot. Signed-off-by: YRabbit --- gowin/main.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gowin/main.cc b/gowin/main.cc index 1473f3e89b..db74545edd 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -21,6 +21,7 @@ #ifdef MAIN_EXECUTABLE #include +#include #include #include "command.h" #include "design_utils.h" @@ -90,6 +91,11 @@ void GowinCommandHandler::customAfterLoad(Context *ctx) int main(int argc, char *argv[]) { + // set the locale here because when you create a context you create several + // floating point strings whose representation depends on the locale. If + // you don't do this, the strings will be in the C locale and later when Qt + // starts it won't be able to read them back as numbers. + std::locale::global(std::locale("")); GowinCommandHandler handler(argc, argv); return handler.exec(); } From e9ce55bfd020a05b93fae5be8fe431876ae9c742 Mon Sep 17 00:00:00 2001 From: Stefan Riesenberger Date: Mon, 14 Mar 2022 12:43:19 +0100 Subject: [PATCH 099/712] ice40: fix crash when packing LUTs with no output --- ice40/pack.cc | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index c32e346fcc..f533941ddd 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -49,32 +49,35 @@ static void pack_lut_lutffs(Context *ctx) log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx)); // See if we can pack into a DFF // TODO: LUT cascade - NetInfo *o = ci->ports.at(id_O).net; - CellInfo *dff = net_only_drives(ctx, o, is_ff, id_D, true); - auto lut_bel = ci->attrs.find(id_BEL); + auto port = ci->ports.find(id_O); bool packed_dff = false; - if (dff) { - if (ctx->verbose) - log_info("found attached dff %s\n", dff->name.c_str(ctx)); - auto dff_bel = dff->attrs.find(id_BEL); - if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { - // Locations don't match, can't pack - } else { - lut_to_lc(ctx, ci, packed.get(), false); - dff_to_lc(ctx, dff, packed.get(), false); - ++lut_and_ff; - ctx->nets.erase(o->name); - if (dff_bel != dff->attrs.end()) - packed->attrs[id_BEL] = dff_bel->second; - for (const auto &attr : dff->attrs) { - // BEL is dealt with specially - if (attr.first != id_BEL) - packed->attrs[attr.first] = attr.second; - } - packed_cells.insert(dff->name); + if (port != ci->ports.end()) { + NetInfo *o = port->second.net; + CellInfo *dff = net_only_drives(ctx, o, is_ff, id_D, true); + auto lut_bel = ci->attrs.find(id_BEL); + if (dff) { if (ctx->verbose) - log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx)); - packed_dff = true; + log_info("found attached dff %s\n", dff->name.c_str(ctx)); + auto dff_bel = dff->attrs.find(id_BEL); + if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { + // Locations don't match, can't pack + } else { + lut_to_lc(ctx, ci, packed.get(), false); + dff_to_lc(ctx, dff, packed.get(), false); + ++lut_and_ff; + ctx->nets.erase(o->name); + if (dff_bel != dff->attrs.end()) + packed->attrs[id_BEL] = dff_bel->second; + for (const auto &attr : dff->attrs) { + // BEL is dealt with specially + if (attr.first != id_BEL) + packed->attrs[attr.first] = attr.second; + } + packed_cells.insert(dff->name); + if (ctx->verbose) + log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx)); + packed_dff = true; + } } } if (!packed_dff) { From badef293ebdf15567ddbf060f65c2eb7c1faa4ed Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 15 Mar 2022 11:02:37 +1000 Subject: [PATCH 100/712] gowin: add support for ODDR primitive Compatible with older versions of apicula bases. Also small fixes and as the number of virtual Bels grows it is necessary to assign them Z coordinate in a centralized way to avoid conflicts and for this purpose introduced the BelZ enum. Signed-off-by: YRabbit --- gowin/arch.cc | 53 ++++++++++++++++++++++- gowin/arch.h | 17 ++++++-- gowin/constids.inc | 12 ++++++ gowin/gfx.cc | 4 +- gowin/pack.cc | 102 +++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 177 insertions(+), 11 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 22874810fd..6213124f99 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -789,7 +789,7 @@ void Arch::addMuxBels(const DatabasePOD *db, int row, int col) // 4 MUX2_LUT5, 2 MUX2_LUT6, 1 MUX2_LUT7, 1 MUX2_LUT8 for (int j = 0; j < 8; ++j) { - z = j + mux_0_z; + z = j + BelZ::mux_0_z; int grow = row + 1; int gcol = col + 1; @@ -990,6 +990,7 @@ Arch::Arch(ArchArgs args) : args(args) IdString portname; int z = 0; bool dff = true; + bool oddrc = false; switch (static_cast(bel->type_id)) { case ID_GSR0: snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1); @@ -1119,6 +1120,56 @@ Arch::Arch(ArchArgs args) : args(args) addBelInput(belname, id_OEN, id(buf)); break; + // IO logic + case ID_ODDRCB: + z++; /* fall-through*/ + case ID_ODDRCA: + oddrc = true; + z++; /* fall-through*/ + case ID_ODDRB: + z++; /* fall-through*/ + case ID_ODDRA: { + snprintf(buf, 32, "R%dC%d_ODDR%s%c", row + 1, col + 1, oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0)); + belname = id(buf); + addBel(belname, id_ODDR, Loc(col, row, BelZ::iologic_0_z + z), false); + + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D0)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_D0, id(buf)); + + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_D1)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_D1, id(buf)); + + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_TX)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_TX, id(buf)); + + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CLK)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_CLK, id(buf)); + + if (oddrc) { + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CE)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_CE, id(buf)); + } + + // dummy wires + snprintf(buf, 32, "ODDR%s%c_Q0", oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0)); + IdString id_q0 = id(buf); + IdString q0_name = wireToGlobal(row, col, db, id_q0); + if (wires.count(q0_name) == 0) + addWire(q0_name, id_q0, row, col); + addBelOutput(belname, id_Q0, q0_name); + + snprintf(buf, 32, "ODDR%s%c_Q1", oddrc ? "C" : "", 'A' + z - (oddrc ? 2 : 0)); + IdString id_q1 = id(buf); + IdString q1_name = wireToGlobal(row, col, db, id_q1); + if (wires.count(q1_name) == 0) + addWire(q1_name, id_q1, row, col); + addBelOutput(belname, id_Q1, q1_name); + } break; default: break; } diff --git a/gowin/arch.h b/gowin/arch.h index 7a5dfc466d..c8392e7e4e 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -287,8 +287,8 @@ struct Arch : BaseArch // These functions include useful errors if not found WireInfo &wire_info(IdString wire); - PipInfo &pip_info(IdString wire); - BelInfo &bel_info(IdString wire); + PipInfo &pip_info(IdString pip); + BelInfo &bel_info(IdString bel); std::vector bel_ids, wire_ids, pip_ids; @@ -459,8 +459,6 @@ struct Arch : BaseArch void assignArchInfo() override; bool cellsCompatible(const CellInfo **cells, int count) const; bool haveBelType(int x, int y, IdString bel_type); - // start Z for the MUX2LUT5 bels - int const mux_0_z = 10; // chip db version unsigned int const chipdb_version = 1; @@ -476,6 +474,17 @@ struct Arch : BaseArch std::map dff_comp_mode; }; +// Bels Z range +namespace BelZ { +enum +{ + mux_0_z = 10, // start Z for the MUX2LUT5 bels + iologic_0_z = 20, // start Z for the IOLOGIC bels + vcc_0_z = 277, // virtual VCC bel Z + gnd_0_z = 278 // virtual VSS bel Z +}; +} + NEXTPNR_NAMESPACE_END #endif /* GOWIN_ARCH_H */ diff --git a/gowin/constids.inc b/gowin/constids.inc index d32a987df2..125fdc74b4 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -679,6 +679,18 @@ X(IOBHS) X(IOBIS) X(IOBJS) +// IOLOGIC +X(TX) +X(OBUF_TYPE) +X(SBUF) +X(DBUF) +X(ODDR) +X(ODDRC) +X(ODDRA) +X(ODDRB) +X(ODDRCA) +X(ODDRCB) + // Wide LUTs X(MUX2_LUT5) X(MUX2_LUT6) diff --git a/gowin/gfx.cc b/gowin/gfx.cc index daae5c71c3..fcd42c7c74 100644 --- a/gowin/gfx.cc +++ b/gowin/gfx.cc @@ -5639,14 +5639,14 @@ void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel) break; case ID_GW_MUX2_LUT5: active.x = inactive.x = bel.x + mux2lut5_x; - active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - arch->mux_0_z) >> 1]; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - BelZ::mux_0_z) >> 1]; active.decal = id_DECAL_MUXUPPER_ACTIVE; inactive.decal = id_DECAL_MUXUPPER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); break; case ID_GW_MUX2_LUT6: active.x = inactive.x = bel.x + mux2lut6_x; - active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - arch->mux_0_z) / 5]; + active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - BelZ::mux_0_z) / 5]; active.decal = id_DECAL_MUXLOWER_ACTIVE; inactive.decal = id_DECAL_MUXLOWER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); diff --git a/gowin/pack.cc b/gowin/pack.cc index 24daee31d6..9f0a2478e6 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -271,7 +271,7 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce // mux is the cluster root packed->cluster = packed->name; lut1->cluster = packed->name; - lut1->constr_z = -ctx->mux_0_z + 1; + lut1->constr_z = -BelZ::mux_0_z + 1; packed->constr_children.clear(); // reconnect MUX ports @@ -314,9 +314,9 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce // mux is the cluster root packed->cluster = packed->name; lut0->cluster = packed->name; - lut0->constr_z = -ctx->mux_0_z; + lut0->constr_z = -BelZ::mux_0_z; lut1->cluster = packed->name; - lut1->constr_z = -ctx->mux_0_z + 1; + lut1->constr_z = -BelZ::mux_0_z + 1; packed->constr_children.clear(); // reconnect MUX ports @@ -724,8 +724,100 @@ static bool is_gowin_diff_iob(const Context *ctx, const CellInfo *cell) } } +static bool is_gowin_iologic(const Context *ctx, const CellInfo *cell) +{ + switch (cell->type.index) { + case ID_ODDR: /* fall-through*/ + case ID_ODDRC: + return true; + default: + return false; + } +} + static bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); } +// Pack IO logic +static void pack_iologic(Context *ctx) +{ + pool packed_cells; + pool delete_nets; + + std::vector> new_cells; + log_info("Packing IO logic..\n"); + + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ctx->verbose) + log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx)); + if (is_gowin_iologic(ctx, ci)) { + CellInfo *q0_dst = nullptr; + CellInfo *q1_dst = nullptr; + switch (ci->type.index) { + case ID_ODDRC: /* fall-through*/ + case ID_ODDR: { + q0_dst = net_only_drives(ctx, ci->ports.at(id_Q0).net, is_iob, id_I); + NPNR_ASSERT(q0_dst != nullptr); + + auto iob_bel = q0_dst->attrs.find(id_BEL); + if (q0_dst->attrs.count(id_DIFF_TYPE)) { + ci->attrs[id_OBUF_TYPE] = std::string("DBUF"); + } else { + ci->attrs[id_OBUF_TYPE] = std::string("SBUF"); + } + if (iob_bel != q0_dst->attrs.end()) { + // already know there to place, no need of any cluster stuff + Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(iob_bel->second.as_string())); + loc.z += BelZ::iologic_0_z; + ci->attrs[id_BEL] = ctx->getBelName(ctx->getBelByLocation(loc)).str(ctx); + } else { + // make cluster from ODDR and OBUF + ci->cluster = ci->name; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = 0; + ci->constr_abs_z = false; + ci->constr_children.push_back(q0_dst); + q0_dst->cluster = ci->name; + q0_dst->constr_x = 0; + q0_dst->constr_y = 0; + q0_dst->constr_z = -BelZ::iologic_0_z; + q0_dst->constr_abs_z = false; + } + + // disconnect Q0 output: it is wired internally + delete_nets.insert(ci->ports.at(id_Q0).net->name); + q0_dst->disconnectPort(id_I); + ci->disconnectPort(id_Q0); + + ci->attrs[id_IOBUF] = 0; + // if Q1 is conected then disconnet it too + if (port_used(ci, id_Q1)) { + q1_dst = net_only_drives(ctx, ci->ports.at(id_Q1).net, is_iob, id_OEN); + if (q1_dst != nullptr) { + delete_nets.insert(ci->ports.at(id_Q1).net->name); + q0_dst->disconnectPort(id_OEN); + ci->disconnectPort(id_Q1); + ci->attrs[id_IOBUF] = 1; + } + } + } break; + default: + break; + } + } + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + for (auto dnet : delete_nets) { + ctx->nets.erase(dnet); + } + for (auto &ncell : new_cells) { + ctx->cells[ncell->name] = std::move(ncell); + } +} + // Pack differential IO buffers static void pack_diff_io(Context *ctx) { @@ -749,7 +841,7 @@ static void pack_diff_io(Context *ctx) NPNR_ASSERT(iob_p != nullptr); NPNR_ASSERT(iob_n != nullptr); auto iob_p_bel_a = iob_p->attrs.find(id_BEL); - if (iob_p_bel_a == ci->attrs.end()) { + if (iob_p_bel_a == iob_p->attrs.end()) { log_error("LVDS '%s' must be restricted.\n", ctx->nameOf(ci)); continue; } @@ -793,6 +885,7 @@ static void pack_diff_io(Context *ctx) ctx->cells[ncell->name] = std::move(ncell); } } + // Pack IO buffers static void pack_io(Context *ctx) { @@ -895,6 +988,7 @@ bool Arch::pack() pack_gsr(ctx); pack_io(ctx); pack_diff_io(ctx); + pack_iologic(ctx); pack_wideluts(ctx); pack_alus(ctx); pack_lut_lutffs(ctx); From 1cc71c7846463c401b63c15cf7da3c674b50269e Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 15 Mar 2022 10:43:31 +0100 Subject: [PATCH 101/712] nexus: Added FASM feature emission for DCC and port timing class info Signed-off-by: Maciej Kurc --- nexus/arch.cc | 8 ++++++++ nexus/fasm.cc | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/nexus/arch.cc b/nexus/arch.cc index cb8cacf8d0..0241e83286 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -546,6 +546,14 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in if (type == TMG_REGISTER_INPUT || type == TMG_REGISTER_OUTPUT) clockInfoCount = 1; return type; + } else if (cell->type == id_DCC) { + if (port == id_CLKI) + return TMG_CLOCK_INPUT; + else if (port == id_CLKO) + return TMG_GEN_CLOCK; + else if (port == id_CE) + return TMG_COMB_INPUT; + return TMG_IGNORE; } return TMG_IGNORE; } diff --git a/nexus/fasm.cc b/nexus/fasm.cc index c460e14bab..c8404587ef 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -526,6 +526,16 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(2); } + // Write config for DCC + void write_dcc(const CellInfo *cell) + { + BelId bel = cell->bel; + push_tile(bel.tile); + push_belname(bel); + write_bit("DCCEN.1"); // Explicit DCC cell implies a clock buffer + write_cell_muxes(cell); + pop(2); + } // Write config for an OXIDE_EBR cell void write_bram(const CellInfo *cell) { @@ -927,6 +937,8 @@ struct NexusFasmWriter write_dphy(ci); else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) write_iol(ci); + else if (ci->type == id_DCC) + write_dcc(ci); blank(); } // Handle DCC route-throughs From ad00f3fdeb9e48a63c2781815d265266123f2621 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 15 Mar 2022 21:52:32 +1000 Subject: [PATCH 102/712] gowin: test locale workaround Signed-off-by: YRabbit --- gowin/main.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gowin/main.cc b/gowin/main.cc index db74545edd..e3c6762bd2 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -95,7 +95,11 @@ int main(int argc, char *argv[]) // floating point strings whose representation depends on the locale. If // you don't do this, the strings will be in the C locale and later when Qt // starts it won't be able to read them back as numbers. - std::locale::global(std::locale("")); + try { + std::locale::global(std::locale("")); + } catch (const std::runtime_error &e) { + // the locale is broken in this system, so leave it as it is + } GowinCommandHandler handler(argc, argv); return handler.exec(); } From 53ddbbaa8584bac463718ba4837d1ee8f79d88c4 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 16 Mar 2022 05:39:55 +1000 Subject: [PATCH 103/712] Set the locale as early as possible Signed-off-by: YRabbit --- common/command.cc | 10 +++++++++- gowin/main.cc | 9 --------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/common/command.cc b/common/command.cc index 2167deb92b..0de9ccc470 100644 --- a/common/command.cc +++ b/common/command.cc @@ -45,7 +45,15 @@ NEXTPNR_NAMESPACE_BEGIN -CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { log_streams.clear(); } +CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) +{ + try { + std::locale::global(std::locale("")); + } catch (const std::runtime_error &e) { + // the locale is broken in this system, so leave it as it is + } + log_streams.clear(); +} bool CommandHandler::parseOptions() { diff --git a/gowin/main.cc b/gowin/main.cc index e3c6762bd2..19c1d02c2d 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -91,15 +91,6 @@ void GowinCommandHandler::customAfterLoad(Context *ctx) int main(int argc, char *argv[]) { - // set the locale here because when you create a context you create several - // floating point strings whose representation depends on the locale. If - // you don't do this, the strings will be in the C locale and later when Qt - // starts it won't be able to read them back as numbers. - try { - std::locale::global(std::locale("")); - } catch (const std::runtime_error &e) { - // the locale is broken in this system, so leave it as it is - } GowinCommandHandler handler(argc, argv); return handler.exec(); } From e3b9c971f926ddf05f39eac0e37dd054ffdcdf28 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 16 Mar 2022 15:05:27 +1000 Subject: [PATCH 104/712] BUGFIX: disable the thousands separator The wire numbers are very large and it is undesirable to use a thousand separator there. This is a side effect of enabling locale. Signed-off-by: YRabbit --- common/command.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/command.cc b/common/command.cc index 0de9ccc470..00f900b375 100644 --- a/common/command.cc +++ b/common/command.cc @@ -45,10 +45,17 @@ NEXTPNR_NAMESPACE_BEGIN +struct no_separator : std::numpunct +{ + protected: + virtual string_type do_grouping() const { return "\000"; } // groups of 0 (disable) +}; + CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { try { - std::locale::global(std::locale("")); + std::locale loc(""); + std::locale::global(std::locale(loc, new no_separator())); } catch (const std::runtime_error &e) { // the locale is broken in this system, so leave it as it is } From c898587b87c5501fb0278990fb2bd5b2bb339fb1 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Wed, 16 Mar 2022 15:47:13 +1000 Subject: [PATCH 105/712] gowin: don't crash if no arguments are set Signed-off-by: YRabbit --- gowin/main.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gowin/main.cc b/gowin/main.cc index 19c1d02c2d..a45a49d40e 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -56,6 +56,10 @@ po::options_description GowinCommandHandler::getArchOptions() std::unique_ptr GowinCommandHandler::createContext(dict &values) { + if (!vm.count("device")) { + log_error("The device must be specified\n"); + } + std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*"); std::smatch match; std::string device = vm["device"].as(); From 43861c0ee298c4d2a5cc89a42e816299c7f86138 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Fri, 11 Mar 2022 16:05:37 +0100 Subject: [PATCH 106/712] nexus: Added support for the DCS Bel Signed-off-by: Maciej Kurc --- nexus/arch.cc | 15 +++++++++++++++ nexus/constids.inc | 7 +++++++ nexus/fasm.cc | 11 +++++++++++ nexus/global.cc | 2 +- nexus/pack.cc | 25 +++++++++++++++++++++++-- 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index 0241e83286..39e51a5b15 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -486,6 +486,12 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort if (fromPort == id_CLK) return false; // don't include delays that are actually clock-to-out here return lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay); + } else if(cell->type == id_DCS) { + if (fromPort == id_SELFORCE || fromPort == id_SEL) { + return false; + } + int index = get_cell_timing_idx(id_DCS, id_DCS); + return lookup_cell_delay(index, fromPort, toPort, delay); } return false; } @@ -553,6 +559,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_GEN_CLOCK; else if (port == id_CE) return TMG_COMB_INPUT; + } else if (cell->type == id_DCS) { + // FIXME: Making inputs TMG_CLOCK_INPUT and the output TMG_CLOCK_GEN + // yielded in error in the timing analyzer. For now keep those as + // regular combinational ports. + if (port == id_CLK0 || port == id_CLK1) + return TMG_COMB_INPUT; + else if (port == id_DCSOUT) { + return TMG_COMB_OUTPUT; + } return TMG_IGNORE; } return TMG_IGNORE; diff --git a/nexus/constids.inc b/nexus/constids.inc index 11726d3b1e..bdf8867b50 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -532,6 +532,13 @@ X(SCLK) X(LOCAL_VCC) +X(DCS) +X(CLK0) +X(CLK1) +X(SELFORCE) +X(DCSOUT) +X(DCSMODE) + X(BEL_TYPE) X(BEL_Z) X(CEOUTMUX) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index c8404587ef..e2eb2305e6 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -536,6 +536,15 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(2); } + // Write config for DCS + void write_dcs(const CellInfo *cell) + { + BelId bel = cell->bel; + push_tile(bel.tile, ctx->id("CMUX_0")); + push_belname(bel); + write_enum(cell, "DCSMODE", "VCC"); + pop(2); + } // Write config for an OXIDE_EBR cell void write_bram(const CellInfo *cell) { @@ -939,6 +948,8 @@ struct NexusFasmWriter write_iol(ci); else if (ci->type == id_DCC) write_dcc(ci); + else if (ci->type == id_DCS) + write_dcs(ci); blank(); } // Handle DCC route-throughs diff --git a/nexus/global.cc b/nexus/global.cc index 31bf0a6b7a..37629f6417 100644 --- a/nexus/global.cc +++ b/nexus/global.cc @@ -193,7 +193,7 @@ struct NexusGlobalRouter CellInfo *drv = ni->driver.cell; if (drv == nullptr) continue; - if (drv->type == id_DCC) { + if (drv->type == id_DCC || drv->type == id_DCS) { route_clk_net(ni); continue; } diff --git a/nexus/pack.cc b/nexus/pack.cc index 66d897a866..cc86699a6b 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -860,6 +860,9 @@ struct NexusPacker // Skip undriven nets; and nets that are already global if (ni->driver.cell == nullptr) continue; + if (ni->driver.cell->type == id_DCS) { + continue; + } if (ni->driver.cell->type == id_DCC) { --available_globals; continue; @@ -1981,14 +1984,32 @@ struct NexusPacker if (user.port == id_CLKI || user.port == id_REFCK) changed_cells.insert(user.cell->name); auto &drv = ctx->nets.at(net)->driver; - if (iter == 1 && drv.cell != nullptr && (drv.port == id_HFCLKOUT || drv.port == id_LFCLKOUT)) - changed_cells.insert(drv.cell->name); + if (iter == 1 && drv.cell != nullptr) { + if (drv.cell->type == id_OSC_CORE && (drv.port == id_HFCLKOUT || drv.port == id_LFCLKOUT)) + changed_cells.insert(drv.cell->name); + if (drv.cell->type == id_DCC && drv.port == id_CLKO) + changed_cells.insert(drv.cell->name); + if (drv.cell->type == id_DCS && drv.port == id_DCSOUT) + changed_cells.insert(drv.cell->name); + } } changed_nets.clear(); for (auto cell : changed_cells) { CellInfo *ci = ctx->cells.at(cell).get(); if (ci->type == id_DCC) { copy_constraint(ci, id_CLKI, id_CLKO, 1); + } else if (ci->type == id_DCS) { + // For DCC copy the worst case ("fastest") constraint + delay_t period_clk0, period_clk1; + bool have_clk0 = get_period(ci, id_CLK0, period_clk0); + bool have_clk1 = get_period(ci, id_CLK1, period_clk1); + if (have_clk0 && !have_clk1) { + copy_constraint(ci, id_CLK0, id_DCSOUT); + } else if (!have_clk0 && have_clk1) { + copy_constraint(ci, id_CLK1, id_DCSOUT); + } else if ( have_clk0 && have_clk1) { + set_period(ci, id_DCSOUT, std::min(period_clk0, period_clk1)); + } } else if (ci->type == id_OSC_CORE) { int div = int_or_default(ci->params, id_HF_CLK_DIV, 128); const float tol = 1.07f; // OSCA has +/-7% frequency tolerance, assume the worst case. From 237391c1b8047d248dc5aa27c7d78edeb86f61e9 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 15 Mar 2022 12:35:10 +0100 Subject: [PATCH 107/712] nexus: Corrected auto frequency constraint for LF output of OSCA Signed-off-by: Maciej Kurc --- nexus/pack.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index cc86699a6b..bb62ec852c 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2000,7 +2000,7 @@ struct NexusPacker copy_constraint(ci, id_CLKI, id_CLKO, 1); } else if (ci->type == id_DCS) { // For DCC copy the worst case ("fastest") constraint - delay_t period_clk0, period_clk1; + delay_t period_clk0 = 0, period_clk1 = 0; bool have_clk0 = get_period(ci, id_CLK0, period_clk0); bool have_clk1 = get_period(ci, id_CLK1, period_clk1); if (have_clk0 && !have_clk1) { @@ -2014,7 +2014,7 @@ struct NexusPacker int div = int_or_default(ci->params, id_HF_CLK_DIV, 128); const float tol = 1.07f; // OSCA has +/-7% frequency tolerance, assume the worst case. set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1) / tol)); - set_period(ci, id_LFCLKOUT, delay_t((1.0e3 / 10) / tol)); + set_period(ci, id_LFCLKOUT, delay_t((1.0e9 / 32) / tol)); } else if (ci->type == id_PLL_CORE) { static const std::array div{id_DIVA, id_DIVB, id_DIVC, id_DIVD, id_DIVE, id_DIVF}; static const std::array output{id_CLKOP, id_CLKOS, id_CLKOS2, From 2635bab2f1b1a32b2505e0918caa2a91c18ce571 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 16 Mar 2022 15:44:45 +0000 Subject: [PATCH 108/712] ecp5: Fix double-counting of FFs in report Signed-off-by: gatecat --- ecp5/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 02ce5aa13c..f65e992ee2 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -85,7 +85,7 @@ class Ecp5Packer used_ramwluts += 2; } if (is_ff(ctx, ci)) - used_ffs += 2; + ++used_ffs; } log_info("Logic utilisation before packing:\n"); auto pc = [](int used, int total) { return 100 * used / total; }; From d8244bb62d695cc0326815136caac90ed3571082 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 17 Mar 2022 19:19:38 +0000 Subject: [PATCH 109/712] mistral: Update to latest upstream Signed-off-by: gatecat --- .github/workflows/mistral_ci.yml | 2 +- mistral/bitstream.cc | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index a02026cfff..408179b47f 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: 0c2ab2b2c6af33fea1c20349be2e0068366ed615 + MISTRAL_REVISION: 6b0ce163d87200d0d5b7f330349aacf886f0f8be run: | source ./.github/ci/build_mistral.sh get_dependencies diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index c51050083f..80490f1f6f 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -210,7 +210,8 @@ struct MistralBitgen if (is_lutram) { for (int i = 0; i < 10; i++) { // Many MLAB settings apply to the whole LAB, not just the ALM - cv->bmux_m_set(block_type, pos, CycloneV::MODE, i, CycloneV::RAM); + cv->bmux_m_set(block_type, pos, CycloneV::TMODE, i, CycloneV::RAM); + cv->bmux_m_set(block_type, pos, CycloneV::BMODE, i, CycloneV::RAM); cv->bmux_n_set(block_type, pos, CycloneV::T_FEEDBACK_SEL, i, 1); } cv->bmux_r_set(block_type, pos, CycloneV::LUT_MASK, alm, 0xFFFFFFFFFFFFFFFFULL); // TODO: LUTRAM init @@ -222,11 +223,11 @@ struct MistralBitgen cv->bmux_n_set(block_type, pos, CycloneV::WRITE_PULSE_LENGTH, 0, 650); // picoseconds, presumably // TODO: understand how these enables really work cv->bmux_b_set(block_type, pos, CycloneV::EN2_EN, 0, false); - cv->bmux_b_set(block_type, pos, CycloneV::EN_SCLK_LOAD_WHAT, 0, true); - cv->bmux_m_set(block_type, pos, CycloneV::SCLR_MUX, 0, CycloneV::GIN2); + cv->bmux_b_set(block_type, pos, CycloneV::SCLR_DIS, 0, true); } else { - // Combinational mode - TODO: flop feedback - cv->bmux_m_set(block_type, pos, CycloneV::MODE, alm, alm_data.l6_mode ? CycloneV::L6 : CycloneV::L5); + // Combinational mode - TODO: flop feedback and more modes... + cv->bmux_m_set(block_type, pos, CycloneV::TMODE, alm, alm_data.l6_mode ? CycloneV::C_E : CycloneV::E_0); + cv->bmux_m_set(block_type, pos, CycloneV::BMODE, alm, alm_data.l6_mode ? CycloneV::D_E : CycloneV::E_1); // LUT function cv->bmux_r_set(block_type, pos, CycloneV::LUT_MASK, alm, ctx->compute_lut_mask(lab, alm)); } @@ -300,12 +301,17 @@ struct MistralBitgen // SCLR if (ff->ffInfo.ctrlset.sclr.net != nullptr) { cv->bmux_b_set(block_type, pos, CycloneV::SCLR_INV, 0, ff->ffInfo.ctrlset.sclr.inverted); + cv->bmux_b_set(block_type, pos, CycloneV::SCLR_DIS, 0, false); } else { cv->bmux_b_set(block_type, pos, sclr_dis[i / 2], alm, true); } // SLOAD if (ff->ffInfo.ctrlset.sload.net != nullptr) { cv->bmux_b_set(block_type, pos, sload_en[i / 2], alm, true); + if (ff->ffInfo.ctrlset.sload.net->name == ctx->id("$PACKER_GND_NET")) { + // force-disabled LOAD (see workaround in assign_ff_info) + cv->bmux_b_set(block_type, pos, CycloneV::SLOAD_EN, 0, false); + } cv->bmux_b_set(block_type, pos, CycloneV::SLOAD_INV, 0, ff->ffInfo.ctrlset.sload.inverted); } } @@ -340,18 +346,18 @@ struct MistralBitgen const std::array aclr_inp{CycloneV::ACLR0_SEL, CycloneV::ACLR1_SEL}; for (int i = 0; i < 2; i++) { - // Quartus seems to set unused ACLRs to CLKI2... - if (!lab_data.aclr_used[i]) - cv->bmux_m_set(block_type, pos, aclr_inp[i], 0, CycloneV::CLKI2); - else - cv->bmux_m_set(block_type, pos, aclr_inp[i], 0, (i == 1) ? CycloneV::GIN0 : CycloneV::GIN1); + // Quartus seems to set unused ACLRs to ACLR0 + if (lab_data.aclr_used[i]) + cv->bmux_m_set(block_type, pos, aclr_inp[i], 0, (i == 1) ? CycloneV::DIN2 : CycloneV::DIN3); + else if (i == 0) + cv->bmux_m_set(block_type, pos, aclr_inp[i], 0, CycloneV::ACLR0); } for (int i = 0; i < 3; i++) { // Check for fabric->clock routing if (ctx->wires_connected( ctx->get_port(block_type, CycloneV::pos2x(pos), CycloneV::pos2y(pos), -1, CycloneV::DATAIN, 0), lab_data.clk_wires[i])) - cv->bmux_m_set(block_type, pos, CycloneV::CLKA_SEL, 0, CycloneV::GIN2); + cv->bmux_m_set(block_type, pos, CycloneV::CLKA_SEL, 0, CycloneV::DIN0); } } From 14d53dfec8dba93996f5c2877d60f6ed167d14fb Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 17 Mar 2022 19:24:05 +0000 Subject: [PATCH 110/712] clangformat Signed-off-by: gatecat --- ice40/pack.cc | 3 ++- nexus/arch.cc | 2 +- nexus/pack.cc | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index f533941ddd..2b5def4666 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -59,7 +59,8 @@ static void pack_lut_lutffs(Context *ctx) if (ctx->verbose) log_info("found attached dff %s\n", dff->name.c_str(ctx)); auto dff_bel = dff->attrs.find(id_BEL); - if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) { + if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && + lut_bel->second != dff_bel->second) { // Locations don't match, can't pack } else { lut_to_lc(ctx, ci, packed.get(), false); diff --git a/nexus/arch.cc b/nexus/arch.cc index 39e51a5b15..9679c3fbed 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -486,7 +486,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort if (fromPort == id_CLK) return false; // don't include delays that are actually clock-to-out here return lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay); - } else if(cell->type == id_DCS) { + } else if (cell->type == id_DCS) { if (fromPort == id_SELFORCE || fromPort == id_SEL) { return false; } diff --git a/nexus/pack.cc b/nexus/pack.cc index bb62ec852c..26da3dc5b0 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2007,14 +2007,14 @@ struct NexusPacker copy_constraint(ci, id_CLK0, id_DCSOUT); } else if (!have_clk0 && have_clk1) { copy_constraint(ci, id_CLK1, id_DCSOUT); - } else if ( have_clk0 && have_clk1) { + } else if (have_clk0 && have_clk1) { set_period(ci, id_DCSOUT, std::min(period_clk0, period_clk1)); } } else if (ci->type == id_OSC_CORE) { int div = int_or_default(ci->params, id_HF_CLK_DIV, 128); const float tol = 1.07f; // OSCA has +/-7% frequency tolerance, assume the worst case. set_period(ci, id_HFCLKOUT, delay_t((1.0e6 / 450) * (div + 1) / tol)); - set_period(ci, id_LFCLKOUT, delay_t((1.0e9 / 32) / tol)); + set_period(ci, id_LFCLKOUT, delay_t((1.0e9 / 32) / tol)); } else if (ci->type == id_PLL_CORE) { static const std::array div{id_DIVA, id_DIVB, id_DIVC, id_DIVD, id_DIVE, id_DIVF}; static const std::array output{id_CLKOP, id_CLKOS, id_CLKOS2, From 29654c52be93df192ce3191b39e2d4ae57120d08 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 17 Mar 2022 20:01:44 +0000 Subject: [PATCH 111/712] ci: Fixes for latest RapidWright Signed-off-by: gatecat --- .github/ci/build_interchange.sh | 3 ++- .github/workflows/interchange_ci.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/ci/build_interchange.sh b/.github/ci/build_interchange.sh index 674bd5c100..3976ce4ea5 100755 --- a/.github/ci/build_interchange.sh +++ b/.github/ci/build_interchange.sh @@ -48,7 +48,8 @@ function get_dependencies { # Install RapidWright git clone https://github.com/Xilinx/RapidWright.git ${RAPIDWRIGHT_PATH} pushd ${RAPIDWRIGHT_PATH} - make update_jars + ./gradlew updateJars --no-watch-fs + make compile popd fi } diff --git a/.github/workflows/interchange_ci.yml b/.github/workflows/interchange_ci.yml index 16b502053f..27b19c5a54 100644 --- a/.github/workflows/interchange_ci.yml +++ b/.github/workflows/interchange_ci.yml @@ -16,7 +16,7 @@ jobs: - name: Install run: | sudo apt-get update - sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig + sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig locales libtinfo-dev - name: ccache uses: hendrikmuhs/ccache-action@v1 From 49cc4ca30b6698b93bbe90c21bedbc3b31c7c946 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Wed, 16 Mar 2022 19:24:08 +0100 Subject: [PATCH 112/712] Nexus: Fixed OSCA parameters, add pll default parameters Signed-off-by: Maciej Dudek --- nexus/fasm.cc | 144 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index e2eb2305e6..b2cb03fa5e 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -521,8 +521,10 @@ struct NexusFasmWriter write_enum(cell, "HFDIV_FABRIC_EN", "ENABLED"); write_enum(cell, "LF_FABRIC_EN"); write_enum(cell, "LF_OUTPUT_EN"); + write_enum(cell, "DTR_EN", "ENABLED"); write_enum(cell, "DEBUG_N", "DISABLED"); write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param(cell, id_HF_CLK_DIV, 8, 0).intval, 8); + write_int_vector(stringf("HF_SED_SEC_DIV[7:0]"), 1, 8); write_cell_muxes(cell); pop(2); } @@ -681,6 +683,125 @@ struct NexusFasmWriter {"V2I_PP_ICTRL", 5}, }; + const dict pll_default_params = { + {"BW_CTL_BIAS", "0b0101"}, + {"CLKOP_TRIM", "0b0000"}, + {"CLKOS_TRIM", "0b0000"}, + {"CLKOS2_TRIM", "0b0000"}, + {"CLKOS3_TRIM", "0b0000"}, + {"CLKOS4_TRIM", "0b0000"}, + {"CLKOS5_TRIM", "0b0000"}, + {"CRIPPLE", "5P"}, + {"CSET", "40P"}, + {"DELAY_CTRL", "200PS"}, + {"DELA", "0"}, + {"DELB", "0"}, + {"DELC", "0"}, + {"DELD", "0"}, + {"DELE", "0"}, + {"DELF", "0"}, + {"DIRECTION", "DISABLED"}, + {"DIVA", "0"}, + {"DIVB", "0"}, + {"DIVC", "0"}, + {"DIVD", "0"}, + {"DIVE", "0"}, + {"DIVF", "0"}, + {"DYN_SEL", "0b000"}, + {"DYN_SOURCE", "STATIC"}, + {"ENCLK_CLKOP", "DISABLED"}, + {"ENCLK_CLKOS", "DISABLED"}, + {"ENCLK_CLKOS2", "DISABLED"}, + {"ENCLK_CLKOS3", "DISABLED"}, + {"ENCLK_CLKOS4", "DISABLED"}, + {"ENCLK_CLKOS5", "DISABLED"}, + {"ENABLE_SYNC", "DISABLED"}, + {"FAST_LOCK_EN", "ENABLED"}, + {"V2I_1V_EN", "DISABLED"}, + {"FBK_CUR_BLE", "0b00000000"}, + {"FBK_EDGE_SEL", "POSITIVE"}, + {"FBK_IF_TIMING_CTL", "0b00"}, + {"FBK_INTEGER_MODE", "DISABLED"}, + {"FBK_MASK", "0b00001000"}, + {"FBK_MMD_DIG", "8"}, + {"FBK_MMD_PULS_CTL", "0b0000"}, + {"FBK_MODE", "0b00"}, + {"FBK_PI_BYPASS", "NOT_BYPASSED"}, + {"FBK_PI_RC", "0b1100"}, + {"FBK_PR_CC", "0b0000"}, + {"FBK_PR_IC", "0b1000"}, + {"FLOAT_CP", "DISABLED"}, + {"FLOCK_CTRL", "2X"}, + {"FLOCK_EN", "ENABLED"}, + {"FLOCK_SRC_SEL", "REFCLK"}, + {"FORCE_FILTER", "DISABLED"}, + {"I_CTRL", "10UA"}, + {"IPI_CMP", "0b1000"}, + {"IPI_CMPN", "0b0011"}, + {"IPI_COMP_EN", "DISABLED"}, + {"IPP_CTRL", "0b1000"}, + {"IPP_SEL", "0b1111"}, + {"KP_VCO", "0b11001"}, + {"LDT_INT_LOCK_STICKY", "DISABLED"}, + {"LDT_LOCK", "1536CYC"}, + {"LDT_LOCK_SEL", "U_FREQ"}, + {"LEGACY_ATT", "DISABLED"}, + {"LOAD_REG", "DISABLED"}, + {"OPENLOOP_EN", "DISABLED"}, + {"PHIA", "0"}, + {"PHIB", "0"}, + {"PHIC", "0"}, + {"PHID", "0"}, + {"PHIE", "0"}, + {"PHIF", "0"}, + {"PLLPDN_EN", "DISABLED"}, + {"PLLPD_N", "UNUSED"}, + {"PLLRESET_ENA", "DISABLED"}, + {"REF_INTEGER_MODE", "DISABLED"}, + {"REF_MASK", "0b00000000"}, + {"REF_MMD_DIG", "8"}, + {"REF_MMD_IN", "0b00001000"}, + {"REF_MMD_PULS_CTL", "0b0000"}, + {"REF_TIMING_CTL", "0b00"}, + {"REFIN_RESET", "SET"}, + {"RESET_LF", "DISABLED"}, + {"ROTATE", "DISABLED"}, + {"SEL_OUTA", "DISABLED"}, + {"SEL_OUTB", "DISABLED"}, + {"SEL_OUTC", "DISABLED"}, + {"SEL_OUTD", "DISABLED"}, + {"SEL_OUTE", "DISABLED"}, + {"SEL_OUTF", "DISABLED"}, + {"SLEEP", "DISABLED"}, + {"SSC_DELTA", "0b000000000000000"}, + {"SSC_DELTA_CTL", "0b00"}, + {"SSC_DITHER", "DISABLED"}, + {"SSC_EN_CENTER_IN", "DOWN_TRIANGLE"}, + {"SSC_EN_SDM", "DISABLED"}, + {"SSC_EN_SSC", "DISABLED"}, + {"SSC_F_CODE", "0b000000000000000"}, + {"SSC_N_CODE", "0b000010100"}, + {"SSC_ORDER", "SDM_ORDER2"}, + {"SSC_PI_BYPASS", "NOT_BYPASSED"}, + {"SSC_REG_WEIGHTING_SEL", "0b000"}, + {"SSC_SQUARE_MODE", "DISABLED"}, + {"SSC_STEP_IN", "0b0000000"}, + {"SSC_TBASE", "0b000000000000"}, + {"STDBY_ATT", "DISABLED"}, + {"TRIMOP_BYPASS_N", "BYPASSED"}, + {"TRIMOS_BYPASS_N", "BYPASSED"}, + {"TRIMOS2_BYPASS_N", "BYPASSED"}, + {"TRIMOS3_BYPASS_N", "BYPASSED"}, + {"TRIMOS4_BYPASS_N", "BYPASSED"}, + {"TRIMOS5_BYPASS_N", "BYPASSED"}, + {"V2I_KVCO_SEL", "85"}, + {"V2I_PP_ICTRL", "0b00110"}, + {"V2I_PP_RES", "10K"}, + {"CLKMUX_FB", "CMUX_CLKOP"}, + {"SEL_FBK", "DIVA"}, + {"DIV_DEL", "0b0000001"}, + }; + // Which MIPI params are 'word' values const dict dphy_word_params = { {"CM", 8}, {"CN", 5}, {"CO", 3}, {"RSEL", 2}, {"RXCDRP", 2}, @@ -691,6 +812,14 @@ struct NexusFasmWriter }; /* clang-format on */ + static bool is_number(std::string s) { + for (auto c : s) { + if(!isdigit(c)) + return false; + } + return true; + } + // Write out config for some kind of PLL cell void write_pll(const CellInfo *cell) { @@ -701,14 +830,25 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(); push(stringf("IP_%s", ctx->nameOf(IdString(ctx->bel_data(bel).name)))); - for (auto ¶m : cell->params) { + CellInfo temp(*cell); + for (auto param : cell->params) temp.params.insert(param); + for (auto &value : pll_default_params) { + IdString n = IdString(ctx, value.first); + if (!temp.params.count(n)) { + if (is_number(value.second)) + temp.params[ctx->id(value.first)] = Property(std::stoi(value.second), 32); + else + temp.params[ctx->id(value.first)] = Property::from_string(value.second); + } + } + for (auto ¶m : temp.params) { const std::string &name = param.first.str(ctx); if (is_mux_param(name) || name == "CLKMUX_FB" || name == "SEL_FBK") continue; auto fnd_word = pll_word_params.find(name); if (fnd_word != pll_word_params.end()) { write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), - ctx->parse_lattice_param(cell, param.first, fnd_word->second, 0).as_int64(), + ctx->parse_lattice_param(&temp, param.first, fnd_word->second, 0).as_int64(), fnd_word->second); } else { write_bit(stringf("%s.%s", name.c_str(), param.second.as_string().c_str())); From 5e9236f9d4679d4596f0e593cdeff674229f5818 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 18 Mar 2022 18:54:12 +0000 Subject: [PATCH 113/712] mistral: Updated CLK mux select name Signed-off-by: gatecat --- .github/workflows/mistral_ci.yml | 2 +- mistral/bitstream.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml index 408179b47f..b0bbfb521e 100644 --- a/.github/workflows/mistral_ci.yml +++ b/.github/workflows/mistral_ci.yml @@ -21,7 +21,7 @@ jobs: - name: Execute build nextpnr env: MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: 6b0ce163d87200d0d5b7f330349aacf886f0f8be + MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9 run: | source ./.github/ci/build_mistral.sh get_dependencies diff --git a/mistral/bitstream.cc b/mistral/bitstream.cc index 80490f1f6f..3e1b8b6642 100644 --- a/mistral/bitstream.cc +++ b/mistral/bitstream.cc @@ -114,7 +114,7 @@ struct MistralBitgen { (void)ci; // currently unused auto pos = CycloneV::xy2pos(x, y); - cv->bmux_r_set(CycloneV::CMUXHG, pos, CycloneV::INPUT_SELECT, bi, 0x1b); // hardcode to general routing + cv->bmux_r_set(CycloneV::CMUXHG, pos, CycloneV::INPUT_SEL, bi, 0x1b); // hardcode to general routing cv->bmux_m_set(CycloneV::CMUXHG, pos, CycloneV::TESTSYN_ENOUT_SELECT, bi, CycloneV::PRE_SYNENB); } From 774d3944b307a7b26164f728b5910f26b7d873f6 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 19 Mar 2022 18:43:31 +0000 Subject: [PATCH 114/712] parallel_refine: Fix compile error with some configs Signed-off-by: gatecat --- common/parallel_refine.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/parallel_refine.cc b/common/parallel_refine.cc index bc665cd3ca..a868ca582f 100644 --- a/common/parallel_refine.cc +++ b/common/parallel_refine.cc @@ -546,6 +546,7 @@ struct ThreadState bool accept_move() { + static constexpr double epsilon = 1e-20; double delta = g.cfg.lambda * (timing_delta / std::max(epsilon, g.total_timing_cost)) + (1.0 - g.cfg.lambda) * (double(wirelen_delta) / std::max(epsilon, g.total_wirelen)); return delta < 0 || @@ -565,7 +566,6 @@ struct ThreadState return true; } - static constexpr double epsilon = 1e-20; bool single_cell_swap(CellInfo *cell, BelId new_bel) { NPNR_ASSERT(moved_cells.empty()); From bb923c7732dbf8c930c8af85acd6f0583115af8e Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Sat, 19 Mar 2022 19:44:08 +0100 Subject: [PATCH 115/712] Gowin: use global VCC and VSS nets (#956) * use global VCC and VSS nets * derp * remove init parameter --- gowin/arch.cc | 11 ++++++++++- gowin/cells.cc | 4 ++++ gowin/constids.inc | 2 ++ gowin/pack.cc | 15 ++++++--------- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 6213124f99..4400a55458 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -440,7 +440,9 @@ IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString { const std::string &wirename = wire.str(this); char buf[32]; - if (wirename == "VCC" || wirename == "GND") { + if (wirename == "VCC" || wirename == "VSS") { + row = 0; + col = 0; return wire; } if (!isdigit(wirename[1]) || !isdigit(wirename[2]) || !isdigit(wirename[3])) { @@ -949,6 +951,13 @@ Arch::Arch(ArchArgs args) : args(args) package_name.c_str(this), speed_id.c_str(this)); // setup db + // add global VCC and GND bels + addBel(id_GND, id_GND, Loc(0, 0, 998), true); + addWire(id_VSS, id_VSS, 0, 0); + addBelOutput(id_GND, id_G, id_VSS); + addBel(id_VCC, id_VCC, Loc(0, 0, 999), true); + addWire(id_VCC, id_VCC, 0, 0); + addBelOutput(id_VCC, id_V, id_VCC); char buf[32]; // The reverse order of the enumeration simplifies the creation // of MUX2_LUT8s: they need the existence of the wire on the right. diff --git a/gowin/cells.cc b/gowin/cells.cc index 8e450b5140..c3b2178259 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -65,6 +65,10 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addOutput(id_O); } else if (type == id_GSR) { new_cell->addInput(id_GSRI); + } else if (type == id_GND) { + new_cell->addOutput(id_G); + } else if (type == id_VCC) { + new_cell->addOutput(id_V); } else { log_error("unable to create generic cell of type %s\n", type.c_str(ctx)); } diff --git a/gowin/constids.inc b/gowin/constids.inc index 125fdc74b4..b293293b66 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -787,6 +787,8 @@ X(SUM) X(CIN) X(COUT) X(OF) +X(V) +X(G) // timing X(X0) diff --git a/gowin/pack.cc b/gowin/pack.cc index 9f0a2478e6..d31b4a84c8 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -611,20 +611,17 @@ static void pack_constants(Context *ctx) { log_info("Packing constants..\n"); - std::unique_ptr gnd_cell = create_generic_cell(ctx, id_SLICE, "$PACKER_GND"); - gnd_cell->params[id_INIT] = Property(0, 1 << 4); + std::unique_ptr gnd_cell = create_generic_cell(ctx, id_GND, "$PACKER_GND"); auto gnd_net = std::make_unique(ctx->id("$PACKER_GND_NET")); gnd_net->driver.cell = gnd_cell.get(); - gnd_net->driver.port = id_F; - gnd_cell->ports.at(id_F).net = gnd_net.get(); + gnd_net->driver.port = id_G; + gnd_cell->ports.at(id_G).net = gnd_net.get(); - std::unique_ptr vcc_cell = create_generic_cell(ctx, id_SLICE, "$PACKER_VCC"); - // Fill with 1s - vcc_cell->params[id_INIT] = Property(Property::S1).extract(0, (1 << 4), Property::S1); + std::unique_ptr vcc_cell = create_generic_cell(ctx, id_VCC, "$PACKER_VCC"); auto vcc_net = std::make_unique(ctx->id("$PACKER_VCC_NET")); vcc_net->driver.cell = vcc_cell.get(); - vcc_net->driver.port = id_F; - vcc_cell->ports.at(id_F).net = vcc_net.get(); + vcc_net->driver.port = id_V; + vcc_cell->ports.at(id_V).net = vcc_net.get(); std::vector dead_nets; From b2348281252f7dc4ea052269a5c5db02f7c03803 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 21 Mar 2022 19:58:36 +0000 Subject: [PATCH 116/712] docs: Initial reference for the Viaduct 'uarch' API Signed-off-by: gatecat --- docs/generic.md | 8 ++- docs/viaduct.md | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 docs/viaduct.md diff --git a/docs/generic.md b/docs/generic.md index 96db872e84..108d41d72d 100644 --- a/docs/generic.md +++ b/docs/generic.md @@ -1,8 +1,12 @@ # nextpnr Generic Architecture -Instead of implementing the [C++ API](archapi.md), you can programmatically +Instead of implementing the full [C++ API](archapi.md), you can programmatically build up a description of an FPGA using the generic architecture and the -Python API. +Python API, or the [Viaduct C++ API](viaduct.md) (described further in its own +document). + +The Viaduct API allows more complex constraints to be implemented and has shorter +startup times than using the Python API. A basic packer is provided that supports LUTs, flipflops and IO buffer insertion. Packing could also be implemented using the Python API. diff --git a/docs/viaduct.md b/docs/viaduct.md new file mode 100644 index 0000000000..a59310f14d --- /dev/null +++ b/docs/viaduct.md @@ -0,0 +1,131 @@ +# Viaduct - a series of small arches + +Viaduct is a C++-based successor to the Python generic API that gets most of the benefits of a full-custom nextpnr architecture, with the simplicity of a harness to build from and a predefined flat set of data structures for the placement and routing resources. + +Like the Python generic API, the routing graph can be built programmatically, or loaded from an external data source at startup. However, the Viaduct framework provides considerably improved startup times by relying less on strings and eliminating the C++/Python boundary; and also enables more complex architectures to be modeled with arbitrary place-and-route time constraints implemented as code, in the spirit of nextpnr. + +A Viaduct implementation is called a 'uarch' (microarch), because it's smaller than a full architecture. + +Viaduct implementations, including some examples, are located as subfolders of `generic/viaduct/`. + +## Viaduct API Reference + +### Initialisation + +A Viaduct uarch must override `ViaductAPI` - see `generic/viaduct_api.h`. This contains virtual methods to be optionally overridden, in most cases only a small number of these need be. + +```c++ +void init(Context *ctx) +``` + +This should perform device resources initialisation. uarches should always call the superclass `ViaductAPI::init(ctx)` first, too. + +Bels (placement locations), wires ('metal' interconnect) and pips (programmable switches that connect wires) can be dynamically created by calling the methods of `Context` described in the [generic arch docs](coding.md) - the most important methods to start with are: + +```c++ +ctx->addWire(IdStringList name, IdString type, int x, int y); +ctx->addPip(IdStringList name, IdString type, WireId srcWire, WireId dstWire, float delay, Loc loc); +ctx->addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidden); +ctx->addBelInput(BelId bel, IdString name, WireId wire); +ctx->addBelOutput(BelId bel, IdString name, WireId wire); +ctx->addBelInout(BelId bel, IdString name, WireId wire); +``` + +### Helpers + +nextpnr uses an indexed, interned string type for performance and object names (for bels, wires and pips) are based on lists of these. To performantly build these; you can add a `ViaductHelpers` instance to your uarch, call `init(ctx)` on it, and then use the `xy_id(x, y, base)` member functions of this. For example: + +```c++ +ViaductHelpers h; +h.init(ctx); +ctx->addWire(h.xy_id(13, 45, ctx->id("CLK0")), ctx->id("CLK"), 13, 45); +``` + +To create a wire named `X13/Y45/CLK0`. + +### Constant IDs + +In some cases, such as during packing and validity checks, `IdString`s for strings such as common port names will be needed a large number of times. To avoid the string hash and compare associated with `ctx->id("string")`, you can use the "constids" support. To use this: + + - create a 'constids.inc' file in your uarch folder containing one ID per line; inside X( ). For example: +``` +X(LUT4) +X(DFF) +X(CLK) +X(D) +X(F) +``` + - set the `VIADUCT_CONSTIDS` macro to the path to this file relative to the generic arch base + - in the same file as `init` is implemented; also define the `GEN_INIT_CONSTIDS` macro before the `viaduct_constids.h` include to create `init_uarch_constids`, which you should call in your `init` implementation. + - in any file you need the constant `IdString`s, include `viaduct_constids.h` and the ids will be accessible as constants named like `id_LUT4`. + +### Constraints + +```c++ +bool checkBelAvail(BelId bel) const; +bool isValidBelForCellType(IdString cell_type, BelId bel) const; +bool isBelLocationValid(BelId bel) const; +bool checkWireAvail(WireId wire) const; +bool checkPipAvail(PipId pip) const; +bool checkPipAvailForNet(PipId pip, NetInfo *net) const; +``` + +These can be overriden, if needed to implement nextpnr's system of arbitrary,architecture-defined constraints on legal placements and the availability of placement and routing resources. These could be used to implement placement rules inside tiles (like clocks that are shared between flipflops); or disable one routing resource when a conflicting one is used. They only need to be overriden, and return false, where a resource is unavailable due to a specific, custom constraint and not just because that resource itself is occupied. + +For more information on terminology, see [FAQ](faq.md); for references of these functions see the [Arch API](archapi.md) docs; and for some general hints see the [Coding Tips](coding.md). + +uarches may update internal, constraint-related structures based on placement and routing updates by optionally overriding the 'hook' functions called whenever bindings are changed. + +```c++ +void notifyBelChange(BelId bel, CellInfo *cell); +void notifyWireChange(WireId wire, NetInfo *net); +void notifyPipChange(PipId pip, NetInfo *net); +``` + +These will be called with `cell` or `net` pointing to the object the resource is being bound to for a bind; or `nullptr` for an unbind. + +### Packing + +Although arches can implement as much or as little packing as they like, nextpnr leans towards doing minimal pre-placement packing and leaving the combination of LUTs and flipflops into tiles, and similar tasks, down to placement validity checks (`checkBelAvail`). + +Any packing tasks that do need to be done; for example cleaning up top level IO pairing cells that should always stay together using relative constraints, should be done by overriding the `pack` method: + +```c++ +void pack(); +``` + + +There are also hooks to perform custom transformations or steps in-between and after placement and routing: + +```c++ +void prePlace(); +void postPlace(); +void preRoute(); +void postRoute(); +``` + +The most common use for this would be to implement a custom bitstream generation step (or similar intermediate format) inside `postRoute` on the final design. Another example use case would be to implement a custom global clock routing pass in `preRoute`. + +### ViaductArch + +As well as creating the uarch class that derives from `ViaductAPI`, you also need to create a factory for it by creating a singleton of a class that derives from `ViaductArch`. This should, in its constructor, construct `ViaductArch` with the arch name, and also implement the `create` function to return a new instance of your `ViaductAPI` implementation. For example: + +```c++ +struct ExampleArch : ViaductArch +{ + ExampleArch() : ViaductArch("example"){}; + std::unique_ptr create(const dict &args) + { + return std::make_unique(); + } +} exampleArch; +``` + +### Adding a new uarch + +The reference above provides an overview of what a Viaduct uarch must implement, it's also recommended to look at the `generic` and `okami` examples in `generic/viaduct`. New uarches should have their source contained in subfolders of `generic/viaduct`; and added to `VIADUCT_UARCHES` list in `generic/family.cmake`. + +Once you've implemented `ViaductAPI` and created the `ViaductArch` singleton, you should be able to run nextpnr with the arch by running `nextpnr-generic --uarch `. + + + From 69b4461f5531f6d05ed8bd4342a7dcecf774b8ef Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 22 Mar 2022 06:15:29 +1000 Subject: [PATCH 117/712] gowin: Name the constants (#958) Place arbitrary constants side by side to avoid conflicts. Signed-off-by: YRabbit --- gowin/arch.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 4400a55458..fa0a66a3e8 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -952,10 +952,10 @@ Arch::Arch(ArchArgs args) : args(args) // setup db // add global VCC and GND bels - addBel(id_GND, id_GND, Loc(0, 0, 998), true); + addBel(id_GND, id_GND, Loc(0, 0, BelZ::gnd_0_z), true); addWire(id_VSS, id_VSS, 0, 0); addBelOutput(id_GND, id_G, id_VSS); - addBel(id_VCC, id_VCC, Loc(0, 0, 999), true); + addBel(id_VCC, id_VCC, Loc(0, 0, BelZ::vcc_0_z), true); addWire(id_VCC, id_VCC, 0, 0); addBelOutput(id_VCC, id_V, id_VCC); char buf[32]; From 07c8506372e48602787dda7fc3edd7b3aef31a37 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 25 Mar 2022 15:55:07 +0000 Subject: [PATCH 118/712] ice40: Improve error reporting for PLL conflicts Signed-off-by: gatecat --- ice40/pack.cc | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index 2b5def4666..263903b090 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -962,31 +962,56 @@ static void place_plls(Context *ctx) // Find a BEL for it BelId found_bel; + std::string conflict_str = ""; for (auto bel_pll : pll_all_bels) { - if (pll_used_bels.count(bel_pll.first)) + if (pll_used_bels.count(bel_pll.first)) { + conflict_str += + stringf(" PLL bel '%s' is already used by '%s'.\n", ctx->nameOfBel(bel_pll.first), + pll_used_bels.at(bel_pll.first)->name.c_str(ctx)); continue; + } BelPin pll_io_a, pll_io_b; BelId gb_a, gb_b; std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = bel_pll.second; if (bel2io.count(pll_io_a.bel)) { if (pll_io_a.bel == pad_bel) could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci); + auto conflict_pin = ctx->get_bel_package_pin(pll_io_a.bel); + conflict_str += + stringf(" PLL bel '%s' cannot be used as it conflicts with input '%s' on pin '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2io.at(pll_io_a.bel)->name.c_str(ctx), + conflict_pin.c_str()); continue; } - if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci)) + if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci)) { + auto conflict_pin = ctx->get_bel_package_pin(pll_io_b.bel); + conflict_str += + stringf(" PLL bel '%s' cannot be used as it conflicts with input '%s' on pin '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2io.at(pll_io_b.bel)->name.c_str(ctx), + conflict_pin.c_str()); continue; - if (gb_a_used && bel2gb.count(gb_a)) + } + if (gb_a_used && bel2gb.count(gb_a)) { + conflict_str += stringf( + " PLL bel '%s' cannot be used as it conflicts with global buffer '%s' at '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2gb.at(gb_a)->name.c_str(ctx), ctx->nameOfBel(gb_a)); continue; - if (gb_b_used && bel2gb.count(gb_b)) + } + if (gb_b_used && bel2gb.count(gb_b)) { + conflict_str += stringf( + " PLL bel '%s' cannot be used as it conflicts with global buffer '%s' at '%s'.\n", + ctx->nameOfBel(bel_pll.first), bel2gb.at(gb_b)->name.c_str(ctx), ctx->nameOfBel(gb_b)); continue; + } found_bel = bel_pll.first; break; } // Apply constrain & Inform user of result - if (found_bel == BelId()) - log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n", ci->name.c_str(ctx), - could_be_pad ? " Did you mean to use a PAD PLL ?" : ""); + if (found_bel == BelId()) { + log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found.%s\n%s\n", ci->name.c_str(ctx), + could_be_pad ? " Did you mean to use a PAD PLL ?" : "", conflict_str.c_str()); + } log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->nameOfBel(found_bel)); if (could_be_pad) From be8d3fd74dae297d79e41165c2141aadef3d6582 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 26 Mar 2022 20:56:30 +1000 Subject: [PATCH 119/712] gowin: Consider the peculiarity of GW1BR-9C The GW1NR-9C chip ODDR implementation differs from all other supported chips by two suspicious inputs. Signed-off-by: YRabbit --- gowin/arch.cc | 15 +++++++++++++++ gowin/arch.h | 3 +++ gowin/constids.inc | 2 ++ gowin/pack.cc | 7 +++++++ 4 files changed, 27 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index fa0a66a3e8..dc40b17989 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1158,6 +1158,21 @@ Arch::Arch(ArchArgs args) : args(args) snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelInput(belname, id_CLK, id(buf)); + const PairPOD *xxx_port = pairLookup(bel->ports.get(), bel->num_ports, ID_XXX_VSS); + if (xxx_port != nullptr) { + ddr_has_extra_inputs = true; + portname = IdString(xxx_port->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_XXX_VSS, id(buf)); + } + xxx_port = pairLookup(bel->ports.get(), bel->num_ports, ID_XXX_VCC); + if (xxx_port != nullptr) { + ddr_has_extra_inputs = true; + portname = IdString(xxx_port->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_XXX_VCC, id(buf)); + } + if (oddrc) { portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_CE)->src_id); snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); diff --git a/gowin/arch.h b/gowin/arch.h index c8392e7e4e..bc29a59bf9 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -470,6 +470,9 @@ struct Arch : BaseArch void updateClockSpinesCache(IdString spine_id, IdString wire_id); void fixClockSpineDecals(void); + // XXX GW1N-9C DDR quirk + bool ddr_has_extra_inputs = false; + // Permissible combinations of modes in a single slice std::map dff_comp_mode; }; diff --git a/gowin/constids.inc b/gowin/constids.inc index b293293b66..8159cc108d 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -681,6 +681,8 @@ X(IOBJS) // IOLOGIC X(TX) +X(XXX_VSS) +X(XXX_VCC) X(OBUF_TYPE) X(SBUF) X(DBUF) diff --git a/gowin/pack.cc b/gowin/pack.cc index d31b4a84c8..28370a7557 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -798,6 +798,13 @@ static void pack_iologic(Context *ctx) ci->attrs[id_IOBUF] = 1; } } + // if have XXX_ inputs connect them + if (ctx->ddr_has_extra_inputs) { + ci->addInput(id_XXX_VSS); + ci->connectPort(id_XXX_VSS, ctx->nets[ctx->id("$PACKER_GND_NET")].get()); + ci->addInput(id_XXX_VCC); + ci->connectPort(id_XXX_VCC, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); + } } break; default: break; From 12b38bab6d630e14381870f51ccfd52c5a10b337 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Sun, 27 Mar 2022 22:15:12 +0200 Subject: [PATCH 120/712] gowin: Add bels for oscillator --- gowin/arch.cc | 38 ++++++++++++++++++++++++++++++++++++++ gowin/constids.inc | 8 ++++++++ 2 files changed, 46 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index dc40b17989..2c1e50b740 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1009,6 +1009,44 @@ Arch::Arch(ArchArgs args) : args(args) snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelInput(belname, id_GSRI, id(buf)); break; + case ID_OSC: + snprintf(buf, 32, "R%dC%d_OSC", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_OSC, Loc(col, row, 0), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_OSCOUT, id(buf)); + break; + case ID_OSCH: + snprintf(buf, 32, "R%dC%d_OSCH", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_OSCH, Loc(col, row, 0), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_OSCOUT, id(buf)); + break; + case ID_OSCF: + snprintf(buf, 32, "R%dC%d_OSCF", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_OSCF, Loc(col, row, 0), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_OSCOUT, id(buf)); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCEN)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_OSCEN, id(buf)); + break; + case ID_OSCZ: + snprintf(buf, 32, "R%dC%d_OSCZ", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_OSCZ, Loc(col, row, 0), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_OSCOUT, id(buf)); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCEN)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_OSCEN, id(buf)); + break; // fall through the ++ case ID_LUT7: z++; diff --git a/gowin/constids.inc b/gowin/constids.inc index 8159cc108d..d2a6b171de 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -758,6 +758,12 @@ X(GSR) X(GSR0) X(GSRI) +// Oscillators +X(OSC) +X(OSCZ) +X(OSCH) +X(OSCF) + // primitive attributes X(INIT) X(FF_USED) @@ -791,6 +797,8 @@ X(COUT) X(OF) X(V) X(G) +X(OSCOUT) +X(OSCEN) // timing X(X0) From 5a9ddc067588c895a483dbb314f3ff72c72fabe2 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 28 Mar 2022 14:06:09 +0100 Subject: [PATCH 121/712] ice40: Merge driving LUT<=2s into carry-only LCs Signed-off-by: gatecat --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- ice40/arch.cc | 29 ++++++++++++++-- ice40/archdefs.h | 1 + ice40/pack.cc | 63 ++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index d1e7b47bf9..6561ed59a0 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -29,7 +29,7 @@ RUN set -e -x ;\ cd /usr/local/src ;\ git clone --recursive https://github.com/YosysHQ/icestorm.git ;\ cd icestorm ;\ - git reset --hard 4bc68c9620e6be20f8fe10d20f84681d80beac23 ;\ + git reset --hard 9f66f9ce16941c6417813cb87653c735a78b53ae ;\ make -j $(nproc) ;\ make install diff --git a/ice40/arch.cc b/ice40/arch.cc index b36c82d522..6746b3028f 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -923,9 +923,20 @@ std::vector Arch::getDecalGraphics(DecalId decal) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const { - if (cell->type == id_ICESTORM_LC && cell->lcInfo.dffEnable) { - if (toPort == id_O) - return false; + if (cell->type == id_ICESTORM_LC) { + if (toPort == id_O) { + if (cell->lcInfo.dffEnable) + return false; + // "false paths" + if (fromPort == id_I0 && ((cell->lcInfo.lutInputMask & 0x1U) == 0)) + return false; + if (fromPort == id_I1 && ((cell->lcInfo.lutInputMask & 0x2U) == 0)) + return false; + if (fromPort == id_I2 && ((cell->lcInfo.lutInputMask & 0x4U) == 0)) + return false; + if (fromPort == id_I3 && ((cell->lcInfo.lutInputMask & 0x8U) == 0)) + return false; + } } else if (cell->type == id_ICESTORM_RAM || cell->type == id_ICESTORM_SPRAM) { return false; } @@ -1231,6 +1242,18 @@ void Arch::assignCellInfo(CellInfo *cell) cell->lcInfo.inputCount++; if (cell->getPort(id_I3)) cell->lcInfo.inputCount++; + // Find don't care LUT inputs to mask for timing analysis + cell->lcInfo.lutInputMask = 0x0; + unsigned init = int_or_default(cell->params, id_LUT_INIT); + for (unsigned k = 0; k < 4; k++) { + for (unsigned i = 0; i < 16; i++) { + // If toggling the LUT input makes a difference it's not a don't care + if (((init >> i) & 0x1U) != ((init >> (i ^ (1U << k))) & 0x1U)) { + cell->lcInfo.lutInputMask |= (1U << k); + break; + } + } + } } else if (cell->type == id_SB_IO) { cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT"; cell->ioInfo.global = bool_or_default(cell->attrs, id_GLOBAL); diff --git a/ice40/archdefs.h b/ice40/archdefs.h index 3d8ea28213..07b209f137 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -137,6 +137,7 @@ struct ArchCellInfo : BaseClusterInfo bool carryEnable; bool negClk; int inputCount; + unsigned lutInputMask; const NetInfo *clk, *cen, *sr; } lcInfo; struct diff --git a/ice40/pack.cc b/ice40/pack.cc index 263903b090..92297e8edd 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -266,6 +266,68 @@ static void pack_carries(Context *ctx) log_info(" %4d LCs used as CARRY only\n", carry_only); } +static void merge_carry_luts(Context *ctx) +{ + // Find carrys + log_info("Packing indirect carry+LUT pairs...\n"); + // Find cases where a less-than-LUT2 is driving a carry and pack them together + // +----+ +-----+ | + // A--|LUT2|----|CARRY| | + // B--| | C-| |-+ + // +----+ +-| | + // | +-----+ + // | + pool packed_cells; + auto rewrite_init = [](unsigned lut_init) { + // I0 -> LUT I2 + // I1, I2 -> carry; don't care + // I3 -> LUT I3 + unsigned result = 0; + for (unsigned i = 0; i < 16; i++) { + unsigned j = 0; + if ((i & 1)) + j |= 4; + if ((i & 8)) + j |= 8; + if (lut_init & (1 << j)) + result |= (1 << i); + } + return result; + }; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_ICESTORM_LC || !bool_or_default(ci->params, id_CARRY_ENABLE)) + continue; // not a carry LC + if (ci->getPort(id_O)) + continue; // LUT output is already used + for (auto port : {id_I1, id_I2}) { // check carry inputs + NetInfo *i = ci->getPort(port); + if (!i) + continue; + CellInfo *drv = i->driver.cell; + if (i->driver.port != id_O) + continue; + if (!drv || drv->type != id_ICESTORM_LC || packed_cells.count(drv->name) || + bool_or_default(drv->params, id_CARRY_ENABLE) || bool_or_default(drv->params, id_DFF_ENABLE)) + continue; // not driven by a LUT, or driver already swallowed + // Check cardinality - must be LUT2 or less, noting top inputs used first + if (drv->getPort(id_I0) || drv->getPort(id_I1)) + continue; + // Pack into carry + drv->movePortTo(id_I2, ci, id_I0); + drv->movePortTo(id_I3, ci, id_I3); + drv->movePortTo(id_O, ci, id_O); + ci->params[id_LUT_INIT] = Property(rewrite_init(int_or_default(drv->params, id_LUT_INIT)), 16); + packed_cells.insert(drv->name); + break; + } + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + log_info(" %4d LUTs merged into carry LCs\n", int(packed_cells.size())); +} + // "Pack" RAMs static void pack_ram(Context *ctx) { @@ -1642,6 +1704,7 @@ bool Arch::pack() pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); pack_carries(ctx); + merge_carry_luts(ctx); pack_ram(ctx); place_plls(ctx); pack_special(ctx); From b9e76d1bcd556da7b25d58ff9aa6134b4d81ab30 Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Wed, 30 Mar 2022 14:59:47 +0200 Subject: [PATCH 122/712] Rename parse_lattice_param to parse_lattice_param_from_cell Add new definition for parse_lattice_param Now parse_lattice_param is design to parse Property rather than search for it in cell. This functionalty was move to parse_lattice_param_from_cell. Signed-off-by: Maciej Dudek --- nexus/arch.h | 3 ++- nexus/fasm.cc | 39 ++++++++++++++++++++++----------------- nexus/pack.cc | 40 ++++++++++++++++++++++------------------ 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index ea4e9f5bbe..323dcacdc7 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1408,7 +1408,8 @@ struct Arch : BaseArch // ------------------------------------------------- // Parse a possibly-Lattice-style (C literal in Verilog string) style parameter - Property parse_lattice_param(const CellInfo *ci, IdString prop, int width, int64_t defval) const; + Property parse_lattice_param_from_cell(const CellInfo *ci, IdString prop, int width, int64_t defval) const; + Property parse_lattice_param(const Property &val, IdString prop, int width, const char* ci="") const; // ------------------------------------------------- diff --git a/nexus/fasm.cc b/nexus/fasm.cc index b2cb03fa5e..a6e94d73a7 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -523,7 +523,7 @@ struct NexusFasmWriter write_enum(cell, "LF_OUTPUT_EN"); write_enum(cell, "DTR_EN", "ENABLED"); write_enum(cell, "DEBUG_N", "DISABLED"); - write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param(cell, id_HF_CLK_DIV, 8, 0).intval, 8); + write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param_from_cell(cell, id_HF_CLK_DIV, 8, 0).intval, 8); write_int_vector(stringf("HF_SED_SEC_DIV[7:0]"), 1, 8); write_cell_muxes(cell); pop(2); @@ -830,28 +830,33 @@ struct NexusFasmWriter write_cell_muxes(cell); pop(); push(stringf("IP_%s", ctx->nameOf(IdString(ctx->bel_data(bel).name)))); - CellInfo temp(*cell); - for (auto param : cell->params) temp.params.insert(param); for (auto &value : pll_default_params) { IdString n = IdString(ctx, value.first); - if (!temp.params.count(n)) { - if (is_number(value.second)) - temp.params[ctx->id(value.first)] = Property(std::stoi(value.second), 32); - else - temp.params[ctx->id(value.first)] = Property::from_string(value.second); - } - } - for (auto ¶m : temp.params) { - const std::string &name = param.first.str(ctx); + Property temp; + if (is_number(value.second)) + temp = Property(std::stoi(value.second), 32); + else + temp = Property::from_string(value.second); + const std::string &name = n.str(ctx); if (is_mux_param(name) || name == "CLKMUX_FB" || name == "SEL_FBK") continue; auto fnd_word = pll_word_params.find(name); if (fnd_word != pll_word_params.end()) { - write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), - ctx->parse_lattice_param(&temp, param.first, fnd_word->second, 0).as_int64(), - fnd_word->second); + if (cell->params.count(n)) { + write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), + ctx->parse_lattice_param_from_cell(cell, n, fnd_word->second, 0).as_int64(), + fnd_word->second); + } else { + write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), + ctx->parse_lattice_param(temp, n, fnd_word->second).as_int64(), + fnd_word->second); + } } else { - write_bit(stringf("%s.%s", name.c_str(), param.second.as_string().c_str())); + if (cell->params.count(n)) { + write_bit(stringf("%s.%s", name.c_str(), cell->params.at(n).as_string().c_str())); + } else { + write_bit(stringf("%s.%s", name.c_str(), temp.as_string().c_str())); + } } } pop(); @@ -869,7 +874,7 @@ struct NexusFasmWriter auto fnd_word = dphy_word_params.find(name); if (fnd_word != dphy_word_params.end()) { write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), - ctx->parse_lattice_param(cell, param.first, fnd_word->second, 0).as_int64(), + ctx->parse_lattice_param_from_cell(cell, param.first, fnd_word->second, 0).as_int64(), fnd_word->second); } else { write_bit(stringf("%s.%s", name.c_str(), param.second.as_string().c_str())); diff --git a/nexus/pack.cc b/nexus/pack.cc index 26da3dc5b0..4eccc6343a 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -32,13 +32,17 @@ namespace { bool is_enabled(CellInfo *ci, IdString prop) { return str_or_default(ci->params, prop, "") == "ENABLED"; } } // namespace -// Parse a possibly-Lattice-style (C literal in Verilog string) style parameter -Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, int64_t defval) const +Property Arch::parse_lattice_param_from_cell(const CellInfo *ci, IdString prop, int width, int64_t defval) const { auto fnd = ci->params.find(prop); if (fnd == ci->params.end()) return Property(defval, width); - const auto &val = fnd->second; + return this->parse_lattice_param(fnd->second, prop, width, nameOf(ci)); +} + +// Parse a possibly-Lattice-style (C literal in Verilog string) style parameter +Property Arch::parse_lattice_param(const Property &val, IdString prop, int width, const char* ci) const +{ if (val.is_string) { const std::string &s = val.str; Property temp; @@ -47,7 +51,7 @@ Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, for (int i = int(s.length()) - 1; i >= 2; i--) { char c = s.at(i); if (c != '0' && c != '1' && c != 'x') - log_error("Invalid binary digit '%c' in property %s.%s\n", c, nameOf(ci), nameOf(prop)); + log_error("Invalid binary digit '%c' in property %s.%s\n", c, ci, nameOf(prop)); temp.str.push_back(c); } } else if (boost::starts_with(s, "0x")) { @@ -61,7 +65,7 @@ Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, else if (c >= 'A' && c <= 'F') nibble = (c - 'A') + 10; else - log_error("Invalid hex digit '%c' in property %s.%s\n", c, nameOf(ci), nameOf(prop)); + log_error("Invalid hex digit '%c' in property %s.%s\n", c, ci, nameOf(prop)); for (int j = 0; j < 4; j++) temp.str.push_back(((nibble >> j) & 0x1) ? Property::S1 : Property::S0); } @@ -73,14 +77,14 @@ Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, else ival = std::stoll(s); } catch (std::runtime_error &e) { - log_error("Invalid decimal value for property %s.%s", nameOf(ci), nameOf(prop)); + log_error("Invalid decimal value for property %s.%s", ci, nameOf(prop)); } temp = Property(ival); } for (auto b : temp.str.substr(width)) { if (b == Property::S1) - log_error("Found value for property %s.%s with width greater than %d\n", nameOf(ci), nameOf(prop), + log_error("Found value for property %s.%s with width greater than %d\n", ci, nameOf(prop), width); } temp.update_intval(); @@ -90,7 +94,7 @@ Property Arch::parse_lattice_param(const CellInfo *ci, IdString prop, int width, if (b == Property::S1) log_error("Found bitvector value for property %s.%s with width greater than %d - perhaps a string was " "converted to bits?\n", - nameOf(ci), nameOf(prop), width); + ci, nameOf(prop), width); } return val.extract(0, width); } @@ -170,7 +174,7 @@ struct NexusPacker int64_t def; for (const auto &p : rule.parse_params) { std::tie(old_param, new_param, width, def) = p; - ci->params[new_param] = ctx->parse_lattice_param(ci, old_param, width, def); + ci->params[new_param] = ctx->parse_lattice_param_from_cell(ci, old_param, width, def); } } @@ -953,7 +957,7 @@ struct NexusPacker ramw->connectPort(id_WCKO, int_wck); ramw->connectPort(id_WREO, int_wre); - uint64_t initval = ctx->parse_lattice_param(ci, id_INITVAL, 64, 0).as_int64(); + uint64_t initval = ctx->parse_lattice_param_from_cell(ci, id_INITVAL, 64, 0).as_int64(); // Rewiring - buses for (int i = 0; i < 4; i++) { @@ -1272,8 +1276,8 @@ struct NexusPacker combs[1]->connectPort(id_F, f1); combs[0]->connectPort(id_F1, f1); - combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0); - combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0); + combs[0]->params[id_INIT] = ctx->parse_lattice_param_from_cell(ci, id_INIT0, 16, 0); + combs[1]->params[id_INIT] = ctx->parse_lattice_param_from_cell(ci, id_INIT1, 16, 0); combs[1]->cluster = combs[0]->name; combs[1]->constr_x = 0; @@ -1329,8 +1333,8 @@ struct NexusPacker // Copy parameters if (ci->params.count(id_INJECT)) combs[0]->params[id_INJECT] = ci->params[id_INJECT]; - combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0); - combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0); + combs[0]->params[id_INIT] = ctx->parse_lattice_param_from_cell(ci, id_INIT0, 16, 0); + combs[1]->params[id_INIT] = ctx->parse_lattice_param_from_cell(ci, id_INIT1, 16, 0); // Internal carry net between the two split COMB cells NetInfo *int_cy = ctx->createNet(ctx->id(stringf("%s$widefn_int_cy$", ctx->nameOf(ci)))); @@ -2026,9 +2030,9 @@ struct NexusPacker log_info(" Input frequency of PLL '%s' is constrained to %.1f MHz\n", ci->name.c_str(ctx), MHz(period_in)); - int input_div = ctx->parse_lattice_param(ci, id_REF_MMD_DIG, 8, 1).as_int64(); + int input_div = ctx->parse_lattice_param_from_cell(ci, id_REF_MMD_DIG, 8, 1).as_int64(); period_in *= input_div; - int feedback_div = ctx->parse_lattice_param(ci, id_REF_MMD_DIG, 8, 1).as_int64(); + int feedback_div = ctx->parse_lattice_param_from_cell(ci, id_REF_MMD_DIG, 8, 1).as_int64(); bool found_fbk = false; std::string clkmux_fb = str_or_default(ci->params, id_CLKMUX_FB, "CMUX_CLKOP"); for (int i = 0; i < 6; i++) { @@ -2036,7 +2040,7 @@ struct NexusPacker if (clkmux_fb != stringf("CMUX_%s", output[i].c_str(ctx))) continue; // Multiply feedback output divider with - feedback_div *= (ctx->parse_lattice_param(ci, div[i], 7, 0).as_int64() + 1); + feedback_div *= (ctx->parse_lattice_param_from_cell(ci, div[i], 7, 0).as_int64() + 1); found_fbk = true; } if (!found_fbk) { @@ -2050,7 +2054,7 @@ struct NexusPacker MHz(vco_period)); for (int i = 0; i < 6; i++) { set_period(ci, output[i], - (ctx->parse_lattice_param(ci, div[i], 7, 0).as_int64() + 1) * vco_period); + (ctx->parse_lattice_param_from_cell(ci, div[i], 7, 0).as_int64() + 1) * vco_period); } } } From 601b32948b8c8e79bf1ac394ce2f42f5d3d9fce9 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Wed, 30 Mar 2022 17:35:16 +0200 Subject: [PATCH 123/712] gowin: Fix z-index of oscillator --- gowin/arch.cc | 8 ++++---- gowin/arch.h | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 2c1e50b740..89bdade349 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1012,7 +1012,7 @@ Arch::Arch(ArchArgs args) : args(args) case ID_OSC: snprintf(buf, 32, "R%dC%d_OSC", row + 1, col + 1); belname = id(buf); - addBel(belname, id_OSC, Loc(col, row, 0), false); + addBel(belname, id_OSC, Loc(col, row, BelZ::osc_z), false); portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelOutput(belname, id_OSCOUT, id(buf)); @@ -1020,7 +1020,7 @@ Arch::Arch(ArchArgs args) : args(args) case ID_OSCH: snprintf(buf, 32, "R%dC%d_OSCH", row + 1, col + 1); belname = id(buf); - addBel(belname, id_OSCH, Loc(col, row, 0), false); + addBel(belname, id_OSCH, Loc(col, row, BelZ::osc_z), false); portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelOutput(belname, id_OSCOUT, id(buf)); @@ -1028,7 +1028,7 @@ Arch::Arch(ArchArgs args) : args(args) case ID_OSCF: snprintf(buf, 32, "R%dC%d_OSCF", row + 1, col + 1); belname = id(buf); - addBel(belname, id_OSCF, Loc(col, row, 0), false); + addBel(belname, id_OSCF, Loc(col, row, BelZ::osc_z), false); portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelOutput(belname, id_OSCOUT, id(buf)); @@ -1039,7 +1039,7 @@ Arch::Arch(ArchArgs args) : args(args) case ID_OSCZ: snprintf(buf, 32, "R%dC%d_OSCZ", row + 1, col + 1); belname = id(buf); - addBel(belname, id_OSCZ, Loc(col, row, 0), false); + addBel(belname, id_OSCZ, Loc(col, row, BelZ::osc_z), false); portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OSCOUT)->src_id); snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelOutput(belname, id_OSCOUT, id(buf)); diff --git a/gowin/arch.h b/gowin/arch.h index bc29a59bf9..c5804bc44b 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -484,7 +484,8 @@ enum mux_0_z = 10, // start Z for the MUX2LUT5 bels iologic_0_z = 20, // start Z for the IOLOGIC bels vcc_0_z = 277, // virtual VCC bel Z - gnd_0_z = 278 // virtual VSS bel Z + gnd_0_z = 278, // virtual VSS bel Z + osc_z = 280 // Z for the oscillator bels }; } From 336124b879495ca4b5ac3aad23c069eafb5231dd Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 30 Mar 2022 20:57:00 +0100 Subject: [PATCH 124/712] ice40: Fix wirenames containing / which is the list separator Signed-off-by: gatecat --- ice40/chipdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ice40/chipdb.py b/ice40/chipdb.py index cc3397eb67..accafbcf47 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -1312,7 +1312,7 @@ def pop(self): bba.l("wire_data_%s" % dev_name, "WireInfoPOD") for wire, info in enumerate(wireinfo): - bba.s(info["name"], "name") + bba.s(info["name"].replace('/', ':'), "name") # / is used as an IdStringList separator; can't also be within name bba.u8(info["name_x"], "name_x") bba.u8(info["name_y"], "name_y") bba.u16(0, "padding") From 2ed68a21db9d04e5688c4c0784b0817f3485926e Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 31 Mar 2022 10:49:00 +0100 Subject: [PATCH 125/712] clangformat Signed-off-by: gatecat --- nexus/arch.h | 2 +- nexus/fasm.cc | 17 +++++++++-------- nexus/pack.cc | 5 ++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/nexus/arch.h b/nexus/arch.h index 323dcacdc7..d713afcfe4 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1409,7 +1409,7 @@ struct Arch : BaseArch // Parse a possibly-Lattice-style (C literal in Verilog string) style parameter Property parse_lattice_param_from_cell(const CellInfo *ci, IdString prop, int width, int64_t defval) const; - Property parse_lattice_param(const Property &val, IdString prop, int width, const char* ci="") const; + Property parse_lattice_param(const Property &val, IdString prop, int width, const char *ci = "") const; // ------------------------------------------------- diff --git a/nexus/fasm.cc b/nexus/fasm.cc index a6e94d73a7..3ed5785eab 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -523,7 +523,8 @@ struct NexusFasmWriter write_enum(cell, "LF_OUTPUT_EN"); write_enum(cell, "DTR_EN", "ENABLED"); write_enum(cell, "DEBUG_N", "DISABLED"); - write_int_vector(stringf("HF_CLK_DIV[7:0]"), ctx->parse_lattice_param_from_cell(cell, id_HF_CLK_DIV, 8, 0).intval, 8); + write_int_vector(stringf("HF_CLK_DIV[7:0]"), + ctx->parse_lattice_param_from_cell(cell, id_HF_CLK_DIV, 8, 0).intval, 8); write_int_vector(stringf("HF_SED_SEC_DIV[7:0]"), 1, 8); write_cell_muxes(cell); pop(2); @@ -812,9 +813,10 @@ struct NexusFasmWriter }; /* clang-format on */ - static bool is_number(std::string s) { - for (auto c : s) { - if(!isdigit(c)) + static bool is_number(std::string s) + { + for (auto c : s) { + if (!isdigit(c)) return false; } return true; @@ -844,12 +846,11 @@ struct NexusFasmWriter if (fnd_word != pll_word_params.end()) { if (cell->params.count(n)) { write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), - ctx->parse_lattice_param_from_cell(cell, n, fnd_word->second, 0).as_int64(), - fnd_word->second); + ctx->parse_lattice_param_from_cell(cell, n, fnd_word->second, 0).as_int64(), + fnd_word->second); } else { write_int_vector(stringf("%s[%d:0]", name.c_str(), fnd_word->second - 1), - ctx->parse_lattice_param(temp, n, fnd_word->second).as_int64(), - fnd_word->second); + ctx->parse_lattice_param(temp, n, fnd_word->second).as_int64(), fnd_word->second); } } else { if (cell->params.count(n)) { diff --git a/nexus/pack.cc b/nexus/pack.cc index 4eccc6343a..0870bf4066 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -41,7 +41,7 @@ Property Arch::parse_lattice_param_from_cell(const CellInfo *ci, IdString prop, } // Parse a possibly-Lattice-style (C literal in Verilog string) style parameter -Property Arch::parse_lattice_param(const Property &val, IdString prop, int width, const char* ci) const +Property Arch::parse_lattice_param(const Property &val, IdString prop, int width, const char *ci) const { if (val.is_string) { const std::string &s = val.str; @@ -84,8 +84,7 @@ Property Arch::parse_lattice_param(const Property &val, IdString prop, int width for (auto b : temp.str.substr(width)) { if (b == Property::S1) - log_error("Found value for property %s.%s with width greater than %d\n", ci, nameOf(prop), - width); + log_error("Found value for property %s.%s with width greater than %d\n", ci, nameOf(prop), width); } temp.update_intval(); return temp.extract(0, width); From 85e8570a73dce11d3cfd2ce82a3b6c47214758af Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sun, 3 Apr 2022 10:05:27 +1000 Subject: [PATCH 126/712] gowin: handle the GW1N-9 feature. This chip has a different default state for one type of I/O buffer --- you have to explicitly switch it to the normal state by feeding VCC/VSS to certain inputs. Signed-off-by: YRabbit --- gowin/arch.cc | 19 +++++++++++++++++-- gowin/arch.h | 2 ++ gowin/constids.inc | 2 ++ gowin/pack.cc | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 89bdade349..7bfef36e8e 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1119,7 +1119,7 @@ Arch::Arch(ArchArgs args) : args(args) z++; /* fall-through*/ case ID_IOBB: z++; /* fall-through*/ - case ID_IOBA: + case ID_IOBA: { snprintf(buf, 32, "R%dC%d_IOB%c", row + 1, col + 1, 'A' + z); belname = id(buf); addBel(belname, id_IOB, Loc(col, row, z), false); @@ -1132,7 +1132,22 @@ Arch::Arch(ArchArgs args) : args(args) portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_OE)->src_id); snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelInput(belname, id_OEN, id(buf)); - break; + // GW1NR-9 quirk + const PairPOD *xxx_port = pairLookup(bel->ports.get(), bel->num_ports, ID_XXX_VSS0); + if (xxx_port != nullptr) { + gw1n9_quirk = true; + portname = IdString(xxx_port->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_XXX_VSS0, id(buf)); + } + xxx_port = pairLookup(bel->ports.get(), bel->num_ports, ID_XXX_VSS1); + if (xxx_port != nullptr) { + gw1n9_quirk = true; + portname = IdString(xxx_port->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_XXX_VSS1, id(buf)); + } + } break; // Simplified IO case ID_IOBJS: z++; /* fall-through*/ diff --git a/gowin/arch.h b/gowin/arch.h index c5804bc44b..14181d793b 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -472,6 +472,8 @@ struct Arch : BaseArch // XXX GW1N-9C DDR quirk bool ddr_has_extra_inputs = false; + // XXX GW1NR-9 iobuf quirk + bool gw1n9_quirk = false; // Permissible combinations of modes in a single slice std::map dff_comp_mode; diff --git a/gowin/constids.inc b/gowin/constids.inc index d2a6b171de..3691c50606 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -683,6 +683,8 @@ X(IOBJS) X(TX) X(XXX_VSS) X(XXX_VCC) +X(XXX_VSS0) +X(XXX_VSS1) X(OBUF_TYPE) X(SBUF) X(DBUF) diff --git a/gowin/pack.cc b/gowin/pack.cc index 28370a7557..4adfec1aa2 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -805,6 +805,14 @@ static void pack_iologic(Context *ctx) ci->addInput(id_XXX_VCC); ci->connectPort(id_XXX_VCC, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); } + if (ctx->gw1n9_quirk && iob_bel != q0_dst->attrs.end()) { + bool have_XXX_VSS0 = + ctx->bels[ctx->getBelByNameStr(iob_bel->second.as_string())].pins.count(id_XXX_VSS0); + if (have_XXX_VSS0) { + q0_dst->disconnectPort(id_XXX_VSS0); + q0_dst->connectPort(id_XXX_VSS0, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); + } + } } break; default: break; @@ -933,6 +941,7 @@ static void pack_io(Context *ctx) // what type to create IdString new_cell_type = id_IOB; std::string constr_bel_name = std::string(""); + bool have_xxx_port = false; // check whether the given IO is limited to simplified IO cells auto constr_bel = ci->attrs.find(id_BEL); if (constr_bel != ci->attrs.end()) { @@ -946,6 +955,9 @@ static void pack_io(Context *ctx) BelId constr_bel = ctx->getBelByNameStr(constr_bel_name); if (constr_bel != BelId()) { new_cell_type = ctx->bels[constr_bel].type; + if (ctx->gw1n9_quirk) { + have_xxx_port = ctx->bels[constr_bel].pins.count(id_XXX_VSS0) != 0; + } } } @@ -954,6 +966,13 @@ static void pack_io(Context *ctx) gwio_to_iob(ctx, ci, ice_cell.get(), packed_cells); new_cells.push_back(std::move(ice_cell)); auto gwiob = new_cells.back().get(); + // XXX GW1NR-9 quirks + if (have_xxx_port && ci->type != id_IBUF) { + gwiob->addInput(id_XXX_VSS0); + gwiob->connectPort(id_XXX_VSS0, ctx->nets[ctx->id("$PACKER_GND_NET")].get()); + gwiob->addInput(id_XXX_VSS1); + gwiob->connectPort(id_XXX_VSS1, ctx->nets[ctx->id("$PACKER_GND_NET")].get()); + } packed_cells.insert(ci->name); if (iob != nullptr) { From c4e47ba1a85d840c31d4be5c3f2c032664abd814 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 4 Apr 2022 19:49:44 +0100 Subject: [PATCH 127/712] generic: Allow bel pins without wires Signed-off-by: gatecat --- generic/arch.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/generic/arch.cc b/generic/arch.cc index c4814bab0b..0aece64fe0 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -119,7 +119,8 @@ void Arch::addBelInput(BelId bel, IdString name, WireId wire) pi.wire = wire; pi.type = PORT_IN; - wire_info(wire).bel_pins.push_back(BelPin{bel, name}); + if (wire != WireId()) + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } void Arch::addBelOutput(BelId bel, IdString name, WireId wire) @@ -131,7 +132,8 @@ void Arch::addBelOutput(BelId bel, IdString name, WireId wire) pi.wire = wire; pi.type = PORT_OUT; - wire_info(wire).bel_pins.push_back(BelPin{bel, name}); + if (wire != WireId()) + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } void Arch::addBelInout(BelId bel, IdString name, WireId wire) @@ -143,7 +145,8 @@ void Arch::addBelInout(BelId bel, IdString name, WireId wire) pi.wire = wire; pi.type = PORT_INOUT; - wire_info(wire).bel_pins.push_back(BelPin{bel, name}); + if (wire != WireId()) + wire_info(wire).bel_pins.push_back(BelPin{bel, name}); } void Arch::addGroupBel(IdStringList group, BelId bel) { groups[group].bels.push_back(bel); } From 03074cdbc242a06c861a703ad76365b4dca7cb4a Mon Sep 17 00:00:00 2001 From: Irides Date: Tue, 5 Apr 2022 10:12:44 -0500 Subject: [PATCH 128/712] cmake: properly include TBB libraries. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78c1caa1d1..fcbbbb13fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,7 @@ if(PROFILER) list(APPEND EXTRA_LIB_DEPS profiler) endif() if(TBB_FOUND) - list(APPEND EXTRA_LIB_DEPS tbb) + list(APPEND EXTRA_LIB_DEPS TBB::tbb) endif() foreach (family ${ARCH}) From efb58711b0dfcdb8080f63bd64d3f9d9fafd2637 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 31 Mar 2022 11:17:57 +0100 Subject: [PATCH 129/712] ecp5: Split the SLICE bel into separate LUT/FF/RAMW bels --- .cirrus/Dockerfile.ubuntu20.04 | 2 +- ecp5/arch.cc | 147 ++-- ecp5/arch.h | 107 ++- ecp5/arch_place.cc | 237 +++--- ecp5/archdefs.h | 41 +- ecp5/bitstream.cc | 115 ++- ecp5/cells.cc | 318 +++----- ecp5/cells.h | 11 +- ecp5/constids.inc | 10 + ecp5/gfx.cc | 26 +- ecp5/globals.cc | 8 +- ecp5/pack.cc | 1401 ++++++++++++-------------------- ecp5/trellis_import.py | 63 +- 13 files changed, 1137 insertions(+), 1349 deletions(-) diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 index 6561ed59a0..35ea18bd9a 100644 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ b/.cirrus/Dockerfile.ubuntu20.04 @@ -48,7 +48,7 @@ RUN set -e -x ;\ cd /usr/local/src ;\ git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\ cd prjtrellis ;\ - git reset --hard 7239331d5463321d4864164f320beef67310f1e5 ;\ + git reset --hard 26f917d6052e084df30211ae3a78c8a165121e09 ;\ cd libtrellis ;\ cmake . ;\ make -j $(nproc) ;\ diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 3d20badb22..50993e2b97 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -103,7 +103,19 @@ Arch::Arch(ArchArgs args) : args(args) if (!package_info) log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str()); - bel_to_cell.resize(chip_info->height * chip_info->width * max_loc_bels, nullptr); + tile_status.resize(chip_info->num_tiles); + for (int i = 0; i < chip_info->num_tiles; i++) { + auto &ts = tile_status.at(i); + auto &tile_data = chip_info->tile_info[i]; + ts.boundcells.resize(chip_info->locations[chip_info->location_type[i]].bel_data.size(), nullptr); + for (auto &name : tile_data.tile_names) { + if (strcmp(chip_info->tiletype_names[name.type_idx].get(), "PLC2") == 0) { + // Is a logic tile + ts.lts = new LogicTileStatus(); + break; + } + } + } BaseArch::init_cell_types(); BaseArch::init_bel_buckets(); @@ -545,20 +557,24 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin == id_FXA || dst_pin == id_FXB) + if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin == id_FXA || dst_pin == id_FXB || + (src_pin == id_F && dst_pin == id_DI)) return 0; auto driver_loc = getBelLocation(src_bel); auto sink_loc = getBelLocation(dst_bel); // Encourage use of direct interconnect + // exact LUT input doesn't matter as they can be permuted by the router... if (driver_loc.x == sink_loc.x && driver_loc.y == sink_loc.y) { - if ((dst_pin == id_A0 || dst_pin == id_A1) && (src_pin == id_F1) && (driver_loc.z == 2 || driver_loc.z == 3)) - return 0; - if ((dst_pin == id_B0 || dst_pin == id_B1) && (src_pin == id_F1) && (driver_loc.z == 0 || driver_loc.z == 1)) - return 0; - if ((dst_pin == id_C0 || dst_pin == id_C1) && (src_pin == id_F0) && (driver_loc.z == 2 || driver_loc.z == 3)) - return 0; - if ((dst_pin == id_D0 || dst_pin == id_D1) && (src_pin == id_F0) && (driver_loc.z == 0 || driver_loc.z == 1)) - return 0; + if (dst_pin.in(id_A, id_B, id_C, id_D) && src_pin == id_Q) { + int lut = (sink_loc.z >> lc_idx_shift), ff = (driver_loc.z >> lc_idx_shift); + if (lut == ff) + return 0; + } + if (dst_pin.in(id_A, id_B, id_C, id_D) && src_pin == id_F) { + int l0 = (driver_loc.z >> lc_idx_shift); + if (l0 != 1 && l0 != 6) + return 0; + } } int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y); @@ -597,6 +613,14 @@ bool Arch::place() cfg.cellGroups.back().insert(id_MULT18X18D); cfg.cellGroups.back().insert(id_ALU54B); + cfg.cellGroups.emplace_back(); + cfg.cellGroups.back().insert(id_TRELLIS_COMB); + cfg.cellGroups.back().insert(id_TRELLIS_FF); + cfg.cellGroups.back().insert(id_TRELLIS_RAMW); + cfg.placeAllAtOnce = true; + + cfg.beta = 0.75; + if (!placer_heap(getCtx(), cfg)) return false; } else if (placer == "sa") { @@ -605,7 +629,6 @@ bool Arch::place() } else { log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str()); } - permute_luts(); // In out-of-context mode, create a locked macro if (bool_or_default(settings, id("arch.ooc"))) @@ -710,7 +733,7 @@ DecalXY Arch::getBelDecal(BelId bel) const decalxy.decal.type = DecalId::TYPE_BEL; decalxy.decal.location = bel.location; decalxy.decal.z = bel.index; - decalxy.decal.active = (bel_to_cell.at(get_bel_flat_index(bel)) != nullptr); + decalxy.decal.active = getBoundBelCell(bel) != nullptr; return decalxy; } @@ -791,14 +814,19 @@ void Arch::get_setuphold_from_tmg_db(IdString tctype, IdString clock, IdString p bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const { // Data for -8 grade - if (cell->type == id_TRELLIS_SLICE) { - bool has_carry = cell->sliceInfo.is_carry; - if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 || - fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_M1 || - fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) { - return get_delay_from_tmg_db(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay); - } - + if (cell->type == id_TRELLIS_COMB) { + bool has_carry = cell->combInfo.flags & ArchCellInfo::COMB_CARRY; + IdString tmg_type = has_carry ? (((cell->constr_z >> Arch::lc_idx_shift) % 2) ? id_TRELLIS_COMB_CARRY1 + : id_TRELLIS_COMB_CARRY0) + : id_TRELLIS_COMB; + if (fromPort == id_A || fromPort == id_B || fromPort == id_C || fromPort == id_D || fromPort == id_M || + fromPort == id_F1 || fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) + return get_delay_from_tmg_db(tmg_type, fromPort, toPort, delay); + else + return false; + } else if (cell->type == id_TRELLIS_FF) { + return false; + } else if (cell->type == id_TRELLIS_RAMW) { if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) || (fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) || (fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) || @@ -841,45 +869,46 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in { auto disconnected = [cell](IdString p) { return !cell->ports.count(p) || cell->ports.at(p).net == nullptr; }; clockInfoCount = 0; - if (cell->type == id_TRELLIS_SLICE) { - int sd0 = cell->sliceInfo.sd0, sd1 = cell->sliceInfo.sd1; - if (port == id_CLK || port == id_WCK) + if (cell->type == id_TRELLIS_COMB) { + if (port == id_WCK) return TMG_CLOCK_INPUT; - if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 || - port == id_D0 || port == id_D1 || port == id_FCI || port == id_FXA || port == id_FXB) + if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI || port == id_FXA || + port == id_FXB || port == id_F1) return TMG_COMB_INPUT; - if (port == id_F0 && disconnected(id_A0) && disconnected(id_B0) && disconnected(id_C0) && disconnected(id_D0) && + if (port == id_F && disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && disconnected(id_FCI)) return TMG_IGNORE; // LUT with no inputs is a constant - if (port == id_F1 && disconnected(id_A1) && disconnected(id_B1) && disconnected(id_C1) && disconnected(id_D1) && - disconnected(id_FCI)) - return TMG_IGNORE; // LUT with no inputs is a constant - - if (port == id_F0 || port == id_F1 || port == id_FCO || port == id_OFX0 || port == id_OFX1) + if (port == id_F || port == id_FCO || port == id_OFX) return TMG_COMB_OUTPUT; - if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 0 && port == id_M0) || - (sd1 == 0 && port == id_M1)) { + if (port == id_M) + return TMG_COMB_INPUT; + if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 || + port == id_WRE) { clockInfoCount = 1; return TMG_REGISTER_INPUT; } - if (port == id_M0 || port == id_M1) - return TMG_COMB_INPUT; - if (port == id_Q0 || port == id_Q1) { + return TMG_IGNORE; + } else if (cell->type == id_TRELLIS_FF) { + bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED); + if (port == id_CLK) + return TMG_CLOCK_INPUT; + if (port == id_DI || (using_m && (port == id_M)) || port == id_CE || port == id_LSR) { + clockInfoCount = 1; + return TMG_REGISTER_INPUT; + } + if (port == id_Q) { clockInfoCount = 1; return TMG_REGISTER_OUTPUT; } - + return TMG_IGNORE; + } else if (cell->type == id_TRELLIS_RAMW) { + if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 || + port == id_D0 || port == id_D1) + return TMG_COMB_INPUT; if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3 || port == id_WADO0 || port == id_WADO1 || port == id_WADO2 || port == id_WADO3) return TMG_COMB_OUTPUT; - - if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || - port == id_WAD3 || port == id_WRE) { - clockInfoCount = 1; - return TMG_REGISTER_INPUT; - } - - NPNR_ASSERT_FALSE_STR("no timing type for slice port '" + port.str(this) + "'"); + return TMG_IGNORE; } else if (cell->type == id_TRELLIS_IO) { if (port == id_T || port == id_I) return TMG_ENDPOINT; @@ -1024,21 +1053,29 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.setup = DelayPair(0); info.hold = DelayPair(0); info.clockToQ = DelayQuad(0); - if (cell->type == id_TRELLIS_SLICE) { - int sd0 = cell->sliceInfo.sd0, sd1 = cell->sliceInfo.sd1; - if (port == id_WD0 || port == id_WD1 || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || - port == id_WAD3 || port == id_WRE) { - info.edge = RISING_EDGE; + if (cell->type == id_TRELLIS_COMB) { + if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 || + port == id_WRE) { + if (port == id_WD) + port = id_WD0; + info.edge = (cell->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV) ? FALLING_EDGE : RISING_EDGE; info.clock_port = id_WCK; get_setuphold_from_tmg_db(id_SDPRAME, id_WCK, port, info.setup, info.hold); - } else if (port == id_DI0 || port == id_DI1 || port == id_CE || port == id_LSR || (sd0 == 0 && port == id_M0) || - (sd1 == 0 && port == id_M1)) { - info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE; + } + } else if (cell->type == id_TRELLIS_FF) { + bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED); + if (port == id_DI || port == id_CE || port == id_LSR || (using_m && port == id_M)) { + if (port == id_DI) + port = id_DI0; + if (port == id_M) + port = id_M0; + info.edge = (cell->ffInfo.flags & ArchCellInfo::FF_CLKINV) ? FALLING_EDGE : RISING_EDGE; info.clock_port = id_CLK; get_setuphold_from_tmg_db(id_SLOGICB, id_CLK, port, info.setup, info.hold); - } else { - info.edge = cell->sliceInfo.clkmux == id_INV ? FALLING_EDGE : RISING_EDGE; + NPNR_ASSERT(port == id_Q); + port = id_Q0; + info.edge = (cell->ffInfo.flags & ArchCellInfo::FF_CLKINV) ? FALLING_EDGE : RISING_EDGE; info.clock_port = id_CLK; bool is_path = get_delay_from_tmg_db(id_SLOGICB, id_CLK, port, info.clockToQ); NPNR_ASSERT(is_path); diff --git a/ecp5/arch.h b/ecp5/arch.h index c1bed2b361..3d95fc9b18 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -452,7 +452,6 @@ struct Arch : BaseArch mutable dict pip_by_name; - std::vector bel_to_cell; enum class LutPermRule { NONE, @@ -462,6 +461,39 @@ struct Arch : BaseArch std::vector lutperm_allowed; bool disable_router_lutperm = false; + // For fast, incremental validity checking of split SLICE + + // BEL z-position lookup, x-ored with (index in tile) << 2 + enum LogicBELType + { + BEL_COMB = 0, + BEL_FF = 1, + BEL_RAMW = 2 + }; + static const int lc_idx_shift = 2; + + struct LogicTileStatus + { + // Per-SLICE valid and dirty bits + struct SliceStatus + { + bool valid = true, dirty = true; + } slices[4]; + // Per-tile legality check for control set legality + bool tile_valid = true; + bool tile_dirty = true; + // Fast index from z-pos to cell + std::array cells; + }; + + struct TileStatus + { + std::vector boundcells; + LogicTileStatus *lts = nullptr; + // TODO: use similar mechanism for DSP legality checking + ~TileStatus() { delete lts; } + }; + // faster replacements for base_pip2net, base_wire2net // indexed by get_pip_vecidx() std::vector pip2net; @@ -492,7 +524,7 @@ struct Arch : BaseArch // ------------------------------------------------- - static const int max_loc_bels = 20; + static const int max_loc_bels = 32; int getGridDimX() const override { return chip_info->width; }; int getGridDimY() const override { return chip_info->height; }; @@ -509,6 +541,11 @@ struct Arch : BaseArch return &(chip_info->locations[chip_info->location_type[id.location.y * chip_info->width + id.location.x]]); } + template inline int tile_index(Id id) const + { + return id.location.y * chip_info->width + id.location.x; + } + IdStringList getBelName(BelId bel) const override { NPNR_ASSERT(bel != BelId()); @@ -519,41 +556,57 @@ struct Arch : BaseArch uint32_t getBelChecksum(BelId bel) const override { return bel.index; } - int get_bel_flat_index(BelId bel) const - { - return (bel.location.y * chip_info->width + bel.location.x) * max_loc_bels + bel.index; - } - int get_slice_index(int x, int y, int slice) const { NPNR_ASSERT(slice >= 0 && slice < 4); return (y * chip_info->width + x) * 4 + slice; } + void update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell) + { + CellInfo *act_cell = (old_cell == nullptr) ? new_cell : old_cell; + if (act_cell->type == id_TRELLIS_FF || act_cell->type == id_TRELLIS_COMB || act_cell->type == id_TRELLIS_RAMW) { + LogicTileStatus *lts = tile_status.at(tile_index(bel)).lts; + NPNR_ASSERT(lts != nullptr); + int z = loc_info(bel)->bel_data[bel.index].z; + lts->slices[(z >> lc_idx_shift) / 2].dirty = true; + if (act_cell->type == id_TRELLIS_FF) + lts->tile_dirty = true; // because FF CLK/LSR signals are tile-wide + if (act_cell->type == id_TRELLIS_COMB && (act_cell->combInfo.flags & ArchCellInfo::COMB_LUTRAM)) + lts->tile_dirty = true; // because RAM shares CLK/LSR signals with FFs + lts->cells[z] = new_cell; + } + } + void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override { NPNR_ASSERT(bel != BelId()); - int idx = get_bel_flat_index(bel); - NPNR_ASSERT(bel_to_cell.at(idx) == nullptr); - bel_to_cell[idx] = cell; + auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index); + NPNR_ASSERT(slot == nullptr); + slot = cell; cell->bel = bel; cell->belStrength = strength; - if (getBelType(bel) == id_TRELLIS_SLICE) { - lutperm_allowed.at(get_slice_index(bel.location.x, bel.location.y, getBelLocation(bel).z)) = - (cell->sliceInfo.is_memory ? LutPermRule::NONE - : (cell->sliceInfo.is_carry ? LutPermRule::CARRY : LutPermRule::ALL)); + if (getBelType(bel) == id_TRELLIS_COMB) { + int flags = cell->combInfo.flags; + lutperm_allowed.at( + get_slice_index(bel.location.x, bel.location.y, (getBelLocation(bel).z >> lc_idx_shift) / 2)) = + (((flags & ArchCellInfo::COMB_LUTRAM) || (flags & ArchCellInfo::COMB_RAMW_BLOCK)) + ? LutPermRule::NONE + : ((flags & ArchCellInfo::COMB_CARRY) ? LutPermRule::CARRY : LutPermRule::ALL)); } + update_bel(bel, nullptr, cell); refreshUiBel(bel); } void unbindBel(BelId bel) override { NPNR_ASSERT(bel != BelId()); - int idx = get_bel_flat_index(bel); - NPNR_ASSERT(bel_to_cell.at(idx) != nullptr); - bel_to_cell[idx]->bel = BelId(); - bel_to_cell[idx]->belStrength = STRENGTH_NONE; - bel_to_cell[idx] = nullptr; + auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index); + NPNR_ASSERT(slot != nullptr); + update_bel(bel, slot, nullptr); + slot->bel = BelId(); + slot->belStrength = STRENGTH_NONE; + slot = nullptr; refreshUiBel(bel); } @@ -574,19 +627,22 @@ struct Arch : BaseArch bool checkBelAvail(BelId bel) const override { NPNR_ASSERT(bel != BelId()); - return bel_to_cell[get_bel_flat_index(bel)] == nullptr; + const CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index); + return slot == nullptr; } CellInfo *getBoundBelCell(BelId bel) const override { NPNR_ASSERT(bel != BelId()); - return bel_to_cell[get_bel_flat_index(bel)]; + CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index); + return slot; } CellInfo *getConflictingBelCell(BelId bel) const override { NPNR_ASSERT(bel != BelId()); - return bel_to_cell[get_bel_flat_index(bel)]; + CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index); + return slot; } BelRange getBels() const override @@ -957,12 +1013,11 @@ struct Arch : BaseArch bool isBelLocationValid(BelId bel) const override; // Helper function for above - bool slices_compatible(const std::vector &cells) const; + bool slices_compatible(LogicTileStatus *lts) const; + void assign_arch_info_for_cell(CellInfo *ci); void assignArchInfo() override; - void permute_luts(); - std::vector> get_tiles_at_loc(int row, int col); std::string get_tile_by_type_loc(int row, int col, std::string type) const { @@ -1028,6 +1083,8 @@ struct Arch : BaseArch std::vector cell_types; std::vector buckets; + + mutable std::vector tile_status; }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index b1849ee672..afe9aca0d7 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -33,53 +33,156 @@ inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name) return found->second.net; } -bool Arch::slices_compatible(const std::vector &cells) const +bool Arch::slices_compatible(LogicTileStatus *lts) const { - // TODO: allow different LSR/CLK and MUX/SRMODE settings once - // routing details are worked out - IdString clk_sig, lsr_sig; - IdString CLKMUX, LSRMUX, SRMODE; - bool first = true; - for (auto cell : cells) { - if (cell->sliceInfo.using_dff) { - if (first) { - clk_sig = cell->sliceInfo.clk_sig; - lsr_sig = cell->sliceInfo.lsr_sig; - CLKMUX = cell->sliceInfo.clkmux; - LSRMUX = cell->sliceInfo.lsrmux; - SRMODE = cell->sliceInfo.srmode; - } else { - if (cell->sliceInfo.clk_sig != clk_sig) - return false; - if (cell->sliceInfo.lsr_sig != lsr_sig) - return false; - if (cell->sliceInfo.clkmux != CLKMUX) + if (lts == nullptr) + return true; + for (int sl = 0; sl < 4; sl++) { + if (!lts->slices[sl].dirty) { + if (!lts->slices[sl].valid) + return false; + continue; + } + lts->slices[sl].dirty = false; + lts->slices[sl].valid = false; + bool found_ff = false; + uint8_t last_ff_flags = 0; + IdString last_ce_sig; + bool ramw_used = false; + if (sl == 2 && lts->cells[((sl * 2) << lc_idx_shift) | BEL_RAMW] != nullptr) + ramw_used = true; + for (int l = 0; l < 2; l++) { + bool comb_m_used = false; + CellInfo *comb = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_COMB]; + if (comb != nullptr) { + uint8_t flags = comb->combInfo.flags; + if (ramw_used && !(flags & ArchCellInfo::COMB_RAMW_BLOCK)) return false; - if (cell->sliceInfo.lsrmux != LSRMUX) + if (flags & ArchCellInfo::COMB_MUX5) { + // MUX5 uses M signal and must be in LC 0 + comb_m_used = true; + if (l != 0) + return false; + } + if (flags & ArchCellInfo::COMB_MUX6) { + // MUX6+ uses M signal and must be in LC 1 + comb_m_used = true; + if (l != 1) + return false; + if (comb->combInfo.mux_fxad != nullptr && + (comb->combInfo.mux_fxad->combInfo.flags & ArchCellInfo::COMB_MUX5)) { + // LUT6 structure must be rooted at SLICE 0 or 2 + if (sl != 0 && sl != 2) + return false; + } + } + // LUTRAM must be in bottom two SLICEs only + if ((flags & ArchCellInfo::COMB_LUTRAM) && (sl > 1)) return false; - if (cell->sliceInfo.srmode != SRMODE) + if (l == 1) { + // Carry usage must be the same for LCs 0 and 1 in a SLICE + CellInfo *comb0 = lts->cells[((sl * 2 + 0) << lc_idx_shift) | BEL_COMB]; + if (comb0 && + ((comb0->combInfo.flags & ArchCellInfo::COMB_CARRY) != (flags & ArchCellInfo::COMB_CARRY))) + return false; + } + } + + CellInfo *ff = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_FF]; + if (ff != nullptr) { + uint8_t flags = ff->ffInfo.flags; + if (comb_m_used && (flags & ArchCellInfo::FF_M_USED)) return false; + if (found_ff) { + if ((flags & ArchCellInfo::FF_GSREN) != (last_ff_flags & ArchCellInfo::FF_GSREN)) + return false; + if ((flags & ArchCellInfo::FF_CECONST) != (last_ff_flags & ArchCellInfo::FF_CECONST)) + return false; + if ((flags & ArchCellInfo::FF_CEINV) != (last_ff_flags & ArchCellInfo::FF_CEINV)) + return false; + if (ff->ffInfo.ce_sig != last_ce_sig) + return false; + } else { + found_ff = true; + last_ff_flags = flags; + last_ce_sig = ff->ffInfo.ce_sig; + } + } + } + + lts->slices[sl].valid = true; + } + if (lts->tile_dirty) { + bool found_global_ff = false; + bool found_global_dpram = false; + bool global_lsrinv = false; + bool global_clkinv = false; + bool global_async = false; + + IdString clk_sig, lsr_sig; + + lts->tile_dirty = false; + lts->tile_valid = false; + +#define CHECK_EQUAL(x, y) \ + do { \ + if ((x) != (y)) \ + return false; \ + } while (0) + for (int i = 0; i < 8; i++) { + if (i < 4) { + // DPRAM + CellInfo *comb = lts->cells[(i << lc_idx_shift) | BEL_COMB]; + if (comb != nullptr && (comb->combInfo.flags & ArchCellInfo::COMB_LUTRAM)) { + if (found_global_dpram) { + CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV), global_clkinv); + CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV), global_lsrinv); + } else { + global_clkinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV); + global_lsrinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV); + found_global_dpram = true; + } + } + } + // FF + CellInfo *ff = lts->cells[(i << lc_idx_shift) | BEL_FF]; + if (ff != nullptr) { + if (found_global_dpram) { + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv); + } + if (found_global_ff) { + CHECK_EQUAL(ff->ffInfo.clk_sig, clk_sig); + CHECK_EQUAL(ff->ffInfo.lsr_sig, lsr_sig); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC), global_async); + + } else { + clk_sig = ff->ffInfo.clk_sig; + lsr_sig = ff->ffInfo.lsr_sig; + global_clkinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV); + global_lsrinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV); + global_async = bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC); + found_global_ff = true; + } } - first = false; } +#undef CHECK_EQUAL + lts->tile_valid = true; + } else { + if (!lts->tile_valid) + return false; } + return true; } bool Arch::isBelLocationValid(BelId bel) const { - if (getBelType(bel) == id_TRELLIS_SLICE) { - std::vector bel_cells; - Loc bel_loc = getBelLocation(bel); - for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { - CellInfo *cell_other = getBoundBelCell(bel_other); - if (cell_other != nullptr) { - bel_cells.push_back(cell_other); - } - } - if (getBoundBelCell(bel) != nullptr && getBoundBelCell(bel)->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1)) - return false; - return slices_compatible(bel_cells); + IdString bel_type = getBelType(bel); + if (bel_type.in(id_TRELLIS_COMB, id_TRELLIS_FF, id_TRELLIS_RAMW)) { + return slices_compatible(tile_status.at(tile_index(bel)).lts); } else { CellInfo *cell = getBoundBelCell(bel); if (cell == nullptr) { @@ -93,70 +196,6 @@ bool Arch::isBelLocationValid(BelId bel) const } } -void Arch::permute_luts() -{ - TimingAnalyser tmg(getCtx()); - tmg.setup(); - - auto proc_lut = [&](CellInfo *ci, int lut) { - std::vector port_names; - for (int i = 0; i < 4; i++) - port_names.push_back(id(std::string("ABCD").substr(i, 1) + std::to_string(lut))); - - std::vector> inputs; - std::vector orig_nets; - - for (int i = 0; i < 4; i++) { - if (!ci->ports.count(port_names.at(i))) { - ci->ports[port_names.at(i)].name = port_names.at(i); - ci->ports[port_names.at(i)].type = PORT_IN; - } - auto &port = ci->ports.at(port_names.at(i)); - float crit = (port.net == nullptr) ? 0 : tmg.get_criticality(CellPortKey(ci->name, port_names.at(i))); - orig_nets.push_back(port.net); - inputs.emplace_back(crit, i); - } - // Least critical first (A input is slowest) - - // Avoid permuting locked LUTs (e.g. from an OOC submodule) - if (ci->belStrength <= STRENGTH_STRONG) - std::sort(inputs.begin(), inputs.end()); - for (int i = 0; i < 4; i++) { - IdString p = port_names.at(i); - // log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first); - ci->disconnectPort(p); - ci->ports.at(p).net = nullptr; - if (orig_nets.at(inputs.at(i).second) != nullptr) { - ci->connectPort(p, orig_nets.at(inputs.at(i).second)); - ci->params[id(p.str(this) + "MUX")] = p.str(this); - } else { - ci->params[id(p.str(this) + "MUX")] = std::string("1"); - } - } - // Rewrite function - int old_init = int_or_default(ci->params, id("LUT" + std::to_string(lut) + "_INITVAL"), 0); - int new_init = 0; - for (int i = 0; i < 16; i++) { - int old_index = 0; - for (int k = 0; k < 4; k++) { - if (i & (1 << k)) - old_index |= (1 << inputs.at(k).second); - } - if (old_init & (1 << old_index)) - new_init |= (1 << i); - } - ci->params[id("LUT" + std::to_string(lut) + "_INITVAL")] = Property(new_init, 16); - }; - - for (auto &cell : cells) { - CellInfo *ci = cell.second.get(); - if (ci->type == id_TRELLIS_SLICE && str_or_default(ci->params, id_MODE, "LOGIC") == "LOGIC") { - proc_lut(ci, 0); - proc_lut(ci, 1); - } - } -} - void Arch::setup_wire_locations() { wire_loc_overrides.clear(); diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index dd260a3e87..b7d892c538 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -156,19 +156,46 @@ struct ArchNetInfo typedef IdString ClusterId; +struct CellInfo; struct NetInfo; struct ArchCellInfo : BaseClusterInfo { + enum CombFlags : uint8_t + { + COMB_NONE = 0x00, + COMB_CARRY = 0x01, + COMB_LUTRAM = 0x02, + COMB_MUX5 = 0x04, + COMB_MUX6 = 0x08, + COMB_RAM_WCKINV = 0x10, + COMB_RAM_WREINV = 0x20, + COMB_RAMW_BLOCK = 0x40, + }; + + enum FFFlags : uint8_t + { + FF_NONE = 0x00, + FF_CLKINV = 0x01, + FF_CEINV = 0x02, + FF_CECONST = 0x04, + FF_LSRINV = 0x08, + FF_GSREN = 0x10, + FF_ASYNC = 0x20, + FF_M_USED = 0x40, + }; + + struct + { + uint8_t flags; + IdString ram_wck, ram_wre; + CellInfo *mux_fxad; + } combInfo; struct { - bool using_dff; - bool has_l6mux; - bool is_carry; - bool is_memory; - IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; - int sd0, sd1; - } sliceInfo; + uint8_t flags; + IdString clk_sig, lsr_sig, ce_sig, di_sig; + } ffInfo; struct { bool is_pdp; diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 40d843b965..3c01fe71d3 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -524,10 +524,10 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) cc.tiles[tile].add_arc(sink, source); } -static unsigned permute_lut(Context *ctx, CellInfo *cell, pool &used_phys_pins, int k, unsigned orig_init) +static unsigned permute_lut(Context *ctx, CellInfo *cell, pool &used_phys_pins, unsigned orig_init) { std::array, 4> phys_to_log; - const std::array ports{k ? id_A1 : id_A0, k ? id_B1 : id_B0, k ? id_C1 : id_C0, k ? id_D1 : id_D0}; + const std::array ports{id_A, id_B, id_C, id_D}; for (unsigned i = 0; i < 4; i++) { WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]); for (PipId pip : ctx->getPipsUphill(pin_wire)) { @@ -547,7 +547,7 @@ static unsigned permute_lut(Context *ctx, CellInfo *cell, pool &used_p for (unsigned i = 0; i < 4; i++) if (!phys_to_log.at(i).empty()) used_phys_pins.insert(ports.at(i)); - if (cell->sliceInfo.is_carry) { + if (cell->combInfo.flags & ArchCellInfo::COMB_CARRY) { // Insert dummy entries to ensure we keep the split between the two halves of a CCU2 for (unsigned i = 0; i < 4; i++) { if (!phys_to_log.at(i).empty()) @@ -840,77 +840,72 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex log_warning("found unplaced cell '%s' during bitstream gen\n", ci->name.c_str(ctx)); } BelId bel = ci->bel; - if (ci->type == id_TRELLIS_SLICE) { + if (ci->type == id_TRELLIS_COMB) { pool used_phys_pins; std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); - std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get(); - int lut0_init = int_or_default(ci->params, id_LUT0_INITVAL); - int lut1_init = int_or_default(ci->params, id_LUT1_INITVAL); - cc.tiles[tname].add_word(slice + ".K0.INIT", - int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16)); - cc.tiles[tname].add_word(slice + ".K1.INIT", - int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16)); - cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, id_MODE, "LOGIC")); - cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED")); - cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, id_REG0_SD, "0")); - cc.tiles[tname].add_enum(slice + ".REG1.SD", intstr_or_default(ci->params, id_REG1_SD, "0")); - cc.tiles[tname].add_enum(slice + ".REG0.REGSET", str_or_default(ci->params, id_REG0_REGSET, "RESET")); - cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, id_REG1_REGSET, "RESET")); - cc.tiles[tname].add_enum(slice + ".REG0.LSRMODE", str_or_default(ci->params, id_REG0_LSRMODE, "LSR")); - cc.tiles[tname].add_enum(slice + ".REG1.LSRMODE", str_or_default(ci->params, id_REG1_LSRMODE, "LSR")); - cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, id_CEMUX, "1")); - - if (ci->sliceInfo.using_dff) { - NetInfo *lsrnet = nullptr; - if (ci->ports.find(id_LSR) != ci->ports.end() && ci->ports.at(id_LSR).net != nullptr) - lsrnet = ci->ports.at(id_LSR).net; - if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR0")) == lsrnet) { - cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); - cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR")); - } - if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR1")) == lsrnet) { - cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); - cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR")); - } - - NetInfo *clknet = nullptr; - if (ci->ports.find(id_CLK) != ci->ports.end() && ci->ports.at(id_CLK).net != nullptr) - clknet = ci->ports.at(id_CLK).net; - if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK0")) == clknet) { - cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK")); - } - if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK1")) == clknet) { - cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK")); - } - } - - if (str_or_default(ci->params, id_MODE, "LOGIC") == "CCU2") { - cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", - str_or_default(ci->params, id_CCU2_INJECT1_0, "YES")); - cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", - str_or_default(ci->params, id_CCU2_INJECT1_1, "YES")); + int z = ctx->loc_info(bel)->bel_data[bel.index].z >> Arch::lc_idx_shift; + std::string slice = std::string("SLICE") + "ABCD"[z / 2]; + std::string lc = std::to_string(z % 2); + std::string mode = str_or_default(ci->params, id_MODE, "LOGIC"); + if (mode == "RAMW_BLOCK") + continue; + int lut_init = int_or_default(ci->params, id_INITVAL); + cc.tiles[tname].add_enum(slice + ".MODE", mode); + cc.tiles[tname].add_word(slice + ".K" + lc + ".INIT", + int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, lut_init), 16)); + if (mode == "CCU2") { + cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_" + lc, + str_or_default(ci->params, id_CCU2_INJECT1, "YES")); } else { // Don't interfere with cascade mux wiring - cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", "_NONE_"); - cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", "_NONE_"); + cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_" + lc, "_NONE_"); } - - if (str_or_default(ci->params, id_MODE, "LOGIC") == "DPRAM" && slice == "SLICEA") { + if (mode == "DPRAM" && slice == "SLICEA" && lc == "0") { cc.tiles[tname].add_enum(slice + ".WREMUX", str_or_default(ci->params, id_WREMUX, "WRE")); - std::string wckmux = str_or_default(ci->params, id_WCKMUX, "WCK"); wckmux = (wckmux == "WCK") ? "CLK" : wckmux; cc.tiles[tname].add_enum("CLK1.CLKMUX", wckmux); } - - // Tie unused inputs high - for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) { + for (auto input : {id_A, id_B, id_C, id_D}) { if (!used_phys_pins.count(input)) { - cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1"); + cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + lc + "MUX", "1"); } } + } else if (ci->type == id_TRELLIS_FF) { + std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); + int z = ctx->loc_info(bel)->bel_data[bel.index].z >> Arch::lc_idx_shift; + std::string slice = std::string("SLICE") + "ABCD"[z / 2]; + std::string lc = std::to_string(z % 2); + + cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, id_GSR, "ENABLED")); + cc.tiles[tname].add_enum(slice + ".REG" + lc + ".SD", intstr_or_default(ci->params, id_SD, "0")); + cc.tiles[tname].add_enum(slice + ".REG" + lc + ".REGSET", str_or_default(ci->params, id_REGSET, "RESET")); + cc.tiles[tname].add_enum(slice + ".REG" + lc + ".LSRMODE", str_or_default(ci->params, id_LSRMODE, "LSR")); + + cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, id_CEMUX, "1")); + + NetInfo *lsrnet = ci->getPort(id_LSR); + if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR0")) == lsrnet) { + cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR")); + } + if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "LSR1")) == lsrnet) { + cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, id_LSRMUX, "LSR")); + } - // TODO: CLKMUX + NetInfo *clknet = ci->getPort(id_CLK); + if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK0")) == clknet) { + cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK")); + } + if (ctx->getBoundWireNet(ctx->get_wire_by_loc_basename(bel.location, "CLK1")) == clknet) { + cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, id_CLKMUX, "CLK")); + } + } else if (ci->type == id_TRELLIS_RAMW) { + std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); + cc.tiles[tname].add_enum("SLICEC.MODE", "RAMW"); + cc.tiles[tname].add_word("SLICEC.K0.INIT", std::vector(16, false)); + cc.tiles[tname].add_word("SLICEC.K1.INIT", std::vector(16, false)); } else if (ci->type == id_TRELLIS_IO) { std::string pio = ctx->loc_info(bel)->bel_data[bel.index].name.get(); std::string iotype = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 2c5f96d3fe..99f672a605 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -47,49 +47,28 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str } }; - if (type == id_TRELLIS_SLICE) { + if (type == id_TRELLIS_COMB) { new_cell->params[id_MODE] = std::string("LOGIC"); - new_cell->params[id_GSR] = std::string("DISABLED"); - new_cell->params[id_SRMODE] = std::string("LSR_OVER_CE"); - new_cell->params[id_CEMUX] = std::string("1"); - new_cell->params[id_CLKMUX] = std::string("CLK"); - new_cell->params[id_LSRMUX] = std::string("LSR"); - new_cell->params[id_LUT0_INITVAL] = Property(0, 16); - new_cell->params[id_LUT1_INITVAL] = Property(0, 16); - new_cell->params[id_REG0_SD] = std::string("0"); - new_cell->params[id_REG1_SD] = std::string("0"); - new_cell->params[id_REG0_REGSET] = std::string("RESET"); - new_cell->params[id_REG1_REGSET] = std::string("RESET"); - new_cell->params[id_CCU2_INJECT1_0] = std::string("NO"); - new_cell->params[id_CCU2_INJECT1_1] = std::string("NO"); + new_cell->params[id_INITVAL] = Property(0, 16); + new_cell->params[id_CCU2_INJECT1] = std::string("NO"); new_cell->params[id_WREMUX] = std::string("WRE"); - new_cell->addInput(id_A0); - new_cell->addInput(id_B0); - new_cell->addInput(id_C0); - new_cell->addInput(id_D0); - - new_cell->addInput(id_A1); - new_cell->addInput(id_B1); - new_cell->addInput(id_C1); - new_cell->addInput(id_D1); + new_cell->addInput(id_A); + new_cell->addInput(id_B); + new_cell->addInput(id_C); + new_cell->addInput(id_D); - new_cell->addInput(id_M0); - new_cell->addInput(id_M1); + new_cell->addInput(id_M); + new_cell->addInput(id_F1); new_cell->addInput(id_FCI); new_cell->addInput(id_FXA); new_cell->addInput(id_FXB); - new_cell->addInput(id_CLK); - new_cell->addInput(id_LSR); - new_cell->addInput(id_CE); - new_cell->addInput(id_DI0); new_cell->addInput(id_DI1); - new_cell->addInput(id_WD0); - new_cell->addInput(id_WD1); + new_cell->addInput(id_WD); new_cell->addInput(id_WAD0); new_cell->addInput(id_WAD1); new_cell->addInput(id_WAD2); @@ -97,23 +76,15 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->addInput(id_WRE); new_cell->addInput(id_WCK); - new_cell->addOutput(id_F0); - new_cell->addOutput(id_Q0); - new_cell->addOutput(id_F1); - new_cell->addOutput(id_Q1); + new_cell->addOutput(id_F); new_cell->addOutput(id_FCO); - new_cell->addOutput(id_OFX0); - new_cell->addOutput(id_OFX1); - - new_cell->addOutput(id_WDO0); - new_cell->addOutput(id_WDO1); - new_cell->addOutput(id_WDO2); - new_cell->addOutput(id_WDO3); - new_cell->addOutput(id_WADO0); - new_cell->addOutput(id_WADO1); - new_cell->addOutput(id_WADO2); - new_cell->addOutput(id_WADO3); + new_cell->addOutput(id_OFX); + } else if (type == id_TRELLIS_RAMW) { + for (auto i : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) + new_cell->addInput(i); + for (auto o : {id_WDO0, id_WDO1, id_WDO2, id_WDO3, id_WADO0, id_WADO1, id_WADO2, id_WADO3}) + new_cell->addOutput(o); } else if (type == id_TRELLIS_IO) { new_cell->params[id_DIR] = std::string("INPUT"); new_cell->attrs[id_IO_TYPE] = std::string("LVCMOS33"); @@ -200,122 +171,6 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str return new_cell; } -static void set_param_safe(bool has_ff, CellInfo *lc, IdString name, const std::string &value) -{ - NPNR_ASSERT(!has_ff || lc->params.at(name) == value); - lc->params[name] = value; -} - -static void replace_port_safe(bool has_ff, CellInfo *ff, IdString ff_port, CellInfo *lc, IdString lc_port) -{ - if (has_ff) { - NPNR_ASSERT(lc->ports.at(lc_port).net == ff->ports.at(ff_port).net); - NetInfo *ffnet = ff->ports.at(ff_port).net; - if (ffnet != nullptr) - ffnet->users.remove(ff->ports.at(ff_port).user_idx); - } else { - ff->movePortTo(ff_port, lc, lc_port); - } -} - -void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut) -{ - if (lc->hierpath == IdString()) - lc->hierpath = ff->hierpath; - bool has_ff = lc->ports.at(id_Q0).net != nullptr || lc->ports.at(id_Q1).net != nullptr; - std::string reg = "REG" + std::to_string(index); - set_param_safe(has_ff, lc, id_SRMODE, str_or_default(ff->params, id_SRMODE, "LSR_OVER_CE")); - set_param_safe(has_ff, lc, id_GSR, str_or_default(ff->params, id_GSR, "DISABLED")); - set_param_safe(has_ff, lc, id_CEMUX, str_or_default(ff->params, id_CEMUX, "1")); - set_param_safe(has_ff, lc, id_LSRMUX, str_or_default(ff->params, id_LSRMUX, "LSR")); - set_param_safe(has_ff, lc, id_CLKMUX, str_or_default(ff->params, id_CLKMUX, "CLK")); - - lc->params[ctx->id(reg + "_SD")] = std::string(driven_by_lut ? "1" : "0"); - lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, id_REGSET, "RESET"); - lc->params[ctx->id(reg + "_LSRMODE")] = str_or_default(ff->params, id_LSRMODE, "LSR"); - replace_port_safe(has_ff, ff, id_CLK, lc, id_CLK); - if (ff->ports.find(id_LSR) != ff->ports.end()) - replace_port_safe(has_ff, ff, id_LSR, lc, id_LSR); - if (ff->ports.find(id_CE) != ff->ports.end()) - replace_port_safe(has_ff, ff, id_CE, lc, id_CE); - - ff->movePortTo(id_Q, lc, ctx->id("Q" + std::to_string(index))); - if (ff->getPort(id_M) != nullptr) { - // PRLD FFs that use both M and DI - NPNR_ASSERT(!driven_by_lut); - // As M is used; must route DI through a new LUT - lc->params[ctx->id(reg + "_SD")] = std::string("1"); - lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = Property(0xFF00, 16); - ff->movePortTo(id_DI, lc, ctx->id("D" + std::to_string(index))); - ff->movePortTo(id_M, lc, ctx->id("M" + std::to_string(index))); - lc->connectPorts(ctx->id("F" + std::to_string(index)), lc, ctx->id("DI" + std::to_string(index))); - } else { - if (driven_by_lut) { - ff->movePortTo(id_DI, lc, ctx->id("DI" + std::to_string(index))); - } else { - ff->movePortTo(id_DI, lc, ctx->id("M" + std::to_string(index))); - } - } -} - -void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) -{ - if (lc->hierpath == IdString()) - lc->hierpath = lut->hierpath; - lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = - get_or_default(lut->params, id_INIT, Property(0, 16)); - lut->movePortTo(id_A, lc, ctx->id("A" + std::to_string(index))); - lut->movePortTo(id_B, lc, ctx->id("B" + std::to_string(index))); - lut->movePortTo(id_C, lc, ctx->id("C" + std::to_string(index))); - lut->movePortTo(id_D, lc, ctx->id("D" + std::to_string(index))); - lut->movePortTo(id_Z, lc, ctx->id("F" + std::to_string(index))); -} - -void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) -{ - if (lc->hierpath == IdString()) - lc->hierpath = ccu->hierpath; - lc->params[id_MODE] = std::string("CCU2"); - lc->params[id_LUT0_INITVAL] = get_or_default(ccu->params, id_INIT0, Property(0, 16)); - lc->params[id_LUT1_INITVAL] = get_or_default(ccu->params, id_INIT1, Property(0, 16)); - - lc->params[id_CCU2_INJECT1_0] = str_or_default(ccu->params, id_INJECT1_0, "YES"); - lc->params[id_CCU2_INJECT1_1] = str_or_default(ccu->params, id_INJECT1_1, "YES"); - - ccu->movePortTo(id_CIN, lc, id_FCI); - - ccu->movePortTo(id_A0, lc, id_A0); - ccu->movePortTo(id_B0, lc, id_B0); - ccu->movePortTo(id_C0, lc, id_C0); - ccu->movePortTo(id_D0, lc, id_D0); - - ccu->movePortTo(id_A1, lc, id_A1); - ccu->movePortTo(id_B1, lc, id_B1); - ccu->movePortTo(id_C1, lc, id_C1); - ccu->movePortTo(id_D1, lc, id_D1); - - ccu->movePortTo(id_S0, lc, id_F0); - ccu->movePortTo(id_S1, lc, id_F1); - - ccu->movePortTo(id_COUT, lc, id_FCO); -} - -void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc) -{ - if (lc->hierpath == IdString()) - lc->hierpath = ram->hierpath; - lc->params[id_MODE] = std::string("RAMW"); - ram->movePortTo(ctx->id("WAD[0]"), lc, id_D0); - ram->movePortTo(ctx->id("WAD[1]"), lc, id_B0); - ram->movePortTo(ctx->id("WAD[2]"), lc, id_C0); - ram->movePortTo(ctx->id("WAD[3]"), lc, id_A0); - - ram->movePortTo(ctx->id("DI[0]"), lc, id_C1); - ram->movePortTo(ctx->id("DI[1]"), lc, id_A1); - ram->movePortTo(ctx->id("DI[2]"), lc, id_D1); - ram->movePortTo(ctx->id("DI[3]"), lc, id_B1); -} - static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit) { auto init_prop = get_or_default(ram->params, id_INITVAL, Property(0, 64)); @@ -333,16 +188,70 @@ static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit) return value; } -void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index) +void lut_to_comb(Context *ctx, CellInfo *lut) +{ + lut->type = id_TRELLIS_COMB; + lut->params[id_INITVAL] = get_or_default(lut->params, id_INIT, Property(0, 16)); + lut->params.erase(id_INIT); + lut->renamePort(id_Z, id_F); +} + +void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw) +{ + if (ramw->hierpath == IdString()) + ramw->hierpath = ramw->hierpath; + ram->movePortTo(ctx->id("WAD[0]"), ramw, id_D0); + ram->movePortTo(ctx->id("WAD[1]"), ramw, id_B0); + ram->movePortTo(ctx->id("WAD[2]"), ramw, id_C0); + ram->movePortTo(ctx->id("WAD[3]"), ramw, id_A0); + + ram->movePortTo(ctx->id("DI[0]"), ramw, id_C1); + ram->movePortTo(ctx->id("DI[1]"), ramw, id_A1); + ram->movePortTo(ctx->id("DI[2]"), ramw, id_D1); + ram->movePortTo(ctx->id("DI[3]"), ramw, id_B1); +} + +void ccu2_to_comb(Context *ctx, CellInfo *ccu, CellInfo *comb, NetInfo *internal_carry, int i) { - if (lc->hierpath == IdString()) - lc->hierpath = ram->hierpath; - lc->params[id_MODE] = std::string("DPRAM"); - lc->params[id_WREMUX] = str_or_default(ram->params, id_WREMUX, "WRE"); - lc->params[id_WCKMUX] = str_or_default(ram->params, id_WCKMUX, "WCK"); + std::string ii = std::to_string(i); + if (comb->hierpath == IdString()) + comb->hierpath = ccu->hierpath; + + comb->params[id_MODE] = std::string("CCU2"); + comb->params[id_INITVAL] = get_or_default(ccu->params, ctx->id("INIT" + ii), Property(0, 16)); + comb->params[id_CCU2_INJECT1] = str_or_default(ccu->params, ctx->id("INJECT1_" + ii), "YES"); + + ccu->movePortTo(ctx->id("A" + ii), comb, id_A); + ccu->movePortTo(ctx->id("B" + ii), comb, id_B); + ccu->movePortTo(ctx->id("C" + ii), comb, id_C); + ccu->movePortTo(ctx->id("D" + ii), comb, id_D); + + ccu->movePortTo(ctx->id("S" + ii), comb, id_F); + + if (i == 0) { + ccu->movePortTo(id_CIN, comb, id_FCI); + comb->connectPort(id_FCO, internal_carry); + } else if (i == 1) { + comb->connectPort(id_FCI, internal_carry); + ccu->movePortTo(id_COUT, comb, id_FCO); + } else { + NPNR_ASSERT_FALSE("bad carry index!"); + } - unsigned permuted_init0 = 0, permuted_init1 = 0; - unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1); + for (auto &attr : ccu->attrs) + comb->attrs[attr.first] = attr.second; +} + +void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, int index) +{ + if (comb->hierpath == IdString()) + comb->hierpath = ram->hierpath; + comb->params[id_MODE] = std::string("DPRAM"); + comb->params[id_WREMUX] = str_or_default(ram->params, id_WREMUX, "WRE"); + comb->params[id_WCKMUX] = str_or_default(ram->params, id_WCKMUX, "WCK"); + + unsigned permuted_init = 0; + unsigned init = get_dram_init(ctx, ram, index); for (int i = 0; i < 16; i++) { int permuted_addr = 0; @@ -354,58 +263,41 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw permuted_addr |= 4; if (i & 8) permuted_addr |= 1; - if (init0 & (1 << permuted_addr)) - permuted_init0 |= (1 << i); - if (init1 & (1 << permuted_addr)) - permuted_init1 |= (1 << i); + if (init & (1 << permuted_addr)) + permuted_init |= (1 << i); } - lc->params[id_LUT0_INITVAL] = Property(permuted_init0, 16); - lc->params[id_LUT1_INITVAL] = Property(permuted_init1, 16); + comb->params[ctx->id("INITVAL")] = Property(permuted_init, 16); - if (ram->ports.count(ctx->id("RAD[0]"))) { - lc->connectPort(id_D0, ram->ports.at(ctx->id("RAD[0]")).net); - lc->connectPort(id_D1, ram->ports.at(ctx->id("RAD[0]")).net); - } - if (ram->ports.count(ctx->id("RAD[1]"))) { - lc->connectPort(id_B0, ram->ports.at(ctx->id("RAD[1]")).net); - lc->connectPort(id_B1, ram->ports.at(ctx->id("RAD[1]")).net); - } - if (ram->ports.count(ctx->id("RAD[2]"))) { - lc->connectPort(id_C0, ram->ports.at(ctx->id("RAD[2]")).net); - lc->connectPort(id_C1, ram->ports.at(ctx->id("RAD[2]")).net); - } - if (ram->ports.count(ctx->id("RAD[3]"))) { - lc->connectPort(id_A0, ram->ports.at(ctx->id("RAD[3]")).net); - lc->connectPort(id_A1, ram->ports.at(ctx->id("RAD[3]")).net); - } + if (ram->ports.count(ctx->id("RAD[0]"))) + comb->connectPort(id_D, ram->ports.at(ctx->id("RAD[0]")).net); - if (ram->ports.count(id_WRE)) - lc->connectPort(id_WRE, ram->ports.at(id_WRE).net); - if (ram->ports.count(id_WCK)) - lc->connectPort(id_WCK, ram->ports.at(id_WCK).net); + if (ram->ports.count(ctx->id("RAD[1]"))) + comb->connectPort(id_B, ram->ports.at(ctx->id("RAD[1]")).net); + + if (ram->ports.count(ctx->id("RAD[2]"))) + comb->connectPort(id_C, ram->ports.at(ctx->id("RAD[2]")).net); - ramw->connectPorts(id_WADO0, lc, id_WAD0); - ramw->connectPorts(id_WADO1, lc, id_WAD1); - ramw->connectPorts(id_WADO2, lc, id_WAD2); - ramw->connectPorts(id_WADO3, lc, id_WAD3); + if (ram->ports.count(ctx->id("RAD[3]"))) + comb->connectPort(id_A, ram->ports.at(ctx->id("RAD[3]")).net); - if (index == 0) { - ramw->connectPorts(id_WDO0, lc, id_WD0); - ramw->connectPorts(id_WDO1, lc, id_WD1); + if (ram->ports.count(id_WRE)) + comb->connectPort(id_WRE, ram->ports.at(id_WRE).net); + if (ram->ports.count(id_WCK)) + comb->connectPort(id_WCK, ram->ports.at(id_WCK).net); - ram->movePortTo(ctx->id("DO[0]"), lc, id_F0); - ram->movePortTo(ctx->id("DO[1]"), lc, id_F1); + ramw->connectPorts(id_WADO0, comb, id_WAD0); + ramw->connectPorts(id_WADO1, comb, id_WAD1); + ramw->connectPorts(id_WADO2, comb, id_WAD2); + ramw->connectPorts(id_WADO3, comb, id_WAD3); - } else if (index == 1) { - ramw->connectPorts(id_WDO2, lc, id_WD0); - ramw->connectPorts(id_WDO3, lc, id_WD1); + NPNR_ASSERT(index < 4); + std::string ii = std::to_string(index); + ramw->connectPorts(ctx->id("WDO" + ii), comb, id_WD); + ram->movePortTo(ctx->id("DO[" + ii + "]"), comb, id_F); - ram->movePortTo(ctx->id("DO[2]"), lc, id_F0); - ram->movePortTo(ctx->id("DO[3]"), lc, id_F1); - } else { - NPNR_ASSERT_FALSE("bad DPRAM index"); - } + for (auto &attr : ram->attrs) + comb->attrs[attr.first] = attr.second; } void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector> &created_cells, diff --git a/ecp5/cells.h b/ecp5/cells.h index 8f0a8cbf70..185b19ced3 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -37,8 +37,6 @@ inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; } -inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_SLICE; } - inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; } inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; } @@ -62,11 +60,10 @@ inline bool is_iologic_output_cell(const BaseCtx *ctx, const CellInfo *cell) (str_or_default(cell->attrs, id_ioff_dir, "") != "input")); } -void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut); -void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index); -void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc); -void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc); -void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index); +void lut_to_comb(Context *ctx, CellInfo *lut); +void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw); +void ccu2_to_comb(Context *ctx, CellInfo *ccu, CellInfo *comb, NetInfo *internal_carry, int i); +void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, int index); // Convert a nextpnr IO buffer to a TRELLIS_IO void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector> &created_cells, diff --git a/ecp5/constids.inc b/ecp5/constids.inc index 760e162305..c161f94f98 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -1851,3 +1851,13 @@ X(placer) X(route) X(router) X(syn_useioff) + +X(TRELLIS_COMB) +X(TRELLIS_RAMW) +X(TRELLIS_COMB_CARRY0) +X(TRELLIS_COMB_CARRY1) + +X(WD) +X(OFX) +X(F) +X(CCU2_INJECT1) diff --git a/ecp5/gfx.cc b/ecp5/gfx.cc index 8b5015f866..fe206de02b 100644 --- a/ecp5/gfx.cc +++ b/ecp5/gfx.cc @@ -23,11 +23,13 @@ NEXTPNR_NAMESPACE_BEGIN const float slice_x1 = 0.92; +const float slice_x2_comb = 0.927; +const float slice_x1_ff = 0.933; const float slice_x2 = 0.94; const float slice_x2_wide = 0.97; const float slice_y1 = 0.71; -const float slice_y2 = 0.745 + 0.0068; -const float slice_pitch = 0.0374 + 0.0068; +const float slice_y2 = 0.7275 + 0.0068 / 2; +const float slice_pitch = (0.0374 + 0.0068) / 2; const float io_cell_v_x1 = 0.76; const float io_cell_v_x2 = 0.95; @@ -54,11 +56,25 @@ void gfxTileBel(std::vector &g, int x, int y, int z, int w, int GraphicElement el; el.type = GraphicElement::TYPE_BOX; el.style = style; - if (bel_type == id_TRELLIS_SLICE) { + if (bel_type == id_TRELLIS_COMB) { el.x1 = x + slice_x1; + el.x2 = x + slice_x2_comb; + el.y1 = y + slice_y1 + (z >> Arch::lc_idx_shift) * slice_pitch; + el.y2 = y + slice_y2 + (z >> Arch::lc_idx_shift) * slice_pitch; + g.push_back(el); + + el.style = GraphicElement::STYLE_FRAME; + el.x1 = x + slice_x2_comb + 15 * wire_distance; + el.x2 = el.x1 + wire_distance; + el.y1 = y + slice_y2 - wire_distance * (TILE_WIRE_CLK3_SLICE - TILE_WIRE_DUMMY_D2 + 5 + (3 - z) * 26) + + 3 * slice_pitch - 0.0007f; + el.y2 = el.y1 + wire_distance * 5; + g.push_back(el); + } else if (bel_type == id_TRELLIS_FF) { + el.x1 = x + slice_x1_ff; el.x2 = x + slice_x2; - el.y1 = y + slice_y1 + (z)*slice_pitch; - el.y2 = y + slice_y2 + (z)*slice_pitch; + el.y1 = y + slice_y1 + (z >> Arch::lc_idx_shift) * slice_pitch; + el.y2 = y + slice_y2 + (z >> Arch::lc_idx_shift) * slice_pitch; g.push_back(el); el.style = GraphicElement::STYLE_FRAME; diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 71188aa0fb..7123705a63 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -53,7 +53,9 @@ class Ecp5GlobalRouter private: bool is_clock_port(const PortRef &user) { - if (user.cell->type == id_TRELLIS_SLICE && (user.port == id_CLK || user.port == id_WCK)) + if (user.cell->type == id_TRELLIS_FF && user.port == id_CLK) + return true; + if (user.cell->type == id_TRELLIS_COMB && user.port == id_WCK) return true; if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK || user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK)) @@ -65,7 +67,9 @@ class Ecp5GlobalRouter bool is_logic_port(const PortRef &user) { - if (user.cell->type == id_TRELLIS_SLICE && user.port != id_CLK && user.port != id_WCK) + if (user.cell->type == id_TRELLIS_FF && user.port != id_CLK) + return true; + if (user.cell->type == id_TRELLIS_COMB && user.port != id_WCK) return true; return false; } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index f65e992ee2..7aa9b4c4b0 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -56,22 +56,21 @@ class Ecp5Packer } // Print logic usage - int available_slices = 0; void print_logic_usage() { int total_luts = 0, total_ffs = 0; int total_ramluts = 0, total_ramwluts = 0; for (auto bel : ctx->getBels()) { - if (ctx->getBelType(bel) == id_TRELLIS_SLICE) { - available_slices += 1; - total_luts += 2; - total_ffs += 2; + if (ctx->getBelType(bel) == id_TRELLIS_COMB) { + total_luts += 1; Loc l = ctx->getBelLocation(bel); - if (l.z == 0 || l.z == 1) - total_ramluts += 2; - if (l.z == 2) - total_ramwluts += 2; + if (l.z <= 3) + total_ramluts += 1; } + if (ctx->getBelType(bel) == id_TRELLIS_FF) + total_ffs += 1; + if (ctx->getBelType(bel) == id_TRELLIS_RAMW) + total_ramwluts += 2; } int used_lgluts = 0, used_cyluts = 0, used_ramluts = 0, used_ramwluts = 0, used_ffs = 0; for (auto &cell : ctx->cells) { @@ -101,292 +100,164 @@ class Ecp5Packer log_break(); } - // Find FFs associated with LUTs, or LUT expansion muxes - void find_lutff_pairs() + // Pack LUTs + void pack_luts() { - log_info("Finding LUTFF pairs...\n"); + log_info("Packing LUTs...\n"); for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (is_lut(ctx, ci) || is_pfumx(ctx, ci) || is_l6mux(ctx, ci)) { - NetInfo *znet = ci->ports.at(id_Z).net; - if (znet != nullptr) { - CellInfo *ff = net_only_drives(ctx, znet, is_ff, id_DI, false); - // Can't combine preload FF with LUT due to conflict on M - if (ff != nullptr && ff->getPort(id_M) == nullptr) { - lutffPairs[ci->name] = ff->name; - fflutPairs[ff->name] = ci->name; - } - } - } - } - } - - // Check if a flipflop is available in a slice - bool is_ff_available(CellInfo *slice, int ff) - { - if (slice->getPort((ff == 1) ? id_Q1 : id_Q0) != nullptr) - return false; - if (slice->getPort((ff == 1) ? id_M1 : id_M0) != nullptr) - return false; - return true; - } - - // Check if a flipflop can be added to a slice - bool can_add_ff_to_slice(CellInfo *slice, CellInfo *ff) - { - std::string clkmux = str_or_default(ff->params, id_CLKMUX, "CLK"); - std::string lsrmux = str_or_default(ff->params, id_LSRMUX, "LSR"); - - bool has_dpram = str_or_default(slice->params, id_MODE, "LOGIC") == "DPRAM"; - if (has_dpram) { - std::string wckmux = str_or_default(slice->params, id_WCKMUX, "WCK"); - std::string wremux = str_or_default(slice->params, id_WREMUX, "WRE"); - if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK")) - return false; - if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR")) - return false; + if (is_lut(ctx, ci)) + lut_to_comb(ctx, ci); } - bool has_ff0 = slice->getPort(id_Q0) != nullptr; - bool has_ff1 = slice->getPort(id_Q1) != nullptr; - if (!has_ff0 && !has_ff1) - return true; - if (str_or_default(ff->params, id_GSR, "DISABLED") != str_or_default(slice->params, id_GSR, "DISABLED")) - return false; - if (str_or_default(ff->params, id_SRMODE, "LSR_OVER_CE") != - str_or_default(slice->params, id_SRMODE, "LSR_OVER_CE")) - return false; - if (str_or_default(ff->params, id_CEMUX, "1") != str_or_default(slice->params, id_CEMUX, "1")) - return false; - if (str_or_default(ff->params, id_LSRMUX, "LSR") != str_or_default(slice->params, id_LSRMUX, "LSR")) - return false; - if (str_or_default(ff->params, id_CLKMUX, "CLK") != str_or_default(slice->params, id_CLKMUX, "CLK")) - return false; - if (net_or_nullptr(ff, id_CLK) != net_or_nullptr(slice, id_CLK)) - return false; - if (net_or_nullptr(ff, id_CE) != net_or_nullptr(slice, id_CE)) - return false; - if (net_or_nullptr(ff, id_LSR) != net_or_nullptr(slice, id_LSR)) - return false; - return true; } - const NetInfo *net_or_nullptr(CellInfo *cell, IdString port) + // Gets the z-position of a cell in a macro + int get_macro_cell_z(const CellInfo *ci) { - auto fnd = cell->ports.find(port); - if (fnd == cell->ports.end()) - return nullptr; + if (ci->constr_abs_z) + return ci->constr_z; + else if (ci->cluster != ClusterId() && ctx->getClusterRootCell(ci->cluster) != ci) + return ci->constr_z + get_macro_cell_z(ctx->getClusterRootCell(ci->cluster)); else - return fnd->second.net; + return 0; } - // Return whether two FFs can be packed together in the same slice - bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1) + // Gets the relative xy-position of a cell in a macro + std::pair get_macro_cell_xy(const CellInfo *ci) { - if (str_or_default(ff0->params, id_GSR, "DISABLED") != str_or_default(ff1->params, id_GSR, "DISABLED")) - return false; - if (str_or_default(ff0->params, id_SRMODE, "LSR_OVER_CE") != - str_or_default(ff1->params, id_SRMODE, "LSR_OVER_CE")) - return false; - if (str_or_default(ff0->params, id_CEMUX, "1") != str_or_default(ff1->params, id_CEMUX, "1")) - return false; - if (str_or_default(ff0->params, id_LSRMUX, "LSR") != str_or_default(ff1->params, id_LSRMUX, "LSR")) - return false; - if (str_or_default(ff0->params, id_CLKMUX, "CLK") != str_or_default(ff1->params, id_CLKMUX, "CLK")) - return false; - if (net_or_nullptr(ff0, id_CLK) != net_or_nullptr(ff1, id_CLK)) - return false; - if (net_or_nullptr(ff0, id_CE) != net_or_nullptr(ff1, id_CE)) - return false; - if (net_or_nullptr(ff0, id_LSR) != net_or_nullptr(ff1, id_LSR)) - return false; - return true; + if (ci->cluster != ClusterId()) + return {ci->constr_x, ci->constr_y}; + else + return {0, 0}; } - // Return whether or not an FF can be added to a tile (pairing checks must also be done using the fn above) - bool can_add_ff_to_tile(const std::vector &tile_ffs, CellInfo *ff0) + // Relatively constrain one cell to another + void rel_constr_cells(CellInfo *a, CellInfo *b, int dz) { - for (const auto &existing : tile_ffs) { - if (net_or_nullptr(existing, id_CLK) != net_or_nullptr(ff0, id_CLK)) - return false; - if (net_or_nullptr(existing, id_LSR) != net_or_nullptr(ff0, id_LSR)) - return false; - if (str_or_default(existing->params, id_CLKMUX, "CLK") != str_or_default(ff0->params, id_CLKMUX, "CLK")) - return false; - if (str_or_default(existing->params, id_LSRMUX, "LSR") != str_or_default(ff0->params, id_LSRMUX, "LSR")) - return false; - if (str_or_default(existing->params, id_SRMODE, "LSR_OVER_CE") != - str_or_default(ff0->params, id_SRMODE, "LSR_OVER_CE")) - return false; + if (a->cluster != ClusterId() && ctx->getClusterRootCell(a->cluster) != a) { + NPNR_ASSERT(b->cluster == ClusterId()); + NPNR_ASSERT(b->constr_children.empty()); + CellInfo *root = ctx->getClusterRootCell(a->cluster); + root->constr_children.push_back(b); + b->cluster = root->cluster; + b->constr_x = a->constr_x; + b->constr_y = a->constr_y; + b->constr_z = get_macro_cell_z(a) + dz; + b->constr_abs_z = a->constr_abs_z; + } else if (b->cluster != ClusterId() && ctx->getClusterRootCell(b->cluster) != b) { + NPNR_ASSERT(a->constr_children.empty()); + CellInfo *root = ctx->getClusterRootCell(b->cluster); + root->constr_children.push_back(a); + a->cluster = root->cluster; + a->constr_x = b->constr_x; + a->constr_y = b->constr_y; + a->constr_z = get_macro_cell_z(b) - dz; + a->constr_abs_z = b->constr_abs_z; + } else if (!b->constr_children.empty()) { + NPNR_ASSERT(a->constr_children.empty()); + b->constr_children.push_back(a); + a->cluster = b->cluster; + a->constr_x = 0; + a->constr_y = 0; + a->constr_z = get_macro_cell_z(b) - dz; + a->constr_abs_z = b->constr_abs_z; + } else { + NPNR_ASSERT(a->cluster == ClusterId() || ctx->getClusterRootCell(a->cluster) == a); + a->constr_children.push_back(b); + a->cluster = a->name; + b->cluster = a->name; + b->constr_x = 0; + b->constr_y = 0; + b->constr_z = get_macro_cell_z(a) + dz; + b->constr_abs_z = a->constr_abs_z; } - return true; } - // Return true if a FF can be added to a DPRAM slice - bool can_pack_ff_dram(CellInfo *dpram, CellInfo *ff) + // Check if it is legal to add a FF to a macro + // This reuses the tile validity code + bool can_add_flipflop_to_macro(CellInfo *comb, CellInfo *ff) { - if (ff->getPort(id_M) != nullptr) - return false; // skip PRLD FFs due to M/DI conflict - std::string wckmux = str_or_default(dpram->params, id_WCKMUX, "WCK"); - std::string clkmux = str_or_default(ff->params, id_CLKMUX, "CLK"); - if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK")) - return false; - std::string wremux = str_or_default(dpram->params, id_WREMUX, "WRE"); - std::string lsrmux = str_or_default(ff->params, id_LSRMUX, "LSR"); - if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR")) - return false; - return true; - } + Arch::LogicTileStatus lts; + std::fill(lts.cells.begin(), lts.cells.end(), nullptr); + lts.tile_dirty = true; + for (auto &sl : lts.slices) + sl.dirty = true; + + auto process_cell = [&](CellInfo *ci) { + if (get_macro_cell_xy(ci) != get_macro_cell_xy(comb)) + return; + int z = get_macro_cell_z(ci); + auto &slot = lts.cells.at(z); + NPNR_ASSERT(slot == nullptr); + slot = ci; + // Make sure fields needed for validity checking are set correctly + ctx->assign_arch_info_for_cell(ci); + }; - // Return true if two LUTs can be paired considering FF compatibility - bool can_pack_lutff(IdString lut0, IdString lut1) - { - auto ff0 = lutffPairs.find(lut0), ff1 = lutffPairs.find(lut1); - if (ff0 != lutffPairs.end() && ff1 != lutffPairs.end()) { - return can_pack_ffs(ctx->cells.at(ff0->second).get(), ctx->cells.at(ff1->second).get()); + if (comb->cluster != ClusterId()) { + CellInfo *root = ctx->getClusterRootCell(comb->cluster); + process_cell(root); + for (auto &ch : root->constr_children) + process_cell(ch); } else { - return true; + process_cell(comb); + for (auto &ch : comb->constr_children) + process_cell(ch); } + int ff_z = get_macro_cell_z(comb) + (Arch::BEL_FF - Arch::BEL_COMB); + if (lts.cells.at(ff_z) != nullptr) + return false; + ctx->assign_arch_info_for_cell(ff); + lts.cells.at(ff_z) = ff; + return ctx->slices_compatible(<s); } - // Find "closely connected" LUTs and pair them together - void pair_luts() + void pack_ffs() { - log_info("Finding LUT-LUT pairs...\n"); - pool procdLuts; + log_info("Packing FFs...\n"); + int pairs = 0; for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (is_lut(ctx, ci) && procdLuts.find(cell.first) == procdLuts.end()) { - NetInfo *znet = ci->ports.at(id_Z).net; - std::vector inpnets; - if (znet != nullptr) { - for (auto user : znet->users) { - if (is_lut(ctx, user.cell) && user.cell != ci && - procdLuts.find(user.cell->name) == procdLuts.end()) { - if (can_pack_lutff(ci->name, user.cell->name)) { - procdLuts.insert(ci->name); - procdLuts.insert(user.cell->name); - lutPairs[ci->name] = user.cell->name; - goto paired; - } - } - } - if (false) { - paired: - continue; - } - } - if (lutffPairs.find(ci->name) != lutffPairs.end()) { - NetInfo *qnet = ctx->cells.at(lutffPairs[ci->name])->ports.at(id_Q).net; - if (qnet != nullptr) { - for (auto user : qnet->users) { - if (is_lut(ctx, user.cell) && user.cell != ci && - procdLuts.find(user.cell->name) == procdLuts.end()) { - if (can_pack_lutff(ci->name, user.cell->name)) { - procdLuts.insert(ci->name); - procdLuts.insert(user.cell->name); - lutPairs[ci->name] = user.cell->name; - goto paired_ff; - } - } - } - if (false) { - paired_ff: + if (is_ff(ctx, ci)) { + NetInfo *di = get_net_or_empty(ci, id_DI); + if (di->driver.cell != nullptr && di->driver.cell->type == id_TRELLIS_COMB && di->driver.port == id_F) { + CellInfo *comb = di->driver.cell; + if (comb->cluster != ClusterId()) { + // Special procedure where the comb cell is part of an existing macro + // Need to make sure that CLK, CE, SR, etc are shared correctly, or + // the design will not be routeable + if (can_add_flipflop_to_macro(comb, ci)) { + ci->params[id_SD] = std::string("1"); + rel_constr_cells(comb, ci, (Arch::BEL_FF - Arch::BEL_COMB)); + // Packed successfully + ++pairs; continue; } - } - } - for (const char *inp : {"A", "B", "C", "D"}) { - if (!ci->ports.count(ctx->id(inp))) - continue; - NetInfo *innet = ci->ports.at(ctx->id(inp)).net; - if (innet != nullptr && innet->driver.cell != nullptr) { - CellInfo *drv = innet->driver.cell; - if (is_lut(ctx, drv) && drv != ci && innet->driver.port == id_Z) { - if (procdLuts.find(drv->name) == procdLuts.end()) { - if (can_pack_lutff(ci->name, drv->name)) { - procdLuts.insert(ci->name); - procdLuts.insert(drv->name); - lutPairs[ci->name] = drv->name; - goto paired_inlut; - } - } - } else if (is_ff(ctx, drv) && innet->driver.port == id_Q) { - auto fflut = fflutPairs.find(drv->name); - if (fflut != fflutPairs.end() && fflut->second != ci->name && - procdLuts.find(fflut->second) == procdLuts.end()) { - if (can_pack_lutff(ci->name, fflut->second)) { - procdLuts.insert(ci->name); - procdLuts.insert(fflut->second); - lutPairs[ci->name] = fflut->second; - goto paired_inlut; - } - } - } - } - } - - // Pack LUTs feeding the same CCU2, RAM or DFF into a SLICE - if (znet != nullptr && znet->users.entries() < 10) { - for (auto user : znet->users) { - if (is_lc(ctx, user.cell) || user.cell->type == id_DP16KD || is_ff(ctx, user.cell)) { - for (auto port : user.cell->ports) { - if (port.second.type != PORT_IN || port.second.net == nullptr || - port.second.net == znet) - continue; - if (port.second.net->users.entries() > 10) - continue; - CellInfo *drv = port.second.net->driver.cell; - if (drv == nullptr) - continue; - if (is_lut(ctx, drv) && !procdLuts.count(drv->name) && - can_pack_lutff(ci->name, drv->name)) { - procdLuts.insert(ci->name); - procdLuts.insert(drv->name); - lutPairs[ci->name] = drv->name; - goto paired_inlut; - } - } - } - } - } - - // Pack LUTs sharing an input with a simple fanout-based heuristic - for (const char *inp : {"A", "B", "C", "D"}) { - if (!ci->ports.count(ctx->id(inp))) + } else { + // LUT/COMB is not part of a macro, this is the easy case + // Constrain FF and LUT together, no need to rewire + ci->params[id_SD] = std::string("1"); + comb->constr_children.push_back(ci); + ci->cluster = comb->name; + comb->cluster = comb->name; + ci->constr_x = 0; + ci->constr_y = 0; + ci->constr_z = (Arch::BEL_FF - Arch::BEL_COMB); + ci->constr_abs_z = false; + // Packed successfully + ++pairs; continue; - NetInfo *innet = ci->ports.at(ctx->id(inp)).net; - if (innet != nullptr && innet->users.entries() < 5 && innet->users.entries() > 1) - inpnets.push_back(innet); - } - std::sort(inpnets.begin(), inpnets.end(), - [&](const NetInfo *a, const NetInfo *b) { return a->users.entries() < b->users.entries(); }); - for (auto inet : inpnets) { - for (auto &user : inet->users) { - if (user.cell == nullptr || user.cell == ci || !is_lut(ctx, user.cell)) - continue; - if (procdLuts.count(user.cell->name)) - continue; - if (can_pack_lutff(ci->name, user.cell->name)) { - procdLuts.insert(ci->name); - procdLuts.insert(user.cell->name); - lutPairs[ci->name] = user.cell->name; - goto paired_inlut; - } } } - - if (false) { - paired_inlut: - continue; + { + // Didn't manage to pack it with a driving combinational cell + // Rewire to use general routing + ci->params[id_SD] = std::string("0"); + ci->renamePort(id_DI, id_M); } } } - if (ctx->debug) { - log_info("Singleton LUTs (packer QoR debug): \n"); - for (auto &cell : ctx->cells) - if (is_lut(ctx, cell.second.get()) && !procdLuts.count(cell.first)) - log_info(" %s\n", cell.first.c_str(ctx)); - } + log_info(" %d FFs paired with LUTs.\n", pairs); } // Return true if an port is a top level port that provides its own IOBUF @@ -429,6 +300,192 @@ class Ecp5Packer return false; } + // Pass to pack LUT5s into a newly created slice + void pack_lut5xs() + { + log_info("Packing LUT5-7s...\n"); + + // Gets the "COMB1" side of a LUT5, where we pack a LUT[67] into + auto get_comb1_from_lut5 = [&](CellInfo *lut5) { + NetInfo *f1 = get_net_or_empty(lut5, id_F1); + NPNR_ASSERT(f1 != nullptr); + NPNR_ASSERT(f1->driver.cell != nullptr); + return f1->driver.cell; + }; + + dict> lut5_roots, lut6_roots, lut7_roots; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_pfumx(ctx, ci)) { + NetInfo *f0 = ci->ports.at(id_BLUT).net; + + if (f0 == nullptr) + log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx)); + NetInfo *f1 = ci->ports.at(id_ALUT).net; + if (f1 == nullptr) + log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx)); + + CellInfo *lut0 = + (f0->driver.cell && f0->driver.cell->type == id_TRELLIS_COMB && f0->driver.port == id_F) + ? f0->driver.cell + : nullptr; + CellInfo *lut1 = + (f1->driver.cell && f1->driver.cell->type == id_TRELLIS_COMB && f1->driver.port == id_F) + ? f1->driver.cell + : nullptr; + if (lut0 == nullptr || lut0->cluster != ClusterId()) + log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); + if (lut1 == nullptr || lut1->cluster != ClusterId()) + log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); + lut0->addInput(id_F1); + lut0->addInput(id_M); + lut0->addOutput(id_OFX); + + ci->movePortTo(id_Z, lut0, id_OFX); + ci->movePortTo(id_ALUT, lut0, id_F1); + ci->movePortTo(id_C0, lut0, id_M); + ci->disconnectPort(id_BLUT); + + lut5_roots[lut0->name] = {lut0, lut1}; + packed_cells.insert(ci->name); + } + } + flush_cells(); + // Pack LUT6s + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_l6mux(ctx, ci)) { + NetInfo *ofx0_0 = ci->ports.at(id_D0).net; + if (ofx0_0 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); + NetInfo *ofx0_1 = ci->ports.at(id_D1).net; + if (ofx0_1 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); + CellInfo *comb0 = (ofx0_0->driver.cell && ofx0_0->driver.cell->type == id_TRELLIS_COMB && + ofx0_0->driver.port == id_OFX) + ? ofx0_0->driver.cell + : nullptr; + CellInfo *comb1 = (ofx0_1->driver.cell && ofx0_1->driver.cell->type == id_TRELLIS_COMB && + ofx0_1->driver.port == id_OFX) + ? ofx0_1->driver.cell + : nullptr; + if (comb0 == nullptr) { + if (!net_driven_by(ctx, ofx0_0, is_l6mux, id_Z)) + log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux " + "('%s.%s')\n", + ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), + ofx0_0->driver.port.c_str(ctx)); + continue; + } + if (lut6_roots.count(comb0->name)) + continue; + + if (comb1 == nullptr) { + if (!net_driven_by(ctx, ofx0_1, is_l6mux, id_Z)) + log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux " + "('%s.%s')\n", + ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), + ofx0_0->driver.port.c_str(ctx)); + continue; + } + if (lut6_roots.count(comb1->name)) + continue; + if (ctx->verbose) + log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx)); + comb0 = get_comb1_from_lut5(comb0); + comb1 = get_comb1_from_lut5(comb1); + + comb1->addInput(id_FXA); + comb1->addInput(id_FXB); + comb1->addInput(id_M); + comb1->addOutput(id_OFX); + ci->movePortTo(id_D0, comb1, id_FXA); + ci->movePortTo(id_D1, comb1, id_FXB); + ci->movePortTo(id_SD, comb1, id_M); + ci->movePortTo(id_Z, comb1, id_OFX); + lut6_roots[comb1->name] = {comb0, comb1}; + packed_cells.insert(ci->name); + } + } + flush_cells(); + // Pack LUT7s + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_l6mux(ctx, ci)) { + NetInfo *ofx1_0 = ci->ports.at(id_D0).net; + if (ofx1_0 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); + NetInfo *ofx1_1 = ci->ports.at(id_D1).net; + if (ofx1_1 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); + CellInfo *comb1 = (ofx1_0->driver.cell && ofx1_0->driver.cell->type == id_TRELLIS_COMB && + ofx1_0->driver.port == id_OFX) + ? ofx1_0->driver.cell + : nullptr; + CellInfo *comb3 = (ofx1_1->driver.cell && ofx1_1->driver.cell->type == id_TRELLIS_COMB && + ofx1_1->driver.port == id_OFX) + ? ofx1_1->driver.cell + : nullptr; + if (comb1 == nullptr) + log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n", + ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx), + ofx1_0->driver.port.c_str(ctx)); + if (comb3 == nullptr) + log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n", + ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx), + ofx1_1->driver.port.c_str(ctx)); + + NetInfo *fxa_0 = comb1->ports.at(id_FXA).net; + if (fxa_0 == nullptr) + log_error("SLICE '%s' has disconnected port 'FXA'\n", comb1->name.c_str(ctx)); + NetInfo *fxa_1 = comb3->ports.at(id_FXA).net; + if (fxa_1 == nullptr) + log_error("SLICE '%s' has disconnected port 'FXA'\n", comb3->name.c_str(ctx)); + + CellInfo *comb2 = net_driven_by( + ctx, fxa_1, + [](const Context *ctx, const CellInfo *ci) { + (void)ctx; + return ci->type == id_TRELLIS_COMB; + }, + id_OFX); + if (comb2 == nullptr) + log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n", + comb3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx), + fxa_1->driver.port.c_str(ctx)); + comb2 = get_comb1_from_lut5(comb2); + comb2->addInput(id_FXA); + comb2->addInput(id_FXB); + comb2->addInput(id_M); + comb2->addOutput(id_OFX); + ci->movePortTo(id_D0, comb2, id_FXA); + ci->movePortTo(id_D1, comb2, id_FXB); + ci->movePortTo(id_SD, comb2, id_M); + ci->movePortTo(id_Z, comb2, id_OFX); + + lut7_roots[comb2->name] = {comb1, comb3}; + packed_cells.insert(ci->name); + } + } + + for (auto &root : lut7_roots) { + auto &cells = root.second; + cells.second->cluster = cells.second->name; + cells.second->constr_abs_z = true; + cells.second->constr_z = (1 << Arch::lc_idx_shift) | Arch::BEL_COMB; + rel_constr_cells(cells.second, cells.first, (4 << Arch::lc_idx_shift)); + } + for (auto &root : lut6_roots) { + auto &cells = root.second; + rel_constr_cells(cells.second, cells.first, (2 << Arch::lc_idx_shift)); + } + for (auto &root : lut5_roots) { + auto &cells = root.second; + rel_constr_cells(cells.first, cells.second, (1 << Arch::lc_idx_shift)); + } + flush_cells(); + } + // Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated void pack_io() { @@ -523,215 +580,6 @@ class Ecp5Packer flush_cells(); } - // Pass to pack LUT5s into a newly created slice - void pack_lut5xs() - { - log_info("Packing LUT5-7s...\n"); - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - if (is_pfumx(ctx, ci)) { - std::unique_ptr packed = - create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "_SLICE"); - NetInfo *f0 = ci->ports.at(id_BLUT).net; - if (f0 == nullptr) - log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx)); - NetInfo *f1 = ci->ports.at(id_ALUT).net; - if (f1 == nullptr) - log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx)); - CellInfo *lut0 = net_driven_by(ctx, f0, is_lut, id_Z); - CellInfo *lut1 = net_driven_by(ctx, f1, is_lut, id_Z); - if (lut0 == nullptr) - log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); - if (lut1 == nullptr) - log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); - if (ctx->verbose) - log_info(" mux '%s' forms part of a LUT5\n", cell.first.c_str(ctx)); - lut0->movePortTo(id_A, packed.get(), id_A0); - lut0->movePortTo(id_B, packed.get(), id_B0); - lut0->movePortTo(id_C, packed.get(), id_C0); - lut0->movePortTo(id_D, packed.get(), id_D0); - lut1->movePortTo(id_A, packed.get(), id_A1); - lut1->movePortTo(id_B, packed.get(), id_B1); - lut1->movePortTo(id_C, packed.get(), id_C1); - lut1->movePortTo(id_D, packed.get(), id_D1); - ci->movePortTo(id_C0, packed.get(), id_M0); - ci->movePortTo(id_Z, packed.get(), id_OFX0); - packed->params[id_LUT0_INITVAL] = get_or_default(lut0->params, id_INIT, Property(0, 16)); - packed->params[id_LUT1_INITVAL] = get_or_default(lut1->params, id_INIT, Property(0, 16)); - - ctx->nets.erase(f0->name); - ctx->nets.erase(f1->name); - sliceUsage[packed->name].lut0_used = true; - sliceUsage[packed->name].lut1_used = true; - sliceUsage[packed->name].mux5_used = true; - - if (lutffPairs.find(ci->name) != lutffPairs.end()) { - CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get(); - ff_to_slice(ctx, ff, packed.get(), 0, true); - packed_cells.insert(ff->name); - sliceUsage[packed->name].ff0_used = true; - lutffPairs.erase(ci->name); - fflutPairs.erase(ff->name); - } - - new_cells.push_back(std::move(packed)); - packed_cells.insert(lut0->name); - packed_cells.insert(lut1->name); - packed_cells.insert(ci->name); - } - } - flush_cells(); - // Pack LUT6s - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - if (is_l6mux(ctx, ci)) { - NetInfo *ofx0_0 = ci->ports.at(id_D0).net; - if (ofx0_0 == nullptr) - log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); - NetInfo *ofx0_1 = ci->ports.at(id_D1).net; - if (ofx0_1 == nullptr) - log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); - CellInfo *slice0 = net_driven_by(ctx, ofx0_0, is_lc, id_OFX0); - CellInfo *slice1 = net_driven_by(ctx, ofx0_1, is_lc, id_OFX0); - if (slice0 == nullptr) { - if (!net_driven_by(ctx, ofx0_0, is_l6mux, id_Z) && !net_driven_by(ctx, ofx0_0, is_lc, id_OFX1)) - log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux " - "('%s.%s')\n", - ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), - ofx0_0->driver.port.c_str(ctx)); - continue; - } - if (slice1 == nullptr) { - if (!net_driven_by(ctx, ofx0_1, is_l6mux, id_Z) && !net_driven_by(ctx, ofx0_1, is_lc, id_OFX1)) - log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux " - "('%s.%s')\n", - ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), - ofx0_0->driver.port.c_str(ctx)); - continue; - } - if (ctx->verbose) - log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx)); - ci->movePortTo(id_D0, slice1, id_FXA); - ci->movePortTo(id_D1, slice1, id_FXB); - ci->movePortTo(id_SD, slice1, id_M1); - ci->movePortTo(id_Z, slice1, id_OFX1); - slice0->constr_z = 1; - slice0->constr_x = 0; - slice0->constr_y = 0; - slice0->cluster = slice1->name; - slice1->constr_z = 0; - slice1->constr_abs_z = true; - slice1->constr_children.push_back(slice0); - slice1->cluster = slice1->name; - - if (lutffPairs.find(ci->name) != lutffPairs.end()) { - CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get(); - ff_to_slice(ctx, ff, slice1, 1, true); - packed_cells.insert(ff->name); - sliceUsage[slice1->name].ff1_used = true; - lutffPairs.erase(ci->name); - fflutPairs.erase(ff->name); - } - - packed_cells.insert(ci->name); - } - } - flush_cells(); - // Pack LUT7s - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - if (is_l6mux(ctx, ci)) { - NetInfo *ofx1_0 = ci->ports.at(id_D0).net; - if (ofx1_0 == nullptr) - log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); - NetInfo *ofx1_1 = ci->ports.at(id_D1).net; - if (ofx1_1 == nullptr) - log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); - CellInfo *slice1 = net_driven_by(ctx, ofx1_0, is_lc, id_OFX1); - CellInfo *slice3 = net_driven_by(ctx, ofx1_1, is_lc, id_OFX1); - if (slice1 == nullptr) - log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n", - ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx), - ofx1_0->driver.port.c_str(ctx)); - if (slice3 == nullptr) - log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n", - ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx), - ofx1_1->driver.port.c_str(ctx)); - - NetInfo *fxa_0 = slice1->ports.at(id_FXA).net; - if (fxa_0 == nullptr) - log_error("SLICE '%s' has disconnected port 'FXA'\n", slice1->name.c_str(ctx)); - NetInfo *fxa_1 = slice3->ports.at(id_FXA).net; - if (fxa_1 == nullptr) - log_error("SLICE '%s' has disconnected port 'FXA'\n", slice3->name.c_str(ctx)); - - CellInfo *slice0 = net_driven_by(ctx, fxa_0, is_lc, id_OFX0); - CellInfo *slice2 = net_driven_by(ctx, fxa_1, is_lc, id_OFX0); - if (slice0 == nullptr) - log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n", - slice1->name.c_str(ctx), fxa_0->driver.cell->name.c_str(ctx), - fxa_0->driver.port.c_str(ctx)); - if (slice2 == nullptr) - log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n", - slice3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx), - fxa_1->driver.port.c_str(ctx)); - - ci->movePortTo(id_D0, slice2, id_FXA); - ci->movePortTo(id_D1, slice2, id_FXB); - ci->movePortTo(id_SD, slice2, id_M1); - ci->movePortTo(id_Z, slice2, id_OFX1); - - for (auto slice : {slice0, slice1, slice2, slice3}) { - slice->constr_children.clear(); - slice->constr_abs_z = false; - slice->constr_x = 0; - slice->constr_y = 0; - slice->constr_z = 0; - slice->cluster = ClusterId(); - } - slice3->constr_children.clear(); - slice3->constr_abs_z = true; - slice3->constr_z = 0; - slice3->cluster = slice3->name; - - slice2->constr_children.clear(); - slice2->constr_abs_z = true; - slice2->constr_z = 1; - slice2->constr_x = 0; - slice2->constr_y = 0; - slice2->cluster = slice3->name; - slice3->constr_children.push_back(slice2); - - slice1->constr_children.clear(); - slice1->constr_abs_z = true; - slice1->constr_z = 2; - slice1->constr_x = 0; - slice1->constr_y = 0; - slice1->cluster = slice3->name; - slice3->constr_children.push_back(slice1); - - slice0->constr_children.clear(); - slice0->constr_abs_z = true; - slice0->constr_z = 3; - slice0->constr_x = 0; - slice0->constr_y = 0; - slice0->cluster = slice3->name; - slice3->constr_children.push_back(slice0); - - if (lutffPairs.find(ci->name) != lutffPairs.end()) { - CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get(); - ff_to_slice(ctx, ff, slice2, 1, true); - packed_cells.insert(ff->name); - sliceUsage[slice2->name].ff1_used = true; - lutffPairs.erase(ci->name); - fflutPairs.erase(ff->name); - } - - packed_cells.insert(ci->name); - } - } - flush_cells(); - } // Create a feed in to the carry chain CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) { @@ -881,58 +729,37 @@ class Ecp5Packer std::vector> ff_packing; for (auto &chain : all_chains) { int cell_count = 0; - std::vector tile_ffs; std::vector packed_chain; for (auto &cell : chain.cells) { - if (cell_count % 4 == 0) - tile_ffs.clear(); - std::unique_ptr slice = - create_ecp5_cell(ctx, id_TRELLIS_SLICE, cell->name.str(ctx) + "$CCU2_SLICE"); - - ccu2c_to_slice(ctx, cell, slice.get()); - - CellInfo *ff0 = nullptr; - NetInfo *f0net = slice->ports.at(id_F0).net; - if (f0net != nullptr) { - ff0 = net_only_drives(ctx, f0net, is_ff, id_DI, false); - if (ff0 != nullptr && can_add_ff_to_tile(tile_ffs, ff0)) { - ff_packing.push_back(std::make_tuple(ff0, slice.get(), 0)); - tile_ffs.push_back(ff0); - packed_cells.insert(ff0->name); - } - } + std::unique_ptr comb0 = + create_ecp5_cell(ctx, id_TRELLIS_COMB, cell->name.str(ctx) + "$CCU2_COMB0"); + std::unique_ptr comb1 = + create_ecp5_cell(ctx, id_TRELLIS_COMB, cell->name.str(ctx) + "$CCU2_COMB1"); + NetInfo *carry_net = ctx->createNet(ctx->id(cell->name.str(ctx) + "$CCU2_FCI_INT")); - CellInfo *ff1 = nullptr; - NetInfo *f1net = slice->ports.at(id_F1).net; - if (f1net != nullptr) { - ff1 = net_only_drives(ctx, f1net, is_ff, id_DI, false); - if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) && - can_add_ff_to_tile(tile_ffs, ff1)) { - ff_packing.push_back(std::make_tuple(ff1, slice.get(), 1)); - tile_ffs.push_back(ff1); - packed_cells.insert(ff1->name); - } - } - packed_chain.push_back(slice.get()); - new_cells.push_back(std::move(slice)); + ccu2_to_comb(ctx, cell, comb0.get(), carry_net, 0); + ccu2_to_comb(ctx, cell, comb1.get(), carry_net, 1); + + packed_chain.push_back(comb0.get()); + packed_chain.push_back(comb1.get()); + + new_cells.push_back(std::move(comb0)); + new_cells.push_back(std::move(comb1)); packed_cells.insert(cell->name); cell_count++; } packed_chains.push_back(packed_chain); } - for (auto ff : ff_packing) - ff_to_slice(ctx, std::get<0>(ff), std::get<1>(ff), std::get<2>(ff), true); - // Relative chain placement for (auto &chain : packed_chains) { chain.at(0)->constr_abs_z = true; chain.at(0)->constr_z = 0; chain.at(0)->cluster = chain.at(0)->name; for (int i = 1; i < int(chain.size()); i++) { - chain.at(i)->constr_x = (i / 4); + chain.at(i)->constr_x = (i / 8); chain.at(i)->constr_y = 0; - chain.at(i)->constr_z = i % 4; + chain.at(i)->constr_z = (i % 8) << ctx->lc_idx_shift | Arch::BEL_COMB; chain.at(i)->constr_abs_z = true; chain.at(i)->cluster = chain.at(0)->name; chain.at(0)->constr_children.push_back(chain.at(i)); @@ -951,83 +778,64 @@ class Ecp5Packer // Create RAMW slice std::unique_ptr ramw_slice = - create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "$RAMW_SLICE"); - dram_to_ramw(ctx, ci, ramw_slice.get()); + create_ecp5_cell(ctx, id_TRELLIS_RAMW, ci->name.str(ctx) + "$RAMW_SLICE"); + dram_to_ramw_split(ctx, ci, ramw_slice.get()); // Create actual RAM slices - std::unique_ptr ram0_slice = - create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "$DPRAM0_SLICE"); - dram_to_ram_slice(ctx, ci, ram0_slice.get(), ramw_slice.get(), 0); - - std::unique_ptr ram1_slice = - create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "$DPRAM1_SLICE"); - dram_to_ram_slice(ctx, ci, ram1_slice.get(), ramw_slice.get(), 1); + std::unique_ptr ram_comb[4]; + for (int i = 0; i < 4; i++) { + ram_comb[i] = create_ecp5_cell(ctx, id_TRELLIS_COMB, + ci->name.str(ctx) + "$DPRAM_COMB" + std::to_string(i)); + dram_to_comb(ctx, ci, ram_comb[i].get(), ramw_slice.get(), i); + } + // Create 'block' SLICEs as a placement hint that these cells are mutually exclusive with the RAMW + std::unique_ptr ramw_block[2]; + for (int i = 0; i < 2; i++) { + ramw_block[i] = create_ecp5_cell(ctx, id_TRELLIS_COMB, + ci->name.str(ctx) + "$RAMW_BLOCK" + std::to_string(i)); + ramw_block[i]->params[id_MODE] = std::string("RAMW_BLOCK"); + } // Disconnect ports of original cell after packing ci->disconnectPort(id_WCK); ci->disconnectPort(id_WRE); - ci->disconnectPort(ctx->id("RAD[0]")); - ci->disconnectPort(ctx->id("RAD[1]")); - ci->disconnectPort(ctx->id("RAD[2]")); - ci->disconnectPort(ctx->id("RAD[3]")); - - // Attempt to pack FFs into RAM slices - std::vector> ff_packing; - std::vector tile_ffs; - for (auto slice : {ram0_slice.get(), ram1_slice.get()}) { - CellInfo *ff0 = nullptr; - NetInfo *f0net = slice->ports.at(id_F0).net; - if (f0net != nullptr) { - ff0 = net_only_drives(ctx, f0net, is_ff, id_DI, false); - if (ff0 != nullptr && can_add_ff_to_tile(tile_ffs, ff0)) { - if (can_pack_ff_dram(slice, ff0)) { - ff_packing.push_back(std::make_tuple(ff0, slice, 0)); - tile_ffs.push_back(ff0); - packed_cells.insert(ff0->name); - } - } - } - - CellInfo *ff1 = nullptr; - NetInfo *f1net = slice->ports.at(id_F1).net; - if (f1net != nullptr) { - ff1 = net_only_drives(ctx, f1net, is_ff, id_DI, false); - if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) && - can_add_ff_to_tile(tile_ffs, ff1)) { - if (can_pack_ff_dram(slice, ff1)) { - ff_packing.push_back(std::make_tuple(ff1, slice, 1)); - tile_ffs.push_back(ff1); - packed_cells.insert(ff1->name); - } - } - } - } - - for (auto ff : ff_packing) - ff_to_slice(ctx, std::get<0>(ff), std::get<1>(ff), std::get<2>(ff), true); + for (int i = 0; i < 4; i++) + ci->disconnectPort(ctx->id(stringf("RAD[%d]", i))); // Setup placement constraints - ram0_slice->constr_abs_z = true; - ram0_slice->constr_z = 0; - ram0_slice->cluster = ram0_slice->name; - - ram1_slice->cluster = ram0_slice->name; - ram1_slice->constr_abs_z = true; - ram1_slice->constr_x = 0; - ram1_slice->constr_y = 0; - ram1_slice->constr_z = 1; - ram0_slice->constr_children.push_back(ram1_slice.get()); - - ramw_slice->cluster = ram0_slice->name; + // Use the 0th bit as an anchor + ram_comb[0]->constr_abs_z = true; + ram_comb[0]->constr_z = Arch::BEL_COMB; + ram_comb[0]->cluster = ram_comb[0]->name; + for (int i = 1; i < 4; i++) { + ram_comb[i]->cluster = ram_comb[0]->name; + ram_comb[i]->constr_abs_z = true; + ram_comb[i]->constr_x = 0; + ram_comb[i]->constr_y = 0; + ram_comb[i]->constr_z = (i << ctx->lc_idx_shift) | Arch::BEL_COMB; + ram_comb[0]->constr_children.push_back(ram_comb[i].get()); + } + for (int i = 0; i < 2; i++) { + ramw_block[i]->cluster = ram_comb[0]->name; + ramw_block[i]->constr_abs_z = true; + ramw_block[i]->constr_x = 0; + ramw_block[i]->constr_y = 0; + ramw_block[i]->constr_z = ((i + 4) << ctx->lc_idx_shift) | Arch::BEL_COMB; + ram_comb[0]->constr_children.push_back(ramw_block[i].get()); + } + + ramw_slice->cluster = ram_comb[0]->name; ramw_slice->constr_abs_z = true; ramw_slice->constr_x = 0; ramw_slice->constr_y = 0; - ramw_slice->constr_z = 2; - ram0_slice->constr_children.push_back(ramw_slice.get()); + ramw_slice->constr_z = (4 << ctx->lc_idx_shift) | Arch::BEL_RAMW; + ram_comb[0]->constr_children.push_back(ramw_slice.get()); - new_cells.push_back(std::move(ram0_slice)); - new_cells.push_back(std::move(ram1_slice)); + for (int i = 0; i < 4; i++) + new_cells.push_back(std::move(ram_comb[i])); + for (int i = 0; i < 2; i++) + new_cells.push_back(std::move(ramw_block[i])); new_cells.push_back(std::move(ramw_slice)); packed_cells.insert(ci->name); } @@ -1035,183 +843,6 @@ class Ecp5Packer flush_cells(); } - // Pack LUTs that have been paired together - void pack_lut_pairs() - { - log_info("Packing paired LUTs into a SLICE...\n"); - for (auto pair : lutPairs) { - CellInfo *lut0 = ctx->cells.at(pair.first).get(); - CellInfo *lut1 = ctx->cells.at(pair.second).get(); - std::unique_ptr slice = create_ecp5_cell(ctx, id_TRELLIS_SLICE, lut0->name.str(ctx) + "_SLICE"); - - lut_to_slice(ctx, lut0, slice.get(), 0); - lut_to_slice(ctx, lut1, slice.get(), 1); - - auto ff0 = lutffPairs.find(lut0->name); - - if (ff0 != lutffPairs.end()) { - ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true); - packed_cells.insert(ff0->second); - fflutPairs.erase(ff0->second); - lutffPairs.erase(lut0->name); - } - - auto ff1 = lutffPairs.find(lut1->name); - - if (ff1 != lutffPairs.end()) { - ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true); - packed_cells.insert(ff1->second); - fflutPairs.erase(ff1->second); - lutffPairs.erase(lut1->name); - } - - new_cells.push_back(std::move(slice)); - packed_cells.insert(lut0->name); - packed_cells.insert(lut1->name); - } - flush_cells(); - } - - // Pack single LUTs that weren't paired into their own slice, - // with an optional FF also - void pack_remaining_luts() - { - log_info("Packing unpaired LUTs into a SLICE...\n"); - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - if (is_lut(ctx, ci)) { - std::unique_ptr slice = create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "_SLICE"); - lut_to_slice(ctx, ci, slice.get(), 1); - auto ff = lutffPairs.find(ci->name); - - if (ff != lutffPairs.end()) { - ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 1, true); - packed_cells.insert(ff->second); - fflutPairs.erase(ff->second); - lutffPairs.erase(ci->name); - } - - new_cells.push_back(std::move(slice)); - packed_cells.insert(ci->name); - } - } - flush_cells(); - } - - // Find a cell that meets some criteria near an origin cell - // Used for packing an FF into a nearby SLICE - template CellInfo *find_nearby_cell(CellInfo *origin, TFunc Func) - { - pool visited_cells; - std::queue to_visit; - visited_cells.insert(origin); - to_visit.push(origin); - int iter = 0; - while (!to_visit.empty() && iter < 10000) { - CellInfo *cursor = to_visit.front(); - to_visit.pop(); - if (Func(cursor)) - return cursor; - for (const auto &port : cursor->ports) { - NetInfo *pn = port.second.net; - if (pn == nullptr) - continue; - // Skip high-fanout nets that are unlikely to be relevant - if (pn->users.entries() > 25) - continue; - // Add other ports on this net if not already visited - auto visit_port = [&](const PortRef &port) { - if (port.cell == nullptr) - return; - if (visited_cells.count(port.cell)) - return; - // If not already visited; add the cell of this port to the queue - to_visit.push(port.cell); - visited_cells.insert(port.cell); - }; - visit_port(pn->driver); - for (const auto &usr : pn->users) - visit_port(usr); - } - ++iter; - } - return nullptr; - } - - // Pack flipflops that weren't paired with a LUT - float dense_pack_mode_thresh = 0.95f; - void pack_remaining_ffs() - { - // Enter dense flipflop packing mode once utilisation exceeds a threshold (default: 95%) - int used_slices = 0; - for (auto &cell : ctx->cells) - if (cell.second->type == id_TRELLIS_SLICE) - ++used_slices; - - log_info("Packing unpaired FFs into a SLICE...\n"); - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - if (is_ff(ctx, ci)) { - bool pack_dense = used_slices > (dense_pack_mode_thresh * available_slices); - bool requires_m = ci->getPort(id_M) != nullptr; - if (pack_dense && !requires_m) { - // If dense packing threshold exceeded; always try and pack the FF into an existing slice - // Find a SLICE with space "near" the flipflop in the netlist - std::vector ltile; - CellInfo *target = find_nearby_cell(ci, [&](CellInfo *cursor) { - if (cursor->type != id_TRELLIS_SLICE) - return false; - if (cursor->cluster != ClusterId()) { - auto &constr_children = ctx->cells.at(cursor->cluster)->constr_children; - // Skip big chains for performance - if (constr_children.size() > 8) - return false; - // Have to check the whole of the tile for legality when dealing with chains, not just slice - ltile.clear(); - if (cursor->cluster != cursor->name) - ltile.push_back(ctx->cells.at(cursor->cluster).get()); - else - ltile.push_back(cursor); - for (auto c : constr_children) - ltile.push_back(c); - if (!can_add_ff_to_tile(ltile, cursor)) - return false; - } - if (!can_add_ff_to_slice(cursor, ci)) - return false; - for (int i = 0; i < 2; i++) - if (is_ff_available(cursor, i)) - return true; - return false; - }); - - // If found, add the FF to this slice instead of creating a new one - if (target != nullptr) { - for (int i = 0; i < 2; i++) { - if (is_ff_available(target, i)) { - ff_to_slice(ctx, ci, target, i, false); - goto ff_packed; - } - } - } - - if (false) { - ff_packed: - packed_cells.insert(ci->name); - continue; - } - } - - std::unique_ptr slice = create_ecp5_cell(ctx, id_TRELLIS_SLICE, ci->name.str(ctx) + "_SLICE"); - ff_to_slice(ctx, ci, slice.get(), 0, false); - new_cells.push_back(std::move(slice)); - ++used_slices; - packed_cells.insert(ci->name); - } - } - flush_cells(); - } - int make_init_with_const_input(int init, int input, bool value) { int new_init = 0; @@ -1729,7 +1360,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_EXTREFB) { - const NetInfo *refo = net_or_nullptr(ci, id_REFCLKO); + const NetInfo *refo = ci->getPort(id_REFCLKO); CellInfo *dcu = nullptr; std::string loc_bel = std::string("NONE"); std::string dcu_bel = std::string("NONE"); @@ -1785,7 +1416,7 @@ class Ecp5Packer ci->attrs[id_BEL] = dcu_bel; } } else if (ci->type == id_PCSCLKDIV) { - const NetInfo *clki = net_or_nullptr(ci, id_CLKI); + const NetInfo *clki = ci->getPort(id_CLKI); if (clki != nullptr && clki->driver.cell != nullptr && clki->driver.cell->type == id_DCUA) { CellInfo *dcu = clki->driver.cell; if (!dcu->attrs.count(id_BEL)) @@ -1842,7 +1473,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_EHXPLLL && !ci->attrs.count(id_BEL)) { - const NetInfo *drivernet = net_or_nullptr(ci, id_CLKI); + const NetInfo *drivernet = ci->getPort(id_CLKI); if (drivernet == nullptr || drivernet->driver.cell == nullptr) continue; const CellInfo *drivercell = drivernet->driver.cell; @@ -2079,7 +1710,7 @@ class Ecp5Packer {id_RDMOVE, id_RDDIRECTION, id_WRMOVE, id_WRDIRECTION, id_READ0, id_READ1, id_READCLKSEL0, id_READCLKSEL1, id_READCLKSEL2, id_DYNDELAY0, id_DYNDELAY1, id_DYNDELAY2, id_DYNDELAY3, id_DYNDELAY4, id_DYNDELAY5, id_DYNDELAY6, id_DYNDELAY7}) { - if (net_or_nullptr(ci, zport) == nullptr) + if (ci->getPort(zport) == nullptr) tie_zero(ci, zport); } } @@ -2778,7 +2409,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->type == id_CLKDIVF) { - const NetInfo *clki = net_or_nullptr(ci, id_CLKI); + const NetInfo *clki = ci->getPort(id_CLKI); for (auto &eclk : eclks) { if (eclk.second.unbuf == clki) { for (auto bel : ctx->getBels()) { @@ -2800,8 +2431,8 @@ class Ecp5Packer clkdiv_done: continue; } else if (ci->type == id_ECLKSYNCB) { - const NetInfo *eclki = net_or_nullptr(ci, id_ECLKI); - const NetInfo *eclko = net_or_nullptr(ci, id_ECLKO); + const NetInfo *eclki = ci->getPort(id_ECLKI); + const NetInfo *eclko = ci->getPort(id_ECLKO); if (eclki != nullptr && eclki->driver.cell != nullptr) { if (eclki->driver.cell->type == id_ECLKBRIDGECS) { BelId bel = ctx->getBelByNameStr(eclki->driver.cell->attrs.at(id_BEL).as_string()); @@ -2833,13 +2464,13 @@ class Ecp5Packer continue; } else if (ci->type == id_DDRDLLA) { ci->type = id_DDRDLL; // transform from Verilog to Bel name - const NetInfo *clk = net_or_nullptr(ci, id_CLK); + const NetInfo *clk = ci->getPort(id_CLK); if (clk == nullptr) log_error("DDRDLLA '%s' has disconnected port CLK\n", ci->name.c_str(ctx)); bool left_bank_users = false, right_bank_users = false; // Check which side the delay codes (DDRDEL) are used on - const NetInfo *ddrdel = net_or_nullptr(ci, id_DDRDEL); + const NetInfo *ddrdel = ci->getPort(id_DDRDEL); if (ddrdel != nullptr) { for (auto &usr : ddrdel->users) { const CellInfo *uc = usr.cell; @@ -2922,7 +2553,7 @@ class Ecp5Packer continue; // Case of a CLKDIVF driven by an ECLKSYNC constrained above; without the input being used elsewhere as // an edge clock - const NetInfo *clki = net_or_nullptr(ci, id_CLKI); + const NetInfo *clki = ci->getPort(id_CLKI); if (clki == nullptr || clki->driver.cell == nullptr) continue; CellInfo *drv = clki->driver.cell; @@ -3131,12 +2762,9 @@ class Ecp5Packer pack_constants(); pack_dram(); pack_carries(); - find_lutff_pairs(); + pack_luts(); pack_lut5xs(); - pair_luts(); - pack_lut_pairs(); - pack_remaining_luts(); - pack_remaining_ffs(); + pack_ffs(); generate_constraints(); promote_ecp5_globals(ctx); ctx->fixupHierarchy(); @@ -3180,122 +2808,147 @@ bool Arch::pack() } } -void Arch::assignArchInfo() +void Arch::assign_arch_info_for_cell(CellInfo *ci) { - for (auto &cell : cells) { - CellInfo *ci = cell.second.get(); - if (ci->type == id_TRELLIS_SLICE) { - - ci->sliceInfo.using_dff = false; - if (ci->ports.count(id_Q0) && ci->ports[id_Q0].net != nullptr) - ci->sliceInfo.using_dff = true; - if (ci->ports.count(id_Q1) && ci->ports[id_Q1].net != nullptr) - ci->sliceInfo.using_dff = true; - - if (ci->ports.count(id_CLK) && ci->ports[id_CLK].net != nullptr) - ci->sliceInfo.clk_sig = ci->ports[id_CLK].net->name; - else - ci->sliceInfo.clk_sig = IdString(); + auto get_port_net = [&](CellInfo *ci, IdString p) { + NetInfo *n = get_net_or_empty(ci, p); + return n ? n->name : IdString(); + }; + if (ci->type == id_TRELLIS_COMB) { + std::string mode = str_or_default(ci->params, id_MODE, "LOGIC"); + ci->combInfo.flags = ArchCellInfo::COMB_NONE; + if (mode == "CCU2") + ci->combInfo.flags |= ArchCellInfo::COMB_CARRY; + if (mode == "DPRAM") { + ci->combInfo.flags |= ArchCellInfo::COMB_LUTRAM; + std::string wckmux = str_or_default(ci->params, id_WCKMUX, "WCK"); + if (wckmux == "INV") + ci->combInfo.flags |= ArchCellInfo::COMB_RAM_WCKINV; + std::string wremux = str_or_default(ci->params, id_WREMUX, "WRE"); + if (wremux == "INV" || wremux == "0") + ci->combInfo.flags |= ArchCellInfo::COMB_RAM_WREINV; + ci->combInfo.ram_wck = get_port_net(ci, id_WCK); + ci->combInfo.ram_wre = get_port_net(ci, id_WRE); + } + if (mode == "RAMW_BLOCK") + ci->combInfo.flags |= ArchCellInfo::COMB_RAMW_BLOCK; + if (ci->getPort(id_F1) != nullptr) + ci->combInfo.flags |= ArchCellInfo::COMB_MUX5; + if (ci->getPort(id_FXA) != nullptr || ci->getPort(id_FXB) != nullptr) { + ci->combInfo.flags |= ArchCellInfo::COMB_MUX6; + NetInfo *fxa = ci->getPort(id_FXA); + if (fxa != nullptr) + ci->combInfo.mux_fxad = fxa->driver.cell; + } + } else if (ci->type == id_TRELLIS_FF) { + ci->ffInfo.flags = ArchCellInfo::FF_NONE; + if (str_or_default(ci->params, id_GSR, "ENABLED") == "ENABLED") + ci->ffInfo.flags |= ArchCellInfo::FF_GSREN; + if (str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC") + ci->ffInfo.flags |= ArchCellInfo::FF_ASYNC; + if (ci->getPort(id_M) != nullptr) + ci->ffInfo.flags |= ArchCellInfo::FF_M_USED; + std::string clkmux = str_or_default(ci->params, id_CLKMUX, "CLK"); + std::string cemux = str_or_default(ci->params, id_CEMUX, "CE"); + std::string lsrmux = str_or_default(ci->params, id_LSRMUX, "LSR"); + if (clkmux == "INV" || clkmux == "0") + ci->ffInfo.flags |= ArchCellInfo::FF_CLKINV; + if (cemux == "INV" || cemux == "0") + ci->ffInfo.flags |= ArchCellInfo::FF_CEINV; + if (cemux == "1" || cemux == "0") + ci->ffInfo.flags |= ArchCellInfo::FF_CECONST; + if (lsrmux == "INV") + ci->ffInfo.flags |= ArchCellInfo::FF_LSRINV; + ci->ffInfo.clk_sig = get_port_net(ci, id_CLK); + ci->ffInfo.ce_sig = get_port_net(ci, id_CE); + ci->ffInfo.lsr_sig = get_port_net(ci, id_LSR); + } else if (ci->type == id_DP16KD) { + ci->ramInfo.is_pdp = (int_or_default(ci->params, id_DATA_WIDTH_A, 0) == 36); + + // Output register mode (REGMODE_{A,B}). Valid options are 'NOREG' and 'OUTREG'. + std::string regmode_a = str_or_default(ci->params, id_REGMODE_A, "NOREG"); + if (regmode_a != "NOREG" && regmode_a != "OUTREG") + log_error("DP16KD %s has invalid REGMODE_A configuration '%s'\n", ci->name.c_str(this), regmode_a.c_str()); + std::string regmode_b = str_or_default(ci->params, id_REGMODE_B, "NOREG"); + if (regmode_b != "NOREG" && regmode_b != "OUTREG") + log_error("DP16KD %s has invalid REGMODE_B configuration '%s'\n", ci->name.c_str(this), regmode_b.c_str()); + ci->ramInfo.is_output_a_registered = regmode_a == "OUTREG"; + ci->ramInfo.is_output_b_registered = regmode_b == "OUTREG"; + + // Based on the REGMODE, we have different timing lookup tables. + if (!ci->ramInfo.is_output_a_registered && !ci->ramInfo.is_output_b_registered) { + ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG; + } else if (!ci->ramInfo.is_output_a_registered && ci->ramInfo.is_output_b_registered) { + ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_NOREG_REGMODE_B_OUTREG; + } else if (ci->ramInfo.is_output_a_registered && !ci->ramInfo.is_output_b_registered) { + ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_OUTREG_REGMODE_B_NOREG; + } else if (ci->ramInfo.is_output_a_registered && ci->ramInfo.is_output_b_registered) { + ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_OUTREG_REGMODE_B_OUTREG; + } + } else if (ci->type == id_MULT18X18D) { + // For the multiplier block, our timing db is dictated by whether any of the input/output registers are + // enabled. To that end, we need to work out what the parameters are for the INPUTA_CLK, INPUTB_CLK and + // OUTPUT_CLK are. + // The clock check is the same IN_A/B and OUT, so hoist it to a function + auto get_clock_parameter = [&](std::string param_name) { + std::string clk = str_or_default(ci->params, id(param_name), "NONE"); + if (clk != "NONE" && clk != "CLK0" && clk != "CLK1" && clk != "CLK2" && clk != "CLK3") + log_error("MULT18X18D %s has invalid %s configuration '%s'\n", ci->name.c_str(this), param_name.c_str(), + clk.c_str()); + return clk; + }; - if (ci->ports.count(id_LSR) && ci->ports[id_LSR].net != nullptr) - ci->sliceInfo.lsr_sig = ci->ports[id_LSR].net->name; - else - ci->sliceInfo.lsr_sig = IdString(); - - ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK")); - ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR")); - ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); - std::string mode = str_or_default(ci->params, id_MODE, "LOGIC"); - ci->sliceInfo.is_carry = (mode == "CCU2"); - ci->sliceInfo.is_memory = (mode == "DPRAM" || mode == "RAMW"); - ci->sliceInfo.sd0 = std::stoi(str_or_default(ci->params, id_REG0_SD, "0")); - ci->sliceInfo.sd1 = std::stoi(str_or_default(ci->params, id_REG1_SD, "0")); - ci->sliceInfo.has_l6mux = false; - if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr && - ci->ports[id_FXA].net->driver.port == id_OFX0) - ci->sliceInfo.has_l6mux = true; - } else if (ci->type == id_DP16KD) { - ci->ramInfo.is_pdp = (int_or_default(ci->params, id_DATA_WIDTH_A, 0) == 36); - - // Output register mode (REGMODE_{A,B}). Valid options are 'NOREG' and 'OUTREG'. - std::string regmode_a = str_or_default(ci->params, id_REGMODE_A, "NOREG"); - if (regmode_a != "NOREG" && regmode_a != "OUTREG") - log_error("DP16KD %s has invalid REGMODE_A configuration '%s'\n", ci->name.c_str(this), - regmode_a.c_str()); - std::string regmode_b = str_or_default(ci->params, id_REGMODE_B, "NOREG"); - if (regmode_b != "NOREG" && regmode_b != "OUTREG") - log_error("DP16KD %s has invalid REGMODE_B configuration '%s'\n", ci->name.c_str(this), - regmode_b.c_str()); - ci->ramInfo.is_output_a_registered = regmode_a == "OUTREG"; - ci->ramInfo.is_output_b_registered = regmode_b == "OUTREG"; - - // Based on the REGMODE, we have different timing lookup tables. - if (!ci->ramInfo.is_output_a_registered && !ci->ramInfo.is_output_b_registered) { - ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG; - } else if (!ci->ramInfo.is_output_a_registered && ci->ramInfo.is_output_b_registered) { - ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_NOREG_REGMODE_B_OUTREG; - } else if (ci->ramInfo.is_output_a_registered && !ci->ramInfo.is_output_b_registered) { - ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_OUTREG_REGMODE_B_NOREG; - } else if (ci->ramInfo.is_output_a_registered && ci->ramInfo.is_output_b_registered) { - ci->ramInfo.regmode_timing_id = id_DP16KD_REGMODE_A_OUTREG_REGMODE_B_OUTREG; - } - } else if (ci->type == id_MULT18X18D) { - // For the multiplier block, our timing db is dictated by whether any of the input/output registers are - // enabled. To that end, we need to work out what the parameters are for the INPUTA_CLK, INPUTB_CLK and - // OUTPUT_CLK are. - // The clock check is the same IN_A/B and OUT, so hoist it to a function - auto get_clock_parameter = [&](std::string param_name) { - std::string clk = str_or_default(ci->params, id(param_name), "NONE"); - if (clk != "NONE" && clk != "CLK0" && clk != "CLK1" && clk != "CLK2" && clk != "CLK3") - log_error("MULT18X18D %s has invalid %s configuration '%s'\n", ci->name.c_str(this), - param_name.c_str(), clk.c_str()); - return clk; - }; - - // Get the input clock setting from the cell - std::string reg_inputa_clk = get_clock_parameter("REG_INPUTA_CLK"); - std::string reg_inputb_clk = get_clock_parameter("REG_INPUTB_CLK"); - - // Inputs are registered IFF the REG_INPUT value is not NONE - const bool is_in_a_registered = reg_inputa_clk != "NONE"; - const bool is_in_b_registered = reg_inputb_clk != "NONE"; - - // Similarly, get the output register clock - std::string reg_output_clk = get_clock_parameter("REG_OUTPUT_CLK"); - const bool is_output_registered = reg_output_clk != "NONE"; - - // If only one of the inputs is registered, we are going to treat that as - // neither input registered so that we don't have to deal with mixed timing. - // Emit a warning to that effect. - const bool any_input_registered = is_in_a_registered || is_in_b_registered; - const bool both_inputs_registered = is_in_a_registered && is_in_b_registered; - const bool input_registers_mismatched = any_input_registered && !both_inputs_registered; - if (input_registers_mismatched) { - log_warning("MULT18X18D %s has unsupported mixed input register modes (reg_inputa_clk=%s, " - "reg_inputb_clk=%s)\n", - ci->name.c_str(this), reg_inputa_clk.c_str(), reg_inputb_clk.c_str()); - log_warning("Timings for MULT18X18D %s will be calculated as though neither input were registered\n", - ci->name.c_str(this)); - - // Act as though the inputs are unregistered, so select timing DB based only on the - // output register mode - ci->multInfo.timing_id = is_output_registered ? id_MULT18X18D_REGS_OUTPUT : id_MULT18X18D_REGS_NONE; - } else { - // Based on our register settings, pick the timing data to use for this cell - if (!both_inputs_registered && !is_output_registered) { - ci->multInfo.timing_id = id_MULT18X18D_REGS_NONE; - } else if (both_inputs_registered && !is_output_registered) { - ci->multInfo.timing_id = id_MULT18X18D_REGS_INPUT; - } else if (!both_inputs_registered && is_output_registered) { - ci->multInfo.timing_id = id_MULT18X18D_REGS_OUTPUT; - } else if (both_inputs_registered && is_output_registered) { - ci->multInfo.timing_id = id_MULT18X18D_REGS_ALL; - } + // Get the input clock setting from the cell + std::string reg_inputa_clk = get_clock_parameter("REG_INPUTA_CLK"); + std::string reg_inputb_clk = get_clock_parameter("REG_INPUTB_CLK"); + + // Inputs are registered IFF the REG_INPUT value is not NONE + const bool is_in_a_registered = reg_inputa_clk != "NONE"; + const bool is_in_b_registered = reg_inputb_clk != "NONE"; + + // Similarly, get the output register clock + std::string reg_output_clk = get_clock_parameter("REG_OUTPUT_CLK"); + const bool is_output_registered = reg_output_clk != "NONE"; + + // If only one of the inputs is registered, we are going to treat that as + // neither input registered so that we don't have to deal with mixed timing. + // Emit a warning to that effect. + const bool any_input_registered = is_in_a_registered || is_in_b_registered; + const bool both_inputs_registered = is_in_a_registered && is_in_b_registered; + const bool input_registers_mismatched = any_input_registered && !both_inputs_registered; + if (input_registers_mismatched) { + log_warning("MULT18X18D %s has unsupported mixed input register modes (reg_inputa_clk=%s, " + "reg_inputb_clk=%s)\n", + ci->name.c_str(this), reg_inputa_clk.c_str(), reg_inputb_clk.c_str()); + log_warning("Timings for MULT18X18D %s will be calculated as though neither input were registered\n", + ci->name.c_str(this)); + + // Act as though the inputs are unregistered, so select timing DB based only on the + // output register mode + ci->multInfo.timing_id = is_output_registered ? id_MULT18X18D_REGS_OUTPUT : id_MULT18X18D_REGS_NONE; + } else { + // Based on our register settings, pick the timing data to use for this cell + if (!both_inputs_registered && !is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_NONE; + } else if (both_inputs_registered && !is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_INPUT; + } else if (!both_inputs_registered && is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_OUTPUT; + } else if (both_inputs_registered && is_output_registered) { + ci->multInfo.timing_id = id_MULT18X18D_REGS_ALL; } - // If we aren't a pure combinatorial multiplier, then our timings are - // calculated with respect to CLK0 - ci->multInfo.is_clocked = ci->multInfo.timing_id != id_MULT18X18D_REGS_NONE; } + // If we aren't a pure combinatorial multiplier, then our timings are + // calculated with respect to CLK0 + ci->multInfo.is_clocked = ci->multInfo.timing_id != id_MULT18X18D_REGS_NONE; + } +} + +void Arch::assignArchInfo() +{ + for (auto &cell : cells) { + CellInfo *ci = cell.second.get(); + assign_arch_info_for_cell(ci); } for (auto &net : nets) { net.second->is_global = bool_or_default(net.second->attrs, id_ECP5_IS_GLOBAL); diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index a586db7b90..1554ea1a0c 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -311,6 +311,64 @@ def process_loc_globals(chip): } +delay_db = {} +# Convert from Lattice-style grouped SLICE to new nextpnr split style SLICE +def postprocess_timing_data(cells): + def delay_diff(x, y): + return (x[0] - y[0], x[1] - y[1]) + + split_cells = {} + comb_delays = {} + comb_delays[("A", "F")] = delay_db["SLOGICB"][("A0", "F0")] + comb_delays[("B", "F")] = delay_db["SLOGICB"][("B0", "F0")] + comb_delays[("C", "F")] = delay_db["SLOGICB"][("C0", "F0")] + comb_delays[("D", "F")] = delay_db["SLOGICB"][("D0", "F0")] + comb_delays[("A", "OFX")] = delay_db["SLOGICB"][("A0", "OFX0")] + comb_delays[("B", "OFX")] = delay_db["SLOGICB"][("B0", "OFX0")] + comb_delays[("C", "OFX")] = delay_db["SLOGICB"][("C0", "OFX0")] + comb_delays[("D", "OFX")] = delay_db["SLOGICB"][("D0", "OFX0")] + comb_delays[("M", "OFX")] = delay_db["SLOGICB"][("M0", "OFX0")] # worst case + comb_delays[("F1", "OFX")] = delay_diff(delay_db["SLOGICB"][("A1", "OFX0")], + delay_db["SLOGICB"][("A1", "F1")]) + comb_delays[("FXA", "OFX")] = delay_db["SLOGICB"][("FXA", "OFX1")] + comb_delays[("FXB", "OFX")] = delay_db["SLOGICB"][("FXB", "OFX1")] + split_cells["TRELLIS_COMB"] = comb_delays + + carry0_delays = {} + carry0_delays[("A", "F")] = delay_db["SCCU2C"][("A0", "F0")] + carry0_delays[("B", "F")] = delay_db["SCCU2C"][("B0", "F0")] + carry0_delays[("C", "F")] = delay_db["SCCU2C"][("C0", "F0")] + carry0_delays[("D", "F")] = delay_db["SCCU2C"][("D0", "F0")] + carry0_delays[("A", "FCO")] = delay_db["SCCU2C"][("A0", "FCO")] + carry0_delays[("B", "FCO")] = delay_db["SCCU2C"][("B0", "FCO")] + carry0_delays[("C", "FCO")] = delay_db["SCCU2C"][("C0", "FCO")] + carry0_delays[("D", "FCO")] = delay_db["SCCU2C"][("D0", "FCO")] + carry0_delays[("FCI", "F")] = delay_db["SCCU2C"][("FCI", "F0")] + carry0_delays[("FCI", "FCO")] = delay_db["SCCU2C"][("FCI", "FCO")] + + split_cells["TRELLIS_COMB_CARRY0"] = carry0_delays + + carry1_delays = {} + carry1_delays[("A", "F")] = delay_db["SCCU2C"][("A1", "F1")] + carry1_delays[("B", "F")] = delay_db["SCCU2C"][("B1", "F1")] + carry1_delays[("C", "F")] = delay_db["SCCU2C"][("C1", "F1")] + carry1_delays[("D", "F")] = delay_db["SCCU2C"][("D1", "F1")] + carry1_delays[("A", "FCO")] = delay_db["SCCU2C"][("A1", "FCO")] + carry1_delays[("B", "FCO")] = delay_db["SCCU2C"][("B1", "FCO")] + carry1_delays[("C", "FCO")] = delay_db["SCCU2C"][("C1", "FCO")] + carry1_delays[("D", "FCO")] = delay_db["SCCU2C"][("D1", "FCO")] + carry1_delays[("FCI", "F")] = delay_diff(delay_db["SCCU2C"][("FCI", "F1")], delay_db["SCCU2C"][("FCI", "FCO")]) + carry1_delays[("FCI", "FCO")] = (0, 0) + + split_cells["TRELLIS_COMB_CARRY1"] = carry1_delays + + for celltype, celldelays in sorted(split_cells.items()): + delays = [] + setupholds = [] + for (from_pin, to_pin), (min_delay, max_delay) in sorted(celldelays.items()): + delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay)) + cells.append((constids[celltype], delays, setupholds)) + def process_timing_data(): for grade in speed_grade_names: with open(timing_dbs.cells_db_path("ECP5", grade)) as f: @@ -320,6 +378,7 @@ def process_timing_data(): celltype = constids[cell.replace(":", "_").replace("=", "_").replace(",", "_")] delays = [] setupholds = [] + delay_db[cell] = {} for entry in cdata: if entry["type"] == "Width": continue @@ -332,6 +391,7 @@ def process_timing_data(): to_pin = timing_port_xform[to_pin] min_delay = min(entry["rising"][0], entry["falling"][0]) max_delay = min(entry["rising"][2], entry["falling"][2]) + delay_db[cell][(from_pin, to_pin)] = (min_delay, max_delay) delays.append((constids[from_pin], constids[to_pin], min_delay, max_delay)) elif entry["type"] == "SetupHold": if type(entry["pin"]) is list: @@ -346,6 +406,7 @@ def process_timing_data(): else: assert False, entry["type"] cells.append((celltype, delays, setupholds)) + postprocess_timing_data(cells) pip_class_delays = [] for i in range(len(pip_class_to_idx)): pip_class_delays.append((50, 50, 0, 0)) @@ -625,7 +686,7 @@ def main(): # print("Initialising chip...") chip = pytrellis.Chip(dev_names[args.device]) # print("Building routing graph...") - ddrg = pytrellis.make_dedup_chipdb(chip, include_lutperm_pips=True) + ddrg = pytrellis.make_dedup_chipdb(chip, include_lutperm_pips=True, split_slice_mode=True) max_row = chip.get_max_row() max_col = chip.get_max_col() process_timing_data() From 49f178ed94b5fad00d25dbd12adea0bf4732f803 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 8 Apr 2022 13:42:54 +0100 Subject: [PATCH 130/712] Split up common into kernel,place,route Signed-off-by: gatecat --- CMakeLists.txt | 9 ++++++--- common/{ => kernel}/arch_api.h | 0 common/{ => kernel}/arch_pybindings_shared.h | 0 common/{ => kernel}/archcheck.cc | 0 common/{ => kernel}/base_arch.h | 0 common/{ => kernel}/base_clusterinfo.h | 0 common/{ => kernel}/basectx.cc | 0 common/{ => kernel}/basectx.h | 0 common/{ => kernel}/bits.cc | 0 common/{ => kernel}/bits.h | 0 common/{ => kernel}/chain_utils.h | 0 common/{ => kernel}/command.cc | 0 common/{ => kernel}/command.h | 0 common/{ => kernel}/constraints.h | 0 common/{ => kernel}/constraints.impl.h | 0 common/{ => kernel}/context.cc | 0 common/{ => kernel}/context.h | 0 common/{ => kernel}/design_utils.cc | 0 common/{ => kernel}/design_utils.h | 0 common/{ => kernel}/deterministic_rng.h | 0 common/{ => kernel}/dynamic_bitarray.h | 0 common/{ => kernel}/embed.cc | 0 common/{ => kernel}/embed.h | 0 common/{ => kernel}/exclusive_state_groups.h | 0 common/{ => kernel}/exclusive_state_groups.impl.h | 0 common/{ => kernel}/handle_error.cc | 0 common/{ => kernel}/hashlib.h | 0 common/{ => kernel}/idstring.cc | 0 common/{ => kernel}/idstring.h | 0 common/{ => kernel}/idstringlist.cc | 0 common/{ => kernel}/idstringlist.h | 0 common/{ => kernel}/indexed_store.h | 0 common/{ => kernel}/log.cc | 0 common/{ => kernel}/log.h | 0 common/{ => kernel}/nextpnr.cc | 0 common/{ => kernel}/nextpnr.h | 0 common/{ => kernel}/nextpnr_assertions.cc | 0 common/{ => kernel}/nextpnr_assertions.h | 0 common/{ => kernel}/nextpnr_base_types.h | 0 common/{ => kernel}/nextpnr_namespaces.cc | 0 common/{ => kernel}/nextpnr_namespaces.h | 0 common/{ => kernel}/nextpnr_types.cc | 0 common/{ => kernel}/nextpnr_types.h | 0 common/{ => kernel}/property.cc | 0 common/{ => kernel}/property.h | 0 common/{ => kernel}/pybindings.cc | 0 common/{ => kernel}/pybindings.h | 0 common/{ => kernel}/pycontainers.h | 0 common/{ => kernel}/pywrappers.h | 0 common/{ => kernel}/relptr.h | 0 common/{ => kernel}/report.cc | 0 common/{ => kernel}/scope_lock.h | 0 common/{ => kernel}/sdf.cc | 0 common/{ => kernel}/sso_array.h | 0 common/{ => kernel}/str_ring_buffer.cc | 0 common/{ => kernel}/str_ring_buffer.h | 0 common/{ => kernel}/svg.cc | 0 common/{ => kernel}/timing.cc | 0 common/{ => kernel}/timing.h | 0 common/{ => kernel}/util.h | 0 common/{ => place}/fast_bels.h | 0 common/{ => place}/parallel_refine.cc | 0 common/{ => place}/parallel_refine.h | 0 common/{ => place}/place_common.cc | 0 common/{ => place}/place_common.h | 0 common/{ => place}/placer1.cc | 0 common/{ => place}/placer1.h | 0 common/{ => place}/placer_heap.cc | 0 common/{ => place}/placer_heap.h | 0 common/{ => place}/timing_opt.cc | 0 common/{ => place}/timing_opt.h | 0 common/{ => route}/router1.cc | 0 common/{ => route}/router1.h | 0 common/{ => route}/router2.cc | 0 common/{ => route}/router2.h | 0 75 files changed, 6 insertions(+), 3 deletions(-) rename common/{ => kernel}/arch_api.h (100%) rename common/{ => kernel}/arch_pybindings_shared.h (100%) rename common/{ => kernel}/archcheck.cc (100%) rename common/{ => kernel}/base_arch.h (100%) rename common/{ => kernel}/base_clusterinfo.h (100%) rename common/{ => kernel}/basectx.cc (100%) rename common/{ => kernel}/basectx.h (100%) rename common/{ => kernel}/bits.cc (100%) rename common/{ => kernel}/bits.h (100%) rename common/{ => kernel}/chain_utils.h (100%) rename common/{ => kernel}/command.cc (100%) rename common/{ => kernel}/command.h (100%) rename common/{ => kernel}/constraints.h (100%) rename common/{ => kernel}/constraints.impl.h (100%) rename common/{ => kernel}/context.cc (100%) rename common/{ => kernel}/context.h (100%) rename common/{ => kernel}/design_utils.cc (100%) rename common/{ => kernel}/design_utils.h (100%) rename common/{ => kernel}/deterministic_rng.h (100%) rename common/{ => kernel}/dynamic_bitarray.h (100%) rename common/{ => kernel}/embed.cc (100%) rename common/{ => kernel}/embed.h (100%) rename common/{ => kernel}/exclusive_state_groups.h (100%) rename common/{ => kernel}/exclusive_state_groups.impl.h (100%) rename common/{ => kernel}/handle_error.cc (100%) rename common/{ => kernel}/hashlib.h (100%) rename common/{ => kernel}/idstring.cc (100%) rename common/{ => kernel}/idstring.h (100%) rename common/{ => kernel}/idstringlist.cc (100%) rename common/{ => kernel}/idstringlist.h (100%) rename common/{ => kernel}/indexed_store.h (100%) rename common/{ => kernel}/log.cc (100%) rename common/{ => kernel}/log.h (100%) rename common/{ => kernel}/nextpnr.cc (100%) rename common/{ => kernel}/nextpnr.h (100%) rename common/{ => kernel}/nextpnr_assertions.cc (100%) rename common/{ => kernel}/nextpnr_assertions.h (100%) rename common/{ => kernel}/nextpnr_base_types.h (100%) rename common/{ => kernel}/nextpnr_namespaces.cc (100%) rename common/{ => kernel}/nextpnr_namespaces.h (100%) rename common/{ => kernel}/nextpnr_types.cc (100%) rename common/{ => kernel}/nextpnr_types.h (100%) rename common/{ => kernel}/property.cc (100%) rename common/{ => kernel}/property.h (100%) rename common/{ => kernel}/pybindings.cc (100%) rename common/{ => kernel}/pybindings.h (100%) rename common/{ => kernel}/pycontainers.h (100%) rename common/{ => kernel}/pywrappers.h (100%) rename common/{ => kernel}/relptr.h (100%) rename common/{ => kernel}/report.cc (100%) rename common/{ => kernel}/scope_lock.h (100%) rename common/{ => kernel}/sdf.cc (100%) rename common/{ => kernel}/sso_array.h (100%) rename common/{ => kernel}/str_ring_buffer.cc (100%) rename common/{ => kernel}/str_ring_buffer.h (100%) rename common/{ => kernel}/svg.cc (100%) rename common/{ => kernel}/timing.cc (100%) rename common/{ => kernel}/timing.h (100%) rename common/{ => kernel}/util.h (100%) rename common/{ => place}/fast_bels.h (100%) rename common/{ => place}/parallel_refine.cc (100%) rename common/{ => place}/parallel_refine.h (100%) rename common/{ => place}/place_common.cc (100%) rename common/{ => place}/place_common.h (100%) rename common/{ => place}/placer1.cc (100%) rename common/{ => place}/placer1.h (100%) rename common/{ => place}/placer_heap.cc (100%) rename common/{ => place}/placer_heap.h (100%) rename common/{ => place}/timing_opt.cc (100%) rename common/{ => place}/timing_opt.h (100%) rename common/{ => route}/router1.cc (100%) rename common/{ => route}/router1.h (100%) rename common/{ => route}/router2.cc (100%) rename common/{ => route}/router2.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fcbbbb13fe..89bdb3607c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,7 +216,7 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/common/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/version.h ) -include_directories(common/ json/ frontend/ 3rdparty/json11/ 3rdparty/pybind11/include ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) +include_directories(common/kernel/ common/place/ common/route/ json/ frontend/ 3rdparty/json11/ 3rdparty/pybind11/include ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) if(BUILD_HEAP) find_package (Eigen3 REQUIRED NO_MODULE) @@ -225,12 +225,15 @@ if(BUILD_HEAP) add_definitions(-DWITH_HEAP) endif() -aux_source_directory(common/ COMMON_SRC_FILES) +aux_source_directory(common/kernel/ KERNEL_SRC_FILES) +aux_source_directory(common/place/ PLACE_SRC_FILES) +aux_source_directory(common/route/ ROUTE_SRC_FILES) + aux_source_directory(json/ JSON_PARSER_FILES) aux_source_directory(3rdparty/json11 EXT_JSON11_FILES) aux_source_directory(frontend/ FRONTEND_FILES) -set(COMMON_FILES ${COMMON_SRC_FILES} ${EXT_JSON11_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES}) +set(COMMON_FILES ${KERNEL_SRC_FILES} ${PLACE_SRC_FILES} ${ROUTE_SRC_FILES} ${EXT_JSON11_FILES} ${JSON_PARSER_FILES} ${FRONTEND_FILES}) if( NOT CMAKE_BUILD_TYPE ) set(CMAKE_BUILD_TYPE Release) endif() diff --git a/common/arch_api.h b/common/kernel/arch_api.h similarity index 100% rename from common/arch_api.h rename to common/kernel/arch_api.h diff --git a/common/arch_pybindings_shared.h b/common/kernel/arch_pybindings_shared.h similarity index 100% rename from common/arch_pybindings_shared.h rename to common/kernel/arch_pybindings_shared.h diff --git a/common/archcheck.cc b/common/kernel/archcheck.cc similarity index 100% rename from common/archcheck.cc rename to common/kernel/archcheck.cc diff --git a/common/base_arch.h b/common/kernel/base_arch.h similarity index 100% rename from common/base_arch.h rename to common/kernel/base_arch.h diff --git a/common/base_clusterinfo.h b/common/kernel/base_clusterinfo.h similarity index 100% rename from common/base_clusterinfo.h rename to common/kernel/base_clusterinfo.h diff --git a/common/basectx.cc b/common/kernel/basectx.cc similarity index 100% rename from common/basectx.cc rename to common/kernel/basectx.cc diff --git a/common/basectx.h b/common/kernel/basectx.h similarity index 100% rename from common/basectx.h rename to common/kernel/basectx.h diff --git a/common/bits.cc b/common/kernel/bits.cc similarity index 100% rename from common/bits.cc rename to common/kernel/bits.cc diff --git a/common/bits.h b/common/kernel/bits.h similarity index 100% rename from common/bits.h rename to common/kernel/bits.h diff --git a/common/chain_utils.h b/common/kernel/chain_utils.h similarity index 100% rename from common/chain_utils.h rename to common/kernel/chain_utils.h diff --git a/common/command.cc b/common/kernel/command.cc similarity index 100% rename from common/command.cc rename to common/kernel/command.cc diff --git a/common/command.h b/common/kernel/command.h similarity index 100% rename from common/command.h rename to common/kernel/command.h diff --git a/common/constraints.h b/common/kernel/constraints.h similarity index 100% rename from common/constraints.h rename to common/kernel/constraints.h diff --git a/common/constraints.impl.h b/common/kernel/constraints.impl.h similarity index 100% rename from common/constraints.impl.h rename to common/kernel/constraints.impl.h diff --git a/common/context.cc b/common/kernel/context.cc similarity index 100% rename from common/context.cc rename to common/kernel/context.cc diff --git a/common/context.h b/common/kernel/context.h similarity index 100% rename from common/context.h rename to common/kernel/context.h diff --git a/common/design_utils.cc b/common/kernel/design_utils.cc similarity index 100% rename from common/design_utils.cc rename to common/kernel/design_utils.cc diff --git a/common/design_utils.h b/common/kernel/design_utils.h similarity index 100% rename from common/design_utils.h rename to common/kernel/design_utils.h diff --git a/common/deterministic_rng.h b/common/kernel/deterministic_rng.h similarity index 100% rename from common/deterministic_rng.h rename to common/kernel/deterministic_rng.h diff --git a/common/dynamic_bitarray.h b/common/kernel/dynamic_bitarray.h similarity index 100% rename from common/dynamic_bitarray.h rename to common/kernel/dynamic_bitarray.h diff --git a/common/embed.cc b/common/kernel/embed.cc similarity index 100% rename from common/embed.cc rename to common/kernel/embed.cc diff --git a/common/embed.h b/common/kernel/embed.h similarity index 100% rename from common/embed.h rename to common/kernel/embed.h diff --git a/common/exclusive_state_groups.h b/common/kernel/exclusive_state_groups.h similarity index 100% rename from common/exclusive_state_groups.h rename to common/kernel/exclusive_state_groups.h diff --git a/common/exclusive_state_groups.impl.h b/common/kernel/exclusive_state_groups.impl.h similarity index 100% rename from common/exclusive_state_groups.impl.h rename to common/kernel/exclusive_state_groups.impl.h diff --git a/common/handle_error.cc b/common/kernel/handle_error.cc similarity index 100% rename from common/handle_error.cc rename to common/kernel/handle_error.cc diff --git a/common/hashlib.h b/common/kernel/hashlib.h similarity index 100% rename from common/hashlib.h rename to common/kernel/hashlib.h diff --git a/common/idstring.cc b/common/kernel/idstring.cc similarity index 100% rename from common/idstring.cc rename to common/kernel/idstring.cc diff --git a/common/idstring.h b/common/kernel/idstring.h similarity index 100% rename from common/idstring.h rename to common/kernel/idstring.h diff --git a/common/idstringlist.cc b/common/kernel/idstringlist.cc similarity index 100% rename from common/idstringlist.cc rename to common/kernel/idstringlist.cc diff --git a/common/idstringlist.h b/common/kernel/idstringlist.h similarity index 100% rename from common/idstringlist.h rename to common/kernel/idstringlist.h diff --git a/common/indexed_store.h b/common/kernel/indexed_store.h similarity index 100% rename from common/indexed_store.h rename to common/kernel/indexed_store.h diff --git a/common/log.cc b/common/kernel/log.cc similarity index 100% rename from common/log.cc rename to common/kernel/log.cc diff --git a/common/log.h b/common/kernel/log.h similarity index 100% rename from common/log.h rename to common/kernel/log.h diff --git a/common/nextpnr.cc b/common/kernel/nextpnr.cc similarity index 100% rename from common/nextpnr.cc rename to common/kernel/nextpnr.cc diff --git a/common/nextpnr.h b/common/kernel/nextpnr.h similarity index 100% rename from common/nextpnr.h rename to common/kernel/nextpnr.h diff --git a/common/nextpnr_assertions.cc b/common/kernel/nextpnr_assertions.cc similarity index 100% rename from common/nextpnr_assertions.cc rename to common/kernel/nextpnr_assertions.cc diff --git a/common/nextpnr_assertions.h b/common/kernel/nextpnr_assertions.h similarity index 100% rename from common/nextpnr_assertions.h rename to common/kernel/nextpnr_assertions.h diff --git a/common/nextpnr_base_types.h b/common/kernel/nextpnr_base_types.h similarity index 100% rename from common/nextpnr_base_types.h rename to common/kernel/nextpnr_base_types.h diff --git a/common/nextpnr_namespaces.cc b/common/kernel/nextpnr_namespaces.cc similarity index 100% rename from common/nextpnr_namespaces.cc rename to common/kernel/nextpnr_namespaces.cc diff --git a/common/nextpnr_namespaces.h b/common/kernel/nextpnr_namespaces.h similarity index 100% rename from common/nextpnr_namespaces.h rename to common/kernel/nextpnr_namespaces.h diff --git a/common/nextpnr_types.cc b/common/kernel/nextpnr_types.cc similarity index 100% rename from common/nextpnr_types.cc rename to common/kernel/nextpnr_types.cc diff --git a/common/nextpnr_types.h b/common/kernel/nextpnr_types.h similarity index 100% rename from common/nextpnr_types.h rename to common/kernel/nextpnr_types.h diff --git a/common/property.cc b/common/kernel/property.cc similarity index 100% rename from common/property.cc rename to common/kernel/property.cc diff --git a/common/property.h b/common/kernel/property.h similarity index 100% rename from common/property.h rename to common/kernel/property.h diff --git a/common/pybindings.cc b/common/kernel/pybindings.cc similarity index 100% rename from common/pybindings.cc rename to common/kernel/pybindings.cc diff --git a/common/pybindings.h b/common/kernel/pybindings.h similarity index 100% rename from common/pybindings.h rename to common/kernel/pybindings.h diff --git a/common/pycontainers.h b/common/kernel/pycontainers.h similarity index 100% rename from common/pycontainers.h rename to common/kernel/pycontainers.h diff --git a/common/pywrappers.h b/common/kernel/pywrappers.h similarity index 100% rename from common/pywrappers.h rename to common/kernel/pywrappers.h diff --git a/common/relptr.h b/common/kernel/relptr.h similarity index 100% rename from common/relptr.h rename to common/kernel/relptr.h diff --git a/common/report.cc b/common/kernel/report.cc similarity index 100% rename from common/report.cc rename to common/kernel/report.cc diff --git a/common/scope_lock.h b/common/kernel/scope_lock.h similarity index 100% rename from common/scope_lock.h rename to common/kernel/scope_lock.h diff --git a/common/sdf.cc b/common/kernel/sdf.cc similarity index 100% rename from common/sdf.cc rename to common/kernel/sdf.cc diff --git a/common/sso_array.h b/common/kernel/sso_array.h similarity index 100% rename from common/sso_array.h rename to common/kernel/sso_array.h diff --git a/common/str_ring_buffer.cc b/common/kernel/str_ring_buffer.cc similarity index 100% rename from common/str_ring_buffer.cc rename to common/kernel/str_ring_buffer.cc diff --git a/common/str_ring_buffer.h b/common/kernel/str_ring_buffer.h similarity index 100% rename from common/str_ring_buffer.h rename to common/kernel/str_ring_buffer.h diff --git a/common/svg.cc b/common/kernel/svg.cc similarity index 100% rename from common/svg.cc rename to common/kernel/svg.cc diff --git a/common/timing.cc b/common/kernel/timing.cc similarity index 100% rename from common/timing.cc rename to common/kernel/timing.cc diff --git a/common/timing.h b/common/kernel/timing.h similarity index 100% rename from common/timing.h rename to common/kernel/timing.h diff --git a/common/util.h b/common/kernel/util.h similarity index 100% rename from common/util.h rename to common/kernel/util.h diff --git a/common/fast_bels.h b/common/place/fast_bels.h similarity index 100% rename from common/fast_bels.h rename to common/place/fast_bels.h diff --git a/common/parallel_refine.cc b/common/place/parallel_refine.cc similarity index 100% rename from common/parallel_refine.cc rename to common/place/parallel_refine.cc diff --git a/common/parallel_refine.h b/common/place/parallel_refine.h similarity index 100% rename from common/parallel_refine.h rename to common/place/parallel_refine.h diff --git a/common/place_common.cc b/common/place/place_common.cc similarity index 100% rename from common/place_common.cc rename to common/place/place_common.cc diff --git a/common/place_common.h b/common/place/place_common.h similarity index 100% rename from common/place_common.h rename to common/place/place_common.h diff --git a/common/placer1.cc b/common/place/placer1.cc similarity index 100% rename from common/placer1.cc rename to common/place/placer1.cc diff --git a/common/placer1.h b/common/place/placer1.h similarity index 100% rename from common/placer1.h rename to common/place/placer1.h diff --git a/common/placer_heap.cc b/common/place/placer_heap.cc similarity index 100% rename from common/placer_heap.cc rename to common/place/placer_heap.cc diff --git a/common/placer_heap.h b/common/place/placer_heap.h similarity index 100% rename from common/placer_heap.h rename to common/place/placer_heap.h diff --git a/common/timing_opt.cc b/common/place/timing_opt.cc similarity index 100% rename from common/timing_opt.cc rename to common/place/timing_opt.cc diff --git a/common/timing_opt.h b/common/place/timing_opt.h similarity index 100% rename from common/timing_opt.h rename to common/place/timing_opt.h diff --git a/common/router1.cc b/common/route/router1.cc similarity index 100% rename from common/router1.cc rename to common/route/router1.cc diff --git a/common/router1.h b/common/route/router1.h similarity index 100% rename from common/router1.h rename to common/route/router1.h diff --git a/common/router2.cc b/common/route/router2.cc similarity index 100% rename from common/router2.cc rename to common/route/router2.cc diff --git a/common/router2.h b/common/route/router2.h similarity index 100% rename from common/router2.h rename to common/route/router2.h From 92a58a2631a30ac3f4c0291ecd1f2f01a912b9e9 Mon Sep 17 00:00:00 2001 From: gatecat Date: Fri, 8 Apr 2022 14:05:03 +0100 Subject: [PATCH 131/712] ci: Restructure and move entirely to GH actions from Cirrus Signed-off-by: gatecat --- .cirrus.yml | 18 ------ .cirrus/Dockerfile.ubuntu20.04 | 68 -------------------- .cirrus/archcheck.sh | 8 --- .github/ci/build_common.sh | 55 ++++++++++++++++ .github/ci/build_ecp5.sh | 24 +++++++ .github/ci/build_generic.sh | 25 ++++++++ .github/ci/build_gowin.sh | 23 +++++++ .github/ci/build_ice40.sh | 27 ++++++++ .github/ci/build_machxo2.sh | 21 ++++++ .github/ci/build_mistral.sh | 6 ++ .github/ci/build_nexus.sh | 23 +++++++ .github/workflows/arch_ci.yml | 95 ++++++++++++++++++++++++++++ .github/workflows/mistral_ci.yml | 29 --------- CMakeLists.txt | 2 +- ice40/smoketest/attosoc/smoketest.sh | 2 +- tests | 2 +- 16 files changed, 302 insertions(+), 126 deletions(-) delete mode 100644 .cirrus.yml delete mode 100644 .cirrus/Dockerfile.ubuntu20.04 delete mode 100755 .cirrus/archcheck.sh create mode 100644 .github/ci/build_common.sh create mode 100644 .github/ci/build_ecp5.sh create mode 100644 .github/ci/build_generic.sh create mode 100644 .github/ci/build_gowin.sh create mode 100644 .github/ci/build_ice40.sh create mode 100644 .github/ci/build_machxo2.sh create mode 100644 .github/ci/build_nexus.sh create mode 100644 .github/workflows/arch_ci.yml delete mode 100644 .github/workflows/mistral_ci.yml diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 86edee591e..0000000000 --- a/.cirrus.yml +++ /dev/null @@ -1,18 +0,0 @@ -task: - name: build-test-ubuntu2004 - container: - cpu: 5 - memory: 20 - dockerfile: .cirrus/Dockerfile.ubuntu20.04 - - submodule_script: git submodule sync --recursive && git submodule update --init --recursive - build_script: mkdir build && cd build && cmake .. -DARCH='ecp5;generic;gowin;ice40;machxo2;nexus' -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on -DBUILD_GUI=on -DWERROR=on && make -j3 - test_generic_script: cd build && ./nextpnr-generic-test - flow_test_generic_script: export NPNR=$(pwd)/build/nextpnr-generic && cd tests/generic/flow && ./run.sh - test_ice40_script: cd build && ./nextpnr-ice40-test - smoketest_ice40_script: export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh - test_ecp5_script: cd build && ./nextpnr-ecp5-test - smoketest_generic_script: export NEXTPNR=$(pwd)/build/nextpnr-generic && cd generic/examples && ./simple.sh && ./simtest.sh - regressiontest_ice40_script: make -j $(nproc) -C tests/ice40/regressions NPNR=$(pwd)/build/nextpnr-ice40 - regressiontest_ecp5_script: make -j $(nproc) -C tests/ecp5/regressions NPNR=$(pwd)/build/nextpnr-ecp5 - archcheck_script: BUILD_DIR=$(pwd)/build ./.cirrus/archcheck.sh diff --git a/.cirrus/Dockerfile.ubuntu20.04 b/.cirrus/Dockerfile.ubuntu20.04 deleted file mode 100644 index 35ea18bd9a..0000000000 --- a/.cirrus/Dockerfile.ubuntu20.04 +++ /dev/null @@ -1,68 +0,0 @@ -FROM ubuntu:focal-20201106 - -ENV DEBIAN_FRONTEND=noninteractive - -RUN set -e -x ;\ - apt-get -y update ;\ - apt-get -y upgrade ;\ - apt-get -y install \ - build-essential autoconf cmake clang bison wget flex gperf \ - libreadline-dev gawk tcl-dev libffi-dev graphviz xdot python3-dev \ - libboost-all-dev qt5-default git libftdi-dev pkg-config libeigen3-dev \ - zlib1g-dev curl python3-pip libcapnp-dev capnproto - - -RUN set -e -x ;\ - mkdir -p /usr/local/src ;\ - cd /usr/local/src ;\ - git clone --recursive https://github.com/steveicarus/iverilog.git ;\ - cd iverilog ;\ - git reset --hard 84b4ebee0cfcda28a242d89a07020cd70b1d3e7f ;\ - sh autoconf.sh ;\ - ./configure ;\ - make -j $(nproc) ;\ - make install ;\ - rm -rf /usr/local/src/iverilog - -RUN set -e -x ;\ - mkdir -p /usr/local/src ;\ - cd /usr/local/src ;\ - git clone --recursive https://github.com/YosysHQ/icestorm.git ;\ - cd icestorm ;\ - git reset --hard 9f66f9ce16941c6417813cb87653c735a78b53ae ;\ - make -j $(nproc) ;\ - make install - -RUN set -e -x ;\ - mkdir -p /usr/local/src ;\ - cd /usr/local/src ;\ - git clone --recursive https://github.com/YosysHQ/yosys.git ;\ - cd yosys ;\ - git reset --hard cd8b2ed4e6f9447c94d801de7db7ae6ce0976d57 ;\ - make -j $(nproc) ;\ - make install ;\ - rm -rf /usr/local/src/yosys - -RUN set -e -x ;\ - mkdir -p /usr/local/src ;\ - cd /usr/local/src ;\ - git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\ - cd prjtrellis ;\ - git reset --hard 26f917d6052e084df30211ae3a78c8a165121e09 ;\ - cd libtrellis ;\ - cmake . ;\ - make -j $(nproc) ;\ - make install - -RUN set -e -x ;\ - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ;\ - mkdir -p /usr/local/src ;\ - cd /usr/local/src ;\ - git clone --recursive https://github.com/gatecat/prjoxide.git ;\ - cd prjoxide ;\ - git reset --hard 318331f8b30c2e2a31cc41d51f104b671e180a8a ;\ - cd libprjoxide ;\ - PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide - -RUN set -e -x ;\ - pip3 install apycula==0.2a4 diff --git a/.cirrus/archcheck.sh b/.cirrus/archcheck.sh deleted file mode 100755 index 6ff6c043e9..0000000000 --- a/.cirrus/archcheck.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -ex -echo "Running archcheck!" -${BUILD_DIR}/nextpnr-ice40 --hx8k --package ct256 --test -${BUILD_DIR}/nextpnr-ice40 --up5k --package sg48 --test -${BUILD_DIR}/nextpnr-ecp5 --um5g-25k --package CABGA381 --test -${BUILD_DIR}/nextpnr-nexus --device LIFCL-40-9BG400CES --test -${BUILD_DIR}/nextpnr-gowin --device GW1N-UV4LQ144C6/I5 --test diff --git a/.github/ci/build_common.sh b/.github/ci/build_common.sh new file mode 100644 index 0000000000..2a2d844751 --- /dev/null +++ b/.github/ci/build_common.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Install latest Yosys +function build_yosys { + PREFIX=`pwd`/.yosys + YOSYS_PATH=${DEPS_PATH}/yosys + mkdir -p ${YOSYS_PATH} + git clone --recursive https://github.com/YosysHQ/yosys ${YOSYS_PATH} + pushd ${YOSYS_PATH} + git reset --hard ${YOSYS_REVISION} + make -j`nproc` PREFIX=$PREFIX + make install PREFIX=$PREFIX + popd +} + +function build_icestorm { + PREFIX=`pwd`/.icestorm + ICESTORM_PATH=${DEPS_PATH}/icestorm + mkdir -p ${ICESTORM_PATH} + git clone --recursive https://github.com/YosysHQ/icestorm ${ICESTORM_PATH} + pushd ${ICESTORM_PATH} + git reset --hard ${ICESTORM_REVISION} + make -j`nproc` PREFIX=${PREFIX} + make install PREFIX=${PREFIX} + popd +} + +function build_trellis { + PREFIX=`pwd`/.trellis + TRELLIS_PATH=${DEPS_PATH}/prjtrellis + mkdir -p ${TRELLIS_PATH} + git clone --recursive https://github.com/YosysHQ/prjtrellis ${TRELLIS_PATH} + pushd ${TRELLIS_PATH} + git reset --hard ${TRELLIS_REVISION} + mkdir -p libtrellis/build + pushd libtrellis/build + cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} .. + make -j`nproc` + make install + popd + popd +} + +function build_prjoxide { + PREFIX=`pwd`/.prjoxide + PRJOXIDE_PATH=${DEPS_PATH}/prjoxide + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ;\ + mkdir -p ${PRJOXIDE_PATH} + git clone --recursive https://github.com/gatecat/prjoxide ${PRJOXIDE_PATH} + pushd ${PRJOXIDE_PATH} + git reset --hard ${PRJOXIDE_REVISION} + cd libprjoxide + PATH=$PATH:$HOME/.cargo/bin cargo install --root $PREFIX --path prjoxide + popd +} diff --git a/.github/ci/build_ecp5.sh b/.github/ci/build_ecp5.sh new file mode 100644 index 0000000000..2d09a6f65b --- /dev/null +++ b/.github/ci/build_ecp5.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +function get_dependencies { + : +} + +function build_nextpnr { + mkdir build + pushd build + cmake .. -DARCH=ecp5 -DTRELLIS_INSTALL_PREFIX=${GITHUB_WORKSPACE}/.trellis -DWERROR=on -DBUILD_GUI=on -DUSE_IPO=off + make nextpnr-ecp5 -j`nproc` + popd +} + +function run_tests { + export PATH=${GITHUB_WORKSPACE}/.trellis/bin:${GITHUB_WORKSPACE}/.yosys/bin:$PATH + make -j $(nproc) -C tests/ecp5/regressions NPNR=$(pwd)/build/nextpnr-ecp5 +} + +function run_archcheck { + pushd build + ./nextpnr-ecp5 --um5g-25k --package CABGA381 --test + popd +} diff --git a/.github/ci/build_generic.sh b/.github/ci/build_generic.sh new file mode 100644 index 0000000000..49a73d3a15 --- /dev/null +++ b/.github/ci/build_generic.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +function get_dependencies { + : +} + +function build_nextpnr { + mkdir build + pushd build + cmake .. -DARCH=generic -DWERROR=on + make nextpnr-generic -j`nproc` + popd +} + +function run_tests { + export PATH=${GITHUB_WORKSPACE}/.yosys/bin:$PATH + ( export NPNR=$(pwd)/build/nextpnr-generic && cd tests/generic/flow && ./run.sh ) +} + +function run_archcheck { + pushd build + # TODO + # ./nextpnr-generic --uarch example --test + popd +} diff --git a/.github/ci/build_gowin.sh b/.github/ci/build_gowin.sh new file mode 100644 index 0000000000..430cd2b046 --- /dev/null +++ b/.github/ci/build_gowin.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function get_dependencies { + pip3 install apycula==${APYCULA_REVISION} +} + +function build_nextpnr { + mkdir build + pushd build + cmake .. -DARCH=gowin -DWERROR=on -DBUILD_GUI=on -DUSE_IPO=off + make nextpnr-gowin -j`nproc` + popd +} + +function run_tests { + : +} + +function run_archcheck { + pushd build + ./nextpnr-gowin --device GW1N-UV4LQ144C6/I5 --test + popd +} diff --git a/.github/ci/build_ice40.sh b/.github/ci/build_ice40.sh new file mode 100644 index 0000000000..ea9234ccd9 --- /dev/null +++ b/.github/ci/build_ice40.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +function get_dependencies { + : +} + +function build_nextpnr { + mkdir build + pushd build + cmake .. -DARCH=ice40 -DICESTORM_INSTALL_PREFIX=${GITHUB_WORKSPACE}/.icestorm -DWERROR=on -DBUILD_TESTS=on -DBUILD_GUI=on + make nextpnr-ice40 nextpnr-ice40-test -j`nproc` + popd +} + +function run_tests { + export PATH=${GITHUB_WORKSPACE}/.yosys/bin:${GITHUB_WORKSPACE}/.icestorm/bin:$PATH + (cd build && ./nextpnr-ice40-test) + (export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh) + make -j $(nproc) -C tests/ice40/regressions NPNR=$(pwd)/build/nextpnr-ice40 +} + +function run_archcheck { + pushd build + ./nextpnr-ice40 --hx8k --package ct256 --test + ./nextpnr-ice40 --up5k --package sg48 --test + popd +} diff --git a/.github/ci/build_machxo2.sh b/.github/ci/build_machxo2.sh new file mode 100644 index 0000000000..c81a538ba6 --- /dev/null +++ b/.github/ci/build_machxo2.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +function get_dependencies { + : +} + +function build_nextpnr { + mkdir build + pushd build + cmake .. -DARCH=machxo2 -DTRELLIS_INSTALL_PREFIX=${GITHUB_WORKSPACE}/.trellis -DWERROR=on -DUSE_IPO=off + make nextpnr-machxo2 -j`nproc` + popd +} + +function run_tests { + : +} + +function run_archcheck { + : +} diff --git a/.github/ci/build_mistral.sh b/.github/ci/build_mistral.sh index 3c809b0ee0..807c620649 100644 --- a/.github/ci/build_mistral.sh +++ b/.github/ci/build_mistral.sh @@ -1,5 +1,7 @@ #!/bin/bash +export MISTRAL_PATH=${DEPS_PATH}/mistral + function get_dependencies { # Fetch mistral mkdir -p ${MISTRAL_PATH} @@ -17,6 +19,10 @@ function build_nextpnr { popd } +function run_tests { + : +} + function run_archcheck { pushd build ./nextpnr-mistral --device 5CEBA2F17A7 --test diff --git a/.github/ci/build_nexus.sh b/.github/ci/build_nexus.sh new file mode 100644 index 0000000000..e8acd9b6d7 --- /dev/null +++ b/.github/ci/build_nexus.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function get_dependencies { + : +} + +function build_nextpnr { + mkdir build + pushd build + cmake .. -DARCH=nexus -DOXIDE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/.prjoxide + make nextpnr-nexus -j`nproc` + popd +} + +function run_tests { + : +} + +function run_archcheck { + pushd build + ./nextpnr-nexus --device LIFCL-40-9BG400CES --test + popd +} diff --git a/.github/workflows/arch_ci.yml b/.github/workflows/arch_ci.yml new file mode 100644 index 0000000000..5bf83630d5 --- /dev/null +++ b/.github/workflows/arch_ci.yml @@ -0,0 +1,95 @@ +# CI for everything other than the sui generis FPGA interchange arrangements + +name: Arch CI tests + +on: [push, pull_request] + +jobs: + Build-nextpnr: + strategy: + fail-fast: false + matrix: + arch: [mistral, ice40, ecp5, generic, nexus, machxo2, gowin] + runs-on: ubuntu-latest + env: + DEPS_PATH: ${{ github.workspace }}/deps + YOSYS_REVISION: bd7ee79486d4e8788f36de8c25a3fb2df451d682 + ICESTORM_REVISION: 9f66f9ce16941c6417813cb87653c735a78b53ae + TRELLIS_REVISION: 48486ebd1e03e4ac42c96299e881adf9d43bc241 + PRJOXIDE_REVISION: c3fb1526cf4a2165e15b74f4a994d153c7695fe4 + MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9 + APYCULA_REVISION: 0.2a4 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - uses: actions/setup-python@v2 + + - name: Install + run: | + sudo apt-get update + sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev lzma-dev libftdi-dev clang bison flex swig qt5-default iverilog + + - name: Cache yosys installation + uses: actions/cache@v2 + id: cache-yosys + with: + path: .yosys + key: cache-yosys-${{ env.YOSYS_REVISION }}-r2 + + - name: Build yosys + run: | + source ./.github/ci/build_common.sh + build_yosys + if: steps.cache-yosys.outputs.cache-hit != 'true' + + - name: Cache icestorm installation + uses: actions/cache@v2 + id: cache-icestorm + with: + path: .icestorm + key: cache-icestorm-${{ env.ICESTORM_REVISION }} + if: matrix.arch == 'ice40' + + - name: Build icestorm + run: | + source ./.github/ci/build_common.sh + build_icestorm + if: matrix.arch == 'ice40' && steps.cache-icestorm.outputs.cache-hit != 'true' + + - name: Cache trellis installation + uses: actions/cache@v2 + id: cache-trellis + with: + path: .trellis + key: cache-trellis-${{ env.TRELLIS_REVISION }} + if: matrix.arch == 'ecp5' || matrix.arch == 'machxo2' + + - name: Build trellis + run: | + source ./.github/ci/build_common.sh + build_trellis + if: (matrix.arch == 'ecp5' || matrix.arch == 'machxo2') && steps.cache-trellis.outputs.cache-hit != 'true' + + - name: Cache prjoxide installation + uses: actions/cache@v2 + id: cache-prjoxide + with: + path: .prjoxide + key: cache-prjoxide-${{ env.PRJOXIDE_REVISION }} + if: matrix.arch == 'nexus' + + - name: Build prjoxide + run: | + source ./.github/ci/build_common.sh + build_prjoxide + if: matrix.arch == 'nexus' && steps.cache-prjoxide.outputs.cache-hit != 'true' + + - name: Execute build nextpnr + run: | + source ./.github/ci/build_${{ matrix.arch }}.sh + get_dependencies + build_nextpnr + run_tests + run_archcheck diff --git a/.github/workflows/mistral_ci.yml b/.github/workflows/mistral_ci.yml deleted file mode 100644 index b0bbfb521e..0000000000 --- a/.github/workflows/mistral_ci.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Mistral CI tests - -on: [push, pull_request] - -jobs: - Build-nextpnr: - runs-on: ubuntu-latest - steps: - - - uses: actions/checkout@v2 - with: - submodules: recursive - - - uses: actions/setup-python@v2 - - - name: Install - run: | - sudo apt-get update - sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev lzma-dev clang bison flex swig - - - name: Execute build nextpnr - env: - MISTRAL_PATH: ${{ github.workspace }}/deps/mistral - MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9 - run: | - source ./.github/ci/build_mistral.sh - get_dependencies - build_nextpnr - run_archcheck diff --git a/CMakeLists.txt b/CMakeLists.txt index 89bdb3607c..3120cf402b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,7 +134,7 @@ if (MSVC) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996 /wd4127") else() # N.B. the -Wno-array-bounds is to work around a false positive in GCC 9 - set(WARN_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wno-array-bounds") + set(WARN_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wno-array-bounds -Wno-format-truncation") if (WERROR) set(WARN_FLAGS "${WARN_FLAGS} -Werror") endif() diff --git a/ice40/smoketest/attosoc/smoketest.sh b/ice40/smoketest/attosoc/smoketest.sh index 05408ab16c..6582346168 100755 --- a/ice40/smoketest/attosoc/smoketest.sh +++ b/ice40/smoketest/attosoc/smoketest.sh @@ -4,6 +4,6 @@ yosys -q -p 'synth_ice40 -json attosoc.json -top attosoc' attosoc.v picorv32.v $NEXTPNR --hx8k --json attosoc.json --pcf attosoc.pcf --asc attosoc.asc --freq 50 icetime -tmd hx8k -c 50 attosoc.asc icebox_vlog -L -l -p attosoc.pcf -c -n attosoc attosoc.asc > attosoc_pnr.v -iverilog -o attosoc_pnr_tb attosoc_pnr.v attosoc_tb.v `yosys-config --datdir/ice40/cells_sim.v` +iverilog -DNO_ICE40_DEFAULT_ASSIGNMENTS -o attosoc_pnr_tb attosoc_pnr.v attosoc_tb.v `yosys-config --datdir/ice40/cells_sim.v` vvp attosoc_pnr_tb diff output.txt golden.txt diff --git a/tests b/tests index ccc61e5ec7..00c55a9eb9 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit ccc61e5ec7cc04410462ec3196ad467354787afb +Subproject commit 00c55a9eb9ea2e062b51fe0d64741412b185d95d From d3ba259db2d857a075113706cec2fad9a00c3f72 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 11 Apr 2022 18:46:44 +0100 Subject: [PATCH 132/712] ice40: Avoid chain finder from mixing up chains by only allowing I3 chaining at end Signed-off-by: gatecat --- ice40/chains.cc | 79 ++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/ice40/chains.cc b/ice40/chains.cc index 6ea261de49..20edafaac4 100644 --- a/ice40/chains.cc +++ b/ice40/chains.cc @@ -183,43 +183,54 @@ class ChainConstrainer void process_carries() { - std::vector carry_chains = find_chains( - ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); }, - [](const Context *ctx, const - - CellInfo *cell) { - CellInfo *carry_prev = net_driven_by(ctx, cell->ports.at(id_CIN).net, is_lc, id_COUT); - if (carry_prev != nullptr) - return carry_prev; - CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(id_I3).net, is_lc, id_COUT); - if (i3_prev != nullptr) - return i3_prev; - return (CellInfo *)nullptr; - }, - [](const Context *ctx, const CellInfo *cell) { - CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(id_COUT).net, is_lc, id_CIN, false); - if (carry_next != nullptr) - return carry_next; - CellInfo *i3_next = net_only_drives(ctx, cell->ports.at(id_COUT).net, is_lc, id_I3, false); - if (i3_next != nullptr) - return i3_next; - return (CellInfo *)nullptr; - }); - pool chained; - for (auto &base_chain : carry_chains) { - for (auto c : base_chain.cells) - chained.insert(c->name); + // Find carry roots + std::vector carry_chains; + pool processed; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_lc(ctx, ci) && bool_or_default(ci->params, id_CARRY_ENABLE)) { + // possibly a non-root if CIN or I3 driven by another cout + NetInfo *cin = ci->getPort(id_CIN); + if (cin && cin->driver.cell && is_lc(ctx, cin->driver.cell) && cin->driver.port == id_COUT) { + continue; + } + carry_chains.emplace_back(); + auto &cc = carry_chains.back(); + CellInfo *cursor = ci; + while (cursor) { + cc.cells.push_back(cursor); + processed.insert(cursor->name); + NetInfo *cout = cursor->getPort(id_COUT); + if (!cout) + break; + cursor = nullptr; + // look for CIN connectivity + for (auto &usr : cout->users) { + if (is_lc(ctx, usr.cell) && usr.port == id_CIN && !processed.count(usr.cell->name)) { + cursor = usr.cell; + break; + } + } + // look for I3 connectivity - only to a top cell with no further chaining + if (cursor) + continue; + for (auto &usr : cout->users) { + if (is_lc(ctx, usr.cell) && usr.port == id_I3 && !processed.count(usr.cell->name) && + !usr.cell->getPort(id_COUT)) { + cursor = usr.cell; + break; + } + } + } + } } - // Any cells not in chains, but with carry enabled, must also be put in a single-carry chain - // for correct processing + // anything left behind.... for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (chained.find(cell.first) == chained.end() && is_lc(ctx, ci) && - bool_or_default(ci->params, id_CARRY_ENABLE)) { - CellChain sChain; - sChain.cells.push_back(ci); - chained.insert(cell.first); - carry_chains.push_back(sChain); + if (is_lc(ctx, ci) && bool_or_default(ci->params, id_CARRY_ENABLE) && !processed.count(ci->name)) { + carry_chains.emplace_back(); + carry_chains.back().cells.push_back(ci); + processed.insert(ci->name); } } std::vector all_chains; From 61b3e2e1ffbf0c0438c734a04d9d86d75668677e Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 13 Apr 2022 19:18:13 +0100 Subject: [PATCH 133/712] Move general parallel detail place code out of parallel_refine Signed-off-by: gatecat --- common/place/detail_place_cfg.h | 36 ++ common/place/detail_place_core.cc | 457 +++++++++++++++++++++++++ common/place/detail_place_core.h | 224 ++++++++++++ common/place/parallel_refine.cc | 552 +----------------------------- common/place/parallel_refine.h | 6 +- 5 files changed, 730 insertions(+), 545 deletions(-) create mode 100644 common/place/detail_place_cfg.h create mode 100644 common/place/detail_place_core.cc create mode 100644 common/place/detail_place_core.h diff --git a/common/place/detail_place_cfg.h b/common/place/detail_place_cfg.h new file mode 100644 index 0000000000..dadda0e831 --- /dev/null +++ b/common/place/detail_place_cfg.h @@ -0,0 +1,36 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 DETAIL_PLACE_CFG_H +#define DETAIL_PLACE_CFG_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct DetailPlaceCfg +{ + DetailPlaceCfg(Context *ctx); + bool timing_driven; + int hpwl_scale_x, hpwl_scale_y; + float crit_exp = 8; +}; + +NEXTPNR_NAMESPACE_END +#endif diff --git a/common/place/detail_place_core.cc b/common/place/detail_place_core.cc new file mode 100644 index 0000000000..bb3836394b --- /dev/null +++ b/common/place/detail_place_core.cc @@ -0,0 +1,457 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 "detail_place_core.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +DetailPlaceCfg::DetailPlaceCfg(Context *ctx) +{ + timing_driven = ctx->setting("timing_driven"); + + hpwl_scale_x = 1; + hpwl_scale_y = 1; +} + +PlacePartition::PlacePartition(Context *ctx) +{ + x0 = ctx->getGridDimX(); + y0 = ctx->getGridDimY(); + x1 = 0; + y1 = 0; + for (auto &cell : ctx->cells) { + Loc l = ctx->getBelLocation(cell.second->bel); + x0 = std::min(x0, l.x); + x1 = std::max(x1, l.x); + y0 = std::min(y0, l.y); + y1 = std::max(y1, l.y); + cells.push_back(cell.second.get()); + } +} + +void PlacePartition::split(Context *ctx, bool yaxis, float pivot, PlacePartition &l, PlacePartition &r) +{ + std::sort(cells.begin(), cells.end(), [&](CellInfo *a, CellInfo *b) { + Loc l0 = ctx->getBelLocation(a->bel), l1 = ctx->getBelLocation(b->bel); + return yaxis ? (l0.y < l1.y) : (l0.x < l1.x); + }); + size_t pivot_point = size_t(cells.size() * pivot); + l.cells.clear(); + r.cells.clear(); + l.cells.reserve(pivot_point); + r.cells.reserve(cells.size() - pivot_point); + int pivot_coord = (pivot_point == 0) ? (yaxis ? y1 : x1) + : (yaxis ? ctx->getBelLocation(cells.at(pivot_point - 1)->bel).y + : ctx->getBelLocation(cells.at(pivot_point - 1)->bel).x); + for (size_t i = 0; i < cells.size(); i++) { + Loc loc = ctx->getBelLocation(cells.at(i)->bel); + ((yaxis ? loc.y : loc.x) <= pivot_coord ? l.cells : r.cells).push_back(cells.at(i)); + } + if (yaxis) { + l.x0 = r.x0 = x0; + l.x1 = r.x1 = x1; + l.y0 = y0; + l.y1 = pivot_coord; + r.y0 = (pivot_coord == y1) ? y1 : (pivot_coord + 1); + r.y1 = y1; + } else { + l.y0 = r.y0 = y0; + l.y1 = r.y1 = y1; + l.x0 = x0; + l.x1 = pivot_coord; + r.x0 = (pivot_coord == x1) ? x1 : (pivot_coord + 1); + r.x1 = x1; + } +} + +void DetailPlacerState::update_global_costs() +{ + last_bounds.resize(flat_nets.size()); + last_tmg_costs.resize(flat_nets.size()); + total_wirelen = 0; + total_timing_cost = 0; + for (size_t i = 0; i < flat_nets.size(); i++) { + NetInfo *ni = flat_nets.at(i); + if (skip_net(ni)) + continue; + last_bounds.at(i) = NetBB::compute(ctx, ni); + total_wirelen += last_bounds.at(i).hpwl(base_cfg); + if (!timing_skip_net(ni)) { + auto &tc = last_tmg_costs.at(i); + tc.resize(ni->users.capacity()); + for (auto usr : ni->users.enumerate()) { + tc.at(usr.index.idx()) = get_timing_cost(ni, usr.index); + total_timing_cost += tc.at(usr.index.idx()); + } + } + } +} + +NetBB NetBB::compute(const Context *ctx, const NetInfo *net, const dict *cell2bel) +{ + NetBB result{}; + if (!net->driver.cell) + return result; + auto bel_loc = [&](const CellInfo *cell) { + BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel; + return ctx->getBelLocation(bel); + }; + result.nx0 = result.nx1 = result.ny0 = result.ny1 = 1; + Loc drv_loc = bel_loc(net->driver.cell); + result.x0 = result.x1 = drv_loc.x; + result.y0 = result.y1 = drv_loc.y; + for (auto &usr : net->users) { + Loc l = bel_loc(usr.cell); + if (l.x == result.x0) + ++result.nx0; // on the edge + else if (l.x < result.x0) { + result.x0 = l.x; // extends the edge + result.nx0 = 1; + } + if (l.x == result.x1) + ++result.nx1; // on the edge + else if (l.x > result.x1) { + result.x1 = l.x; // extends the edge + result.nx1 = 1; + } + if (l.y == result.y0) + ++result.ny0; // on the edge + else if (l.y < result.y0) { + result.y0 = l.y; // extends the edge + result.ny0 = 1; + } + if (l.y == result.y1) + ++result.ny1; // on the edge + else if (l.y > result.y1) { + result.y1 = l.y; // extends the edge + result.ny1 = 1; + } + } + return result; +} + +void DetailPlacerThreadState::set_partition(const PlacePartition &part) +{ + p = part; + thread_nets.clear(); + thread_net_idx.resize(g.flat_nets.size()); + std::fill(thread_net_idx.begin(), thread_net_idx.end(), -1); + // Determine the set of nets that are within the thread; and therefore we care about + for (auto thread_cell : part.cells) { + for (auto &port : thread_cell->ports) { + if (!port.second.net) + continue; + int global_idx = port.second.net->udata; + auto &thread_idx = thread_net_idx.at(global_idx); + // Already added to the set + if (thread_idx != -1) + continue; + thread_idx = thread_nets.size(); + thread_nets.push_back(port.second.net); + } + } + tmg_ignored_nets.clear(); + ignored_nets.clear(); + for (auto tn : thread_nets) { + ignored_nets.push_back(g.skip_net(tn)); + tmg_ignored_nets.push_back(g.timing_skip_net(tn)); + } + // Set up the original cell-bel map for all nets inside the thread + local_cell2bel.clear(); + for (NetInfo *net : thread_nets) { + if (net->driver.cell) + local_cell2bel[net->driver.cell->name] = net->driver.cell->bel; + for (auto &usr : net->users) + local_cell2bel[usr.cell->name] = usr.cell->bel; + } +} + +void DetailPlacerThreadState::setup_initial_state() +{ + // Setup initial net bounding boxes and timing costs + net_bounds.clear(); + arc_tmg_cost.clear(); + for (auto tn : thread_nets) { + net_bounds.push_back(g.last_bounds.at(tn->udata)); + arc_tmg_cost.push_back(g.last_tmg_costs.at(tn->udata)); + } + new_net_bounds = net_bounds; + for (int j = 0; j < 2; j++) { + auto &a = axes.at(j); + a.already_bounds_changed.resize(net_bounds.size()); + } + already_timing_changed.clear(); + already_timing_changed.resize(net_bounds.size()); + for (size_t i = 0; i < thread_nets.size(); i++) + already_timing_changed.at(i) = std::vector(thread_nets.at(i)->users.capacity()); +} + +bool DetailPlacerThreadState::bounds_check(BelId bel) +{ + Loc l = ctx->getBelLocation(bel); + if (l.x < p.x0 || l.x > p.x1 || l.y < p.y0 || l.y > p.y1) + return false; + return true; +} + +bool DetailPlacerThreadState::bind_move() +{ +#if !defined(__wasm) + std::unique_lock l(g.archapi_mutex); +#endif + for (auto &entry : moved_cells) { + ctx->unbindBel(entry.second.first); + } + bool success = true; + for (auto &entry : moved_cells) { + // Make sure targets are available before we bind them + if (!ctx->checkBelAvail(entry.second.second)) { + success = false; + break; + } + ctx->bindBel(entry.second.second, ctx->cells.at(entry.first).get(), STRENGTH_WEAK); + } + arch_state_dirty = true; + return success; +} + +bool DetailPlacerThreadState::check_validity() +{ +#if !defined(__wasm) + std::shared_lock l(g.archapi_mutex); +#endif + bool result = true; + for (auto e : moved_cells) { + if (!ctx->isBelLocationValid(e.second.first)) { + // Have to check old; too; as unbinding a bel could make a placement illegal by virtue of no longer + // enabling dedicated routes to be used + result = false; + break; + } + if (!ctx->isBelLocationValid(e.second.second)) { + result = false; + break; + } + } + return result; +} + +void DetailPlacerThreadState::revert_move() +{ + if (arch_state_dirty) { + // If changes to the arch state were made, revert them by restoring original cell bindings +#if !defined(__wasm) + std::unique_lock l(g.archapi_mutex); +#endif + for (auto &entry : moved_cells) { + BelId curr_bound = ctx->cells.at(entry.first)->bel; + if (curr_bound != BelId()) + ctx->unbindBel(curr_bound); + } + for (auto &entry : moved_cells) { + ctx->bindBel(entry.second.first, ctx->cells.at(entry.first).get(), STRENGTH_WEAK); + } + arch_state_dirty = false; + } + for (auto &entry : moved_cells) + local_cell2bel[entry.first] = entry.second.first; +} + +void DetailPlacerThreadState::commit_move() +{ + arch_state_dirty = false; + for (auto &axis : axes) { + for (auto bc : axis.bounds_changed_nets) { + // Commit updated net bounds + net_bounds.at(bc) = new_net_bounds.at(bc); + } + } + if (g.base_cfg.timing_driven) { + NPNR_ASSERT(timing_changed_arcs.size() == new_timing_costs.size()); + for (size_t i = 0; i < timing_changed_arcs.size(); i++) { + auto arc = timing_changed_arcs.at(i); + arc_tmg_cost.at(arc.first).at(arc.second.idx()) = new_timing_costs.at(i); + } + } +} + +void DetailPlacerThreadState::compute_changes_for_cell(CellInfo *cell, BelId old_bel, BelId new_bel) +{ + Loc new_loc = ctx->getBelLocation(new_bel); + Loc old_loc = ctx->getBelLocation(old_bel); + for (const auto &port : cell->ports) { + NetInfo *pn = port.second.net; + if (!pn) + continue; + int idx = thread_net_idx.at(pn->udata); + if (ignored_nets.at(idx)) + continue; + NetBB &new_bounds = new_net_bounds.at(idx); + // For the x-axis (i=0) and y-axis (i=1) + for (int i = 0; i < 2; i++) { + auto &axis = axes.at(i); + // New and old on this axis + int new_pos = i ? new_loc.y : new_loc.x, old_pos = i ? old_loc.y : old_loc.x; + // References to updated bounding box entries + auto &b0 = i ? new_bounds.y0 : new_bounds.x0; + auto &n0 = i ? new_bounds.ny0 : new_bounds.nx0; + auto &b1 = i ? new_bounds.y1 : new_bounds.x1; + auto &n1 = i ? new_bounds.ny1 : new_bounds.nx1; + auto &change = axis.already_bounds_changed.at(idx); + // Lower bound + if (new_pos < b0) { + // Further out than current lower bound + b0 = new_pos; + n0 = 1; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (new_pos == b0 && old_pos > b0) { + // Moved from inside into current bound + ++n0; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (old_pos == b0 && new_pos > b0) { + // Moved from current bound to inside + if (change == NO_CHANGE) + axis.bounds_changed_nets.push_back(idx); + if (n0 == 1) { + // Was the last cell on the bound; have to do a full recompute + change = FULL_RECOMPUTE; + } else { + --n0; + if (change == NO_CHANGE) + change = CELL_MOVED_INWARDS; + } + } + // Upper bound + if (new_pos > b1) { + // Further out than current upper bound + b1 = new_pos; + n1 = new_pos; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (new_pos == b1 && old_pos < b1) { + // Moved onto current bound + ++n1; + if (change == NO_CHANGE) { + change = CELL_MOVED_OUTWARDS; + axis.bounds_changed_nets.push_back(idx); + } + } else if (old_pos == b1 && new_pos < b1) { + // Moved from current bound to inside + if (change == NO_CHANGE) + axis.bounds_changed_nets.push_back(idx); + if (n1 == 1) { + // Was the last cell on the bound; have to do a full recompute + change = FULL_RECOMPUTE; + } else { + --n1; + if (change == NO_CHANGE) + change = CELL_MOVED_INWARDS; + } + } + } + // Timing updates if timing driven + if (g.base_cfg.timing_driven && !tmg_ignored_nets.at(idx)) { + if (port.second.type == PORT_OUT) { + int cc; + TimingPortClass cls = ctx->getPortTimingClass(cell, port.first, cc); + if (cls != TMG_IGNORE) { + for (auto usr : pn->users.enumerate()) + if (!already_timing_changed.at(idx).at(usr.index.idx())) { + timing_changed_arcs.emplace_back(std::make_pair(idx, usr.index)); + already_timing_changed.at(idx).at(usr.index.idx()) = true; + } + } + } else { + auto usr = port.second.user_idx; + if (!already_timing_changed.at(idx).at(usr.idx())) { + timing_changed_arcs.emplace_back(std::make_pair(idx, usr)); + already_timing_changed.at(idx).at(usr.idx()) = true; + } + } + } + } +} + +void DetailPlacerThreadState::compute_total_change() +{ + auto &xa = axes.at(0), &ya = axes.at(1); + for (auto &bc : xa.bounds_changed_nets) + if (xa.already_bounds_changed.at(bc) == FULL_RECOMPUTE) + new_net_bounds.at(bc) = NetBB::compute(ctx, thread_nets.at(bc), &local_cell2bel); + for (auto &bc : ya.bounds_changed_nets) + if (xa.already_bounds_changed.at(bc) != FULL_RECOMPUTE && ya.already_bounds_changed.at(bc) == FULL_RECOMPUTE) + new_net_bounds.at(bc) = NetBB::compute(ctx, thread_nets.at(bc), &local_cell2bel); + for (auto &bc : xa.bounds_changed_nets) + wirelen_delta += (new_net_bounds.at(bc).hpwl(g.base_cfg) - net_bounds.at(bc).hpwl(g.base_cfg)); + for (auto &bc : ya.bounds_changed_nets) + if (xa.already_bounds_changed.at(bc) == NO_CHANGE) + wirelen_delta += (new_net_bounds.at(bc).hpwl(g.base_cfg) - net_bounds.at(bc).hpwl(g.base_cfg)); + if (g.base_cfg.timing_driven) { + NPNR_ASSERT(new_timing_costs.empty()); + for (auto arc : timing_changed_arcs) { + double new_cost = g.get_timing_cost(thread_nets.at(arc.first), arc.second, &local_cell2bel); + timing_delta += (new_cost - arc_tmg_cost.at(arc.first).at(arc.second.idx())); + new_timing_costs.push_back(new_cost); + } + } +} + +void DetailPlacerThreadState::reset_move_state() +{ + moved_cells.clear(); + cell_rel.clear(); + for (auto &axis : axes) { + for (auto bc : axis.bounds_changed_nets) { + new_net_bounds.at(bc) = net_bounds.at(bc); + axis.already_bounds_changed[bc] = NO_CHANGE; + } + axis.bounds_changed_nets.clear(); + } + for (auto &arc : timing_changed_arcs) { + already_timing_changed.at(arc.first).at(arc.second.idx()) = false; + } + timing_changed_arcs.clear(); + new_timing_costs.clear(); + wirelen_delta = 0; + timing_delta = 0; +} + +bool DetailPlacerThreadState::add_to_move(CellInfo *cell, BelId old_bel, BelId new_bel) +{ + if (!bounds_check(old_bel) || !bounds_check(new_bel)) + return false; + if (!ctx->isValidBelForCellType(cell->type, new_bel)) + return false; + NPNR_ASSERT(!moved_cells.count(cell->name)); + moved_cells[cell->name] = std::make_pair(old_bel, new_bel); + local_cell2bel[cell->name] = new_bel; + compute_changes_for_cell(cell, old_bel, new_bel); + return true; +} + +NEXTPNR_NAMESPACE_END diff --git a/common/place/detail_place_core.h b/common/place/detail_place_core.h new file mode 100644 index 0000000000..1c280f24b7 --- /dev/null +++ b/common/place/detail_place_core.h @@ -0,0 +1,224 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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. + * + */ + +/* +This provides core data structures for a thread-safe detail placer that needs to swap cells and evaluate the cost +changes of swaps. + +It works on a partition-based threading approach; although threading can be avoided by only instantiating one +per-thread structure and calling its methods from the main thread. + +Each thread's data includes its own local net indexing for nets inside the partition (which can overlap thread +boundaries); and its own local cell-to-bel mapping for any cells on those nets, so there are no races with moves +made by other threads. + +A move is an atomic transaction of updated cell to bel mappings inside a thread. The first step is to reset the +per-move structures; then to add all of the moved cells to the move with add_to_move. + +Evaluation of wirelength and timing changes of a move is done with compute_changes_for_cell and compute_total_change. + +bind_move will probationally bind the move using the arch API functions, acquiring a lock during this time to prevent +races on non-thread-safe arch implementations, returning true if the bind succeeded or false if something went wrong +and it should be aborted. check_validity must then be called to use the arch API validity check functions on the move. + +Finally if the move meets criteria and is accepted then commit_move marks it as committed, otherwise revert_move +aborts the entire move transaction. +*/ + +#ifndef DETAIL_PLACE_CORE_H +#define DETAIL_PLACE_CORE_H + +#include "nextpnr.h" + +#include "detail_place_cfg.h" +#include "fast_bels.h" +#include "timing.h" + +#include + +#if !defined(__wasm) +#include +#endif + +NEXTPNR_NAMESPACE_BEGIN + +struct PlacePartition +{ + int x0, y0, x1, y1; + std::vector cells; + PlacePartition() = default; + explicit PlacePartition(Context *ctx); + void split(Context *ctx, bool yaxis, float pivot, PlacePartition &l, PlacePartition &r); +}; + +typedef int64_t wirelen_t; + +struct NetBB +{ + // Actual bounding box + int x0 = 0, x1 = 0, y0 = 0, y1 = 0; + // Number of cells at each extremity + int nx0 = 0, nx1 = 0, ny0 = 0, ny1 = 0; + inline wirelen_t hpwl(const DetailPlaceCfg &cfg) const + { + return wirelen_t(cfg.hpwl_scale_x * (x1 - x0) + cfg.hpwl_scale_y * (y1 - y0)); + } + static NetBB compute(const Context *ctx, const NetInfo *net, const dict *cell2bel = nullptr); +}; + +struct DetailPlacerState +{ + explicit DetailPlacerState(Context *ctx, DetailPlaceCfg &cfg) + : ctx(ctx), base_cfg(cfg), bels(ctx, false, 64), tmg(ctx){}; + Context *ctx; + DetailPlaceCfg &base_cfg; + FastBels bels; + std::vector flat_nets; // flat array of all nets in the design for fast referencing by index + std::vector last_bounds; + std::vector> last_tmg_costs; + dict region_bounds; + TimingAnalyser tmg; + + wirelen_t total_wirelen = 0; + double total_timing_cost = 0; + +#if !defined(__wasm) + std::shared_timed_mutex archapi_mutex; +#endif + + inline double get_timing_cost(const NetInfo *net, store_index user, + const dict *cell2bel = nullptr) + { + if (!net->driver.cell) + return 0; + const auto &sink = net->users.at(user); + IdString driver_pin, sink_pin; + // Pick the first pin for a prediction; assume all will be similar enouhg + for (auto pin : ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port)) { + driver_pin = pin; + break; + } + for (auto pin : ctx->getBelPinsForCellPin(sink.cell, sink.port)) { + sink_pin = pin; + break; + } + float crit = tmg.get_criticality(CellPortKey(sink)); + BelId src_bel = cell2bel ? cell2bel->at(net->driver.cell->name) : net->driver.cell->bel; + BelId dst_bel = cell2bel ? cell2bel->at(sink.cell->name) : sink.cell->bel; + double delay = ctx->getDelayNS(ctx->predictDelay(src_bel, driver_pin, dst_bel, sink_pin)); + return delay * std::pow(crit, base_cfg.crit_exp); + } + + inline bool skip_net(const NetInfo *net) const + { + if (!net->driver.cell) + return true; + if (ctx->getBelGlobalBuf(net->driver.cell->bel)) + return true; + return false; + } + + inline bool timing_skip_net(const NetInfo *net) const + { + if (!net->driver.cell) + return true; + int cc; + auto cls = ctx->getPortTimingClass(net->driver.cell, net->driver.port, cc); + if (cls == TMG_IGNORE || cls == TMG_GEN_CLOCK) + return true; + return false; + } + + void update_global_costs(); +}; + +struct DetailPlacerThreadState +{ + Context *ctx; // Nextpnr context pointer + DetailPlacerState &g; // Placer engine state + int idx; // Index of the thread + DeterministicRNG rng; // Local RNG + // The cell partition that the thread works on + PlacePartition p; + // Mapping from design-wide net index to thread-wide net index -- not all nets are in all partitions, so we can + // optimise + std::vector thread_net_idx; + // List of nets inside the partition; and their committed bounding boxes & timing costs from the thread's + // perspective + std::vector thread_nets; + std::vector net_bounds; + std::vector> arc_tmg_cost; + std::vector ignored_nets, tmg_ignored_nets; + bool arch_state_dirty = false; + // Our local cell-bel map; that won't be affected by out-of-partition moves + dict local_cell2bel; + + // Data on an inflight move + dict> moved_cells; // cell -> (old; new) + // For cluster moves only + std::vector> cell_rel; + // For incremental wirelength and delay updates + wirelen_t wirelen_delta = 0; + double timing_delta = 0; + // Wirelen related are handled on a per-axis basis to reduce + enum BoundChange + { + NO_CHANGE, + CELL_MOVED_INWARDS, + CELL_MOVED_OUTWARDS, + FULL_RECOMPUTE + }; + struct AxisChanges + { + std::vector bounds_changed_nets; + std::vector already_bounds_changed; + }; + std::array axes; + std::vector new_net_bounds; + + std::vector> already_timing_changed; + std::vector>> timing_changed_arcs; + std::vector new_timing_costs; + + DetailPlacerThreadState(Context *ctx, DetailPlacerState &g, int idx) : ctx(ctx), g(g), idx(idx){}; + void set_partition(const PlacePartition &part); + void setup_initial_state(); + bool bounds_check(BelId bel); + + // Reset the inflight move state + void reset_move_state(); + // Add a cell change to the move + bool add_to_move(CellInfo *cell, BelId old_bel, BelId new_bel); + // For an inflight move; attempt to actually apply the changes to the arch API + bool bind_move(); + // Checks if the arch API bel validity for a move is accepted + bool check_validity(); + // Undo any changes relating to an inflight move + void revert_move(); + // Mark the inflight move as complete and update cost structures + void commit_move(); + // Update the inflight cost change structures for a given cell moe + void compute_changes_for_cell(CellInfo *cell, BelId old_bel, BelId new_bel); + // Update the total cost change for an inflight move + void compute_total_change(); +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/common/place/parallel_refine.cc b/common/place/parallel_refine.cc index a868ca582f..a2de5b1365 100644 --- a/common/place/parallel_refine.cc +++ b/common/place/parallel_refine.cc @@ -22,9 +22,8 @@ #if !defined(__wasm) -#include "fast_bels.h" +#include "detail_place_core.h" #include "scope_lock.h" -#include "timing.h" #include #include @@ -35,515 +34,24 @@ NEXTPNR_NAMESPACE_BEGIN namespace { -struct Partition -{ - int x0, y0, x1, y1; - std::vector cells; - Partition() = default; - explicit Partition(Context *ctx) - { - x0 = ctx->getGridDimX(); - y0 = ctx->getGridDimY(); - x1 = 0; - y1 = 0; - for (auto &cell : ctx->cells) { - Loc l = ctx->getBelLocation(cell.second->bel); - x0 = std::min(x0, l.x); - x1 = std::max(x1, l.x); - y0 = std::min(y0, l.y); - y1 = std::max(y1, l.y); - cells.push_back(cell.second.get()); - } - } - void split(Context *ctx, bool yaxis, float pivot, Partition &l, Partition &r) - { - std::sort(cells.begin(), cells.end(), [&](CellInfo *a, CellInfo *b) { - Loc l0 = ctx->getBelLocation(a->bel), l1 = ctx->getBelLocation(b->bel); - return yaxis ? (l0.y < l1.y) : (l0.x < l1.x); - }); - size_t pivot_point = size_t(cells.size() * pivot); - l.cells.clear(); - r.cells.clear(); - l.cells.reserve(pivot_point); - r.cells.reserve(cells.size() - pivot_point); - int pivot_coord = (pivot_point == 0) ? (yaxis ? y1 : x1) - : (yaxis ? ctx->getBelLocation(cells.at(pivot_point - 1)->bel).y - : ctx->getBelLocation(cells.at(pivot_point - 1)->bel).x); - for (size_t i = 0; i < cells.size(); i++) { - Loc loc = ctx->getBelLocation(cells.at(i)->bel); - ((yaxis ? loc.y : loc.x) <= pivot_coord ? l.cells : r.cells).push_back(cells.at(i)); - } - if (yaxis) { - l.x0 = r.x0 = x0; - l.x1 = r.x1 = x1; - l.y0 = y0; - l.y1 = pivot_coord; - r.y0 = (pivot_coord == y1) ? y1 : (pivot_coord + 1); - r.y1 = y1; - } else { - l.y0 = r.y0 = y0; - l.y1 = r.y1 = y1; - l.x0 = x0; - l.x1 = pivot_coord; - r.x0 = (pivot_coord == x1) ? x1 : (pivot_coord + 1); - r.x1 = x1; - } - } -}; -typedef int64_t wirelen_t; - -struct NetBB +struct GlobalState : DetailPlacerState { - // Actual bounding box - int x0 = 0, x1 = 0, y0 = 0, y1 = 0; - // Number of cells at each extremity - int nx0 = 0, nx1 = 0, ny0 = 0, ny1 = 0; - wirelen_t hpwl(const ParallelRefineCfg &cfg) const - { - return wirelen_t(cfg.hpwl_scale_x * (x1 - x0) + cfg.hpwl_scale_y * (y1 - y0)); - } - static NetBB compute(const Context *ctx, const NetInfo *net, const dict *cell2bel = nullptr) - { - NetBB result{}; - if (!net->driver.cell) - return result; - auto bel_loc = [&](const CellInfo *cell) { - BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel; - return ctx->getBelLocation(bel); - }; - result.nx0 = result.nx1 = result.ny0 = result.ny1 = 1; - Loc drv_loc = bel_loc(net->driver.cell); - result.x0 = result.x1 = drv_loc.x; - result.y0 = result.y1 = drv_loc.y; - for (auto &usr : net->users) { - Loc l = bel_loc(usr.cell); - if (l.x == result.x0) - ++result.nx0; // on the edge - else if (l.x < result.x0) { - result.x0 = l.x; // extends the edge - result.nx0 = 1; - } - if (l.x == result.x1) - ++result.nx1; // on the edge - else if (l.x > result.x1) { - result.x1 = l.x; // extends the edge - result.nx1 = 1; - } - if (l.y == result.y0) - ++result.ny0; // on the edge - else if (l.y < result.y0) { - result.y0 = l.y; // extends the edge - result.ny0 = 1; - } - if (l.y == result.y1) - ++result.ny1; // on the edge - else if (l.y > result.y1) { - result.y1 = l.y; // extends the edge - result.ny1 = 1; - } - } - return result; - } -}; + explicit GlobalState(Context *ctx, ParallelRefineCfg cfg) : DetailPlacerState(ctx, this->cfg), cfg(cfg){}; -struct GlobalState -{ - explicit GlobalState(Context *ctx, ParallelRefineCfg cfg) : ctx(ctx), cfg(cfg), bels(ctx, false, 64), tmg(ctx){}; - Context *ctx; ParallelRefineCfg cfg; - FastBels bels; - std::vector flat_nets; // flat array of all nets in the design for fast referencing by index - std::vector last_bounds; - std::vector> last_tmg_costs; - dict region_bounds; - TimingAnalyser tmg; - - std::shared_timed_mutex archapi_mutex; - double temperature = 1e-7; int radius = 3; - - wirelen_t total_wirelen = 0; - double total_timing_cost = 0; - - double get_timing_cost(const NetInfo *net, store_index user, - const dict *cell2bel = nullptr) - { - if (!net->driver.cell) - return 0; - const auto &sink = net->users.at(user); - IdString driver_pin, sink_pin; - // Pick the first pin for a prediction; assume all will be similar enouhg - for (auto pin : ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port)) { - driver_pin = pin; - break; - } - for (auto pin : ctx->getBelPinsForCellPin(sink.cell, sink.port)) { - sink_pin = pin; - break; - } - float crit = tmg.get_criticality(CellPortKey(sink)); - BelId src_bel = cell2bel ? cell2bel->at(net->driver.cell->name) : net->driver.cell->bel; - BelId dst_bel = cell2bel ? cell2bel->at(sink.cell->name) : sink.cell->bel; - double delay = ctx->getDelayNS(ctx->predictDelay(src_bel, driver_pin, dst_bel, sink_pin)); - return delay * std::pow(crit, cfg.crit_exp); - } - - bool skip_net(const NetInfo *net) const - { - if (!net->driver.cell) - return true; - if (ctx->getBelGlobalBuf(net->driver.cell->bel)) - return true; - return false; - } - bool timing_skip_net(const NetInfo *net) const - { - if (!net->driver.cell) - return true; - int cc; - auto cls = ctx->getPortTimingClass(net->driver.cell, net->driver.port, cc); - if (cls == TMG_IGNORE || cls == TMG_GEN_CLOCK) - return true; - return false; - } // .... }; -struct ThreadState +struct ThreadState : DetailPlacerThreadState { - Context *ctx; // Nextpnr context pointer - GlobalState &g; // Refinement engine state - int idx; // Index of the thread - DeterministicRNG rng; // Local RNG - // The cell partition that the thread works on - Partition p; - // Mapping from design-wide net index to thread-wide net index -- not all nets are in all partitions, so we can - // optimise - std::vector thread_net_idx; - // List of nets inside the partition; and their committed bounding boxes & timing costs from the thread's - // perspective - std::vector thread_nets; - std::vector net_bounds; - std::vector> arc_tmg_cost; - std::vector ignored_nets, tmg_ignored_nets; - bool arch_state_dirty = false; - // Our local cell-bel map; that won't be affected by out-of-partition moves - dict local_cell2bel; - - // Data on an inflight move - dict> moved_cells; // cell -> (old; new) - // For cluster moves only - std::vector> cell_rel; - // For incremental wirelength and delay updates - wirelen_t wirelen_delta = 0; - double timing_delta = 0; - + ThreadState(Context *ctx, GlobalState &g, int idx) : DetailPlacerThreadState(ctx, g, idx), g(g){}; // Total made and accepted moved + GlobalState &g; int n_move = 0, n_accept = 0; - enum BoundChange - { - NO_CHANGE, - CELL_MOVED_INWARDS, - CELL_MOVED_OUTWARDS, - FULL_RECOMPUTE - }; - // Wirelen related are handled on a per-axis basis to reduce - struct AxisChanges - { - std::vector bounds_changed_nets; - std::vector already_bounds_changed; - }; - std::array axes; - std::vector new_net_bounds; - - std::vector> already_timing_changed; - std::vector>> timing_changed_arcs; - std::vector new_timing_costs; - - ThreadState(Context *ctx, GlobalState &g, int idx) : ctx(ctx), g(g), idx(idx){}; - void set_partition(const Partition &part) - { - p = part; - thread_nets.clear(); - thread_net_idx.resize(g.flat_nets.size()); - std::fill(thread_net_idx.begin(), thread_net_idx.end(), -1); - // Determine the set of nets that are within the thread; and therefore we care about - for (auto thread_cell : part.cells) { - for (auto &port : thread_cell->ports) { - if (!port.second.net) - continue; - int global_idx = port.second.net->udata; - auto &thread_idx = thread_net_idx.at(global_idx); - // Already added to the set - if (thread_idx != -1) - continue; - thread_idx = thread_nets.size(); - thread_nets.push_back(port.second.net); - } - } - tmg_ignored_nets.clear(); - ignored_nets.clear(); - for (auto tn : thread_nets) { - ignored_nets.push_back(g.skip_net(tn)); - tmg_ignored_nets.push_back(g.timing_skip_net(tn)); - } - // Set up the original cell-bel map for all nets inside the thread - local_cell2bel.clear(); - for (NetInfo *net : thread_nets) { - if (net->driver.cell) - local_cell2bel[net->driver.cell->name] = net->driver.cell->bel; - for (auto &usr : net->users) - local_cell2bel[usr.cell->name] = usr.cell->bel; - } - } - void setup_initial_state() - { - // Setup initial net bounding boxes and timing costs - net_bounds.clear(); - arc_tmg_cost.clear(); - for (auto tn : thread_nets) { - net_bounds.push_back(g.last_bounds.at(tn->udata)); - arc_tmg_cost.push_back(g.last_tmg_costs.at(tn->udata)); - } - new_net_bounds = net_bounds; - for (int j = 0; j < 2; j++) { - auto &a = axes.at(j); - a.already_bounds_changed.resize(net_bounds.size()); - } - already_timing_changed.clear(); - already_timing_changed.resize(net_bounds.size()); - for (size_t i = 0; i < thread_nets.size(); i++) - already_timing_changed.at(i) = std::vector(thread_nets.at(i)->users.capacity()); - } - bool bounds_check(BelId bel) - { - Loc l = ctx->getBelLocation(bel); - if (l.x < p.x0 || l.x > p.x1 || l.y < p.y0 || l.y > p.y1) - return false; - return true; - } - bool bind_move() - { - std::unique_lock l(g.archapi_mutex); - for (auto &entry : moved_cells) { - ctx->unbindBel(entry.second.first); - } - bool success = true; - for (auto &entry : moved_cells) { - // Make sure targets are available before we bind them - if (!ctx->checkBelAvail(entry.second.second)) { - success = false; - break; - } - ctx->bindBel(entry.second.second, ctx->cells.at(entry.first).get(), STRENGTH_WEAK); - } - arch_state_dirty = true; - return success; - } - bool check_validity() - { - std::shared_lock l(g.archapi_mutex); - bool result = true; - for (auto e : moved_cells) { - if (!ctx->isBelLocationValid(e.second.first)) { - // Have to check old; too; as unbinding a bel could make a placement illegal by virtue of no longer - // enabling dedicated routes to be used - result = false; - break; - } - if (!ctx->isBelLocationValid(e.second.second)) { - result = false; - break; - } - } - return result; - } - void revert_move() - { - if (arch_state_dirty) { - // If changes to the arch state were made, revert them by restoring original cell bindings - std::unique_lock l(g.archapi_mutex); - for (auto &entry : moved_cells) { - BelId curr_bound = ctx->cells.at(entry.first)->bel; - if (curr_bound != BelId()) - ctx->unbindBel(curr_bound); - } - for (auto &entry : moved_cells) { - ctx->bindBel(entry.second.first, ctx->cells.at(entry.first).get(), STRENGTH_WEAK); - } - arch_state_dirty = false; - } - for (auto &entry : moved_cells) - local_cell2bel[entry.first] = entry.second.first; - } - void commit_move() - { - arch_state_dirty = false; - for (auto &axis : axes) { - for (auto bc : axis.bounds_changed_nets) { - // Commit updated net bounds - net_bounds.at(bc) = new_net_bounds.at(bc); - } - } - if (g.cfg.timing_driven) { - NPNR_ASSERT(timing_changed_arcs.size() == new_timing_costs.size()); - for (size_t i = 0; i < timing_changed_arcs.size(); i++) { - auto arc = timing_changed_arcs.at(i); - arc_tmg_cost.at(arc.first).at(arc.second.idx()) = new_timing_costs.at(i); - } - } - } - void compute_changes_for_cell(CellInfo *cell, BelId old_bel, BelId new_bel) - { - Loc new_loc = ctx->getBelLocation(new_bel); - Loc old_loc = ctx->getBelLocation(old_bel); - for (const auto &port : cell->ports) { - NetInfo *pn = port.second.net; - if (!pn) - continue; - int idx = thread_net_idx.at(pn->udata); - if (ignored_nets.at(idx)) - continue; - NetBB &new_bounds = new_net_bounds.at(idx); - // For the x-axis (i=0) and y-axis (i=1) - for (int i = 0; i < 2; i++) { - auto &axis = axes.at(i); - // New and old on this axis - int new_pos = i ? new_loc.y : new_loc.x, old_pos = i ? old_loc.y : old_loc.x; - // References to updated bounding box entries - auto &b0 = i ? new_bounds.y0 : new_bounds.x0; - auto &n0 = i ? new_bounds.ny0 : new_bounds.nx0; - auto &b1 = i ? new_bounds.y1 : new_bounds.x1; - auto &n1 = i ? new_bounds.ny1 : new_bounds.nx1; - auto &change = axis.already_bounds_changed.at(idx); - // Lower bound - if (new_pos < b0) { - // Further out than current lower bound - b0 = new_pos; - n0 = 1; - if (change == NO_CHANGE) { - change = CELL_MOVED_OUTWARDS; - axis.bounds_changed_nets.push_back(idx); - } - } else if (new_pos == b0 && old_pos > b0) { - // Moved from inside into current bound - ++n0; - if (change == NO_CHANGE) { - change = CELL_MOVED_OUTWARDS; - axis.bounds_changed_nets.push_back(idx); - } - } else if (old_pos == b0 && new_pos > b0) { - // Moved from current bound to inside - if (change == NO_CHANGE) - axis.bounds_changed_nets.push_back(idx); - if (n0 == 1) { - // Was the last cell on the bound; have to do a full recompute - change = FULL_RECOMPUTE; - } else { - --n0; - if (change == NO_CHANGE) - change = CELL_MOVED_INWARDS; - } - } - // Upper bound - if (new_pos > b1) { - // Further out than current upper bound - b1 = new_pos; - n1 = new_pos; - if (change == NO_CHANGE) { - change = CELL_MOVED_OUTWARDS; - axis.bounds_changed_nets.push_back(idx); - } - } else if (new_pos == b1 && old_pos < b1) { - // Moved onto current bound - ++n1; - if (change == NO_CHANGE) { - change = CELL_MOVED_OUTWARDS; - axis.bounds_changed_nets.push_back(idx); - } - } else if (old_pos == b1 && new_pos < b1) { - // Moved from current bound to inside - if (change == NO_CHANGE) - axis.bounds_changed_nets.push_back(idx); - if (n1 == 1) { - // Was the last cell on the bound; have to do a full recompute - change = FULL_RECOMPUTE; - } else { - --n1; - if (change == NO_CHANGE) - change = CELL_MOVED_INWARDS; - } - } - } - // Timing updates if timing driven - if (g.cfg.timing_driven && !tmg_ignored_nets.at(idx)) { - if (port.second.type == PORT_OUT) { - int cc; - TimingPortClass cls = ctx->getPortTimingClass(cell, port.first, cc); - if (cls != TMG_IGNORE) { - for (auto usr : pn->users.enumerate()) - if (!already_timing_changed.at(idx).at(usr.index.idx())) { - timing_changed_arcs.emplace_back(std::make_pair(idx, usr.index)); - already_timing_changed.at(idx).at(usr.index.idx()) = true; - } - } - } else { - auto usr = port.second.user_idx; - if (!already_timing_changed.at(idx).at(usr.idx())) { - timing_changed_arcs.emplace_back(std::make_pair(idx, usr)); - already_timing_changed.at(idx).at(usr.idx()) = true; - } - } - } - } - } - void compute_total_change() - { - auto &xa = axes.at(0), &ya = axes.at(1); - for (auto &bc : xa.bounds_changed_nets) - if (xa.already_bounds_changed.at(bc) == FULL_RECOMPUTE) - new_net_bounds.at(bc) = NetBB::compute(ctx, thread_nets.at(bc), &local_cell2bel); - for (auto &bc : ya.bounds_changed_nets) - if (xa.already_bounds_changed.at(bc) != FULL_RECOMPUTE && - ya.already_bounds_changed.at(bc) == FULL_RECOMPUTE) - new_net_bounds.at(bc) = NetBB::compute(ctx, thread_nets.at(bc), &local_cell2bel); - for (auto &bc : xa.bounds_changed_nets) - wirelen_delta += (new_net_bounds.at(bc).hpwl(g.cfg) - net_bounds.at(bc).hpwl(g.cfg)); - for (auto &bc : ya.bounds_changed_nets) - if (xa.already_bounds_changed.at(bc) == NO_CHANGE) - wirelen_delta += (new_net_bounds.at(bc).hpwl(g.cfg) - net_bounds.at(bc).hpwl(g.cfg)); - if (g.cfg.timing_driven) { - NPNR_ASSERT(new_timing_costs.empty()); - for (auto arc : timing_changed_arcs) { - double new_cost = g.get_timing_cost(thread_nets.at(arc.first), arc.second, &local_cell2bel); - timing_delta += (new_cost - arc_tmg_cost.at(arc.first).at(arc.second.idx())); - new_timing_costs.push_back(new_cost); - } - } - } - void reset_move_state() - { - moved_cells.clear(); - cell_rel.clear(); - for (auto &axis : axes) { - for (auto bc : axis.bounds_changed_nets) { - new_net_bounds.at(bc) = net_bounds.at(bc); - axis.already_bounds_changed[bc] = NO_CHANGE; - } - axis.bounds_changed_nets.clear(); - } - for (auto &arc : timing_changed_arcs) { - already_timing_changed.at(arc.first).at(arc.second.idx()) = false; - } - timing_changed_arcs.clear(); - new_timing_costs.clear(); - wirelen_delta = 0; - timing_delta = 0; - } - bool accept_move() { static constexpr double epsilon = 1e-20; @@ -553,19 +61,6 @@ struct ThreadState (g.temperature > 1e-8 && (rng.rng() / float(0x3fffffff)) <= std::exp(-delta / g.temperature)); } - bool add_to_move(CellInfo *cell, BelId old_bel, BelId new_bel) - { - if (!bounds_check(old_bel) || !bounds_check(new_bel)) - return false; - if (!ctx->isValidBelForCellType(cell->type, new_bel)) - return false; - NPNR_ASSERT(!moved_cells.count(cell->name)); - moved_cells[cell->name] = std::make_pair(old_bel, new_bel); - local_cell2bel[cell->name] = new_bel; - compute_changes_for_cell(cell, old_bel, new_bel); - return true; - } - bool single_cell_swap(CellInfo *cell, BelId new_bel) { NPNR_ASSERT(moved_cells.empty()); @@ -806,14 +301,14 @@ struct ParallelRefine g.bels.addCellType(cell_type); } }; - std::vector parts; + std::vector parts; void do_partition() { parts.clear(); parts.emplace_back(ctx); bool yaxis = false; while (parts.size() < t.size()) { - std::vector next(parts.size() * 2); + std::vector next(parts.size() * 2); for (size_t i = 0; i < parts.size(); i++) { // Randomly permute pivot every iteration so we get different thread boundaries const float delta = 0.1; @@ -834,28 +329,6 @@ struct ParallelRefine w.join(); } - void update_global_costs() - { - g.last_bounds.resize(g.flat_nets.size()); - g.last_tmg_costs.resize(g.flat_nets.size()); - g.total_wirelen = 0; - g.total_timing_cost = 0; - for (size_t i = 0; i < g.flat_nets.size(); i++) { - NetInfo *ni = g.flat_nets.at(i); - if (g.skip_net(ni)) - continue; - g.last_bounds.at(i) = NetBB::compute(ctx, ni); - g.total_wirelen += g.last_bounds.at(i).hpwl(g.cfg); - if (!g.timing_skip_net(ni)) { - auto &tc = g.last_tmg_costs.at(i); - tc.resize(ni->users.capacity()); - for (auto usr : ni->users.enumerate()) { - tc.at(usr.index.idx()) = g.get_timing_cost(ni, usr.index); - g.total_timing_cost += tc.at(usr.index.idx()); - } - } - } - } void run() { @@ -868,7 +341,7 @@ struct ParallelRefine log_info("Running parallel refinement with %d threads.\n", int(t.size())); int iter = 1; bool done = false; - update_global_costs(); + g.update_global_costs(); double avg_wirelen = g.total_wirelen; wirelen_t min_wirelen = g.total_wirelen; while (true) { @@ -914,7 +387,7 @@ struct ParallelRefine for (auto &w : workers) w.join(); g.tmg.run(); - update_global_costs(); + g.update_global_costs(); iter++; ctx->yield(); } @@ -924,17 +397,14 @@ struct ParallelRefine }; } // namespace -ParallelRefineCfg::ParallelRefineCfg(Context *ctx) +ParallelRefineCfg::ParallelRefineCfg(Context *ctx) : DetailPlaceCfg(ctx) { - timing_driven = ctx->setting("timing_driven"); threads = ctx->setting("threads", 8); // snap to nearest power of two; and minimum thread size int actual_threads = 1; while ((actual_threads * 2) <= threads && (int(ctx->cells.size()) / (actual_threads * 2)) >= min_thread_size) actual_threads *= 2; threads = actual_threads; - hpwl_scale_x = 1; - hpwl_scale_y = 1; } bool parallel_refine(Context *ctx, ParallelRefineCfg cfg) diff --git a/common/place/parallel_refine.h b/common/place/parallel_refine.h index 556317cd19..a6c8b469bf 100644 --- a/common/place/parallel_refine.h +++ b/common/place/parallel_refine.h @@ -20,18 +20,16 @@ #ifndef PARALLEL_REFINE_H #define PARALLEL_REFINE_H +#include "detail_place_cfg.h" #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN -struct ParallelRefineCfg +struct ParallelRefineCfg : DetailPlaceCfg { ParallelRefineCfg(Context *ctx); - bool timing_driven; int threads; - int hpwl_scale_x, hpwl_scale_y; double lambda = 0.5f; - float crit_exp = 8; int inner_iters = 15; int min_thread_size = 500; }; From 19cade3b3b84ac7bfa07aa9eb8f47866dcd52d4e Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 19 Apr 2022 14:28:07 +0100 Subject: [PATCH 134/712] prefine: Do full-tile swaps, too Signed-off-by: gatecat --- common/place/detail_place_core.cc | 2 +- common/place/parallel_refine.cc | 99 +++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/common/place/detail_place_core.cc b/common/place/detail_place_core.cc index bb3836394b..7e629f24e1 100644 --- a/common/place/detail_place_core.cc +++ b/common/place/detail_place_core.cc @@ -348,7 +348,7 @@ void DetailPlacerThreadState::compute_changes_for_cell(CellInfo *cell, BelId old if (new_pos > b1) { // Further out than current upper bound b1 = new_pos; - n1 = new_pos; + n1 = 1; if (change == NO_CHANGE) { change = CELL_MOVED_OUTWARDS; axis.bounds_changed_nets.push_back(idx); diff --git a/common/place/parallel_refine.cc b/common/place/parallel_refine.cc index a2de5b1365..de71b8e093 100644 --- a/common/place/parallel_refine.cc +++ b/common/place/parallel_refine.cc @@ -39,6 +39,8 @@ struct GlobalState : DetailPlacerState { explicit GlobalState(Context *ctx, ParallelRefineCfg cfg) : DetailPlacerState(ctx, this->cfg), cfg(cfg){}; + dict> cluster2cells; + ParallelRefineCfg cfg; double temperature = 1e-7; int radius = 3; @@ -52,6 +54,8 @@ struct ThreadState : DetailPlacerThreadState GlobalState &g; int n_move = 0, n_accept = 0; + dict, std::vector> tile2cell; + bool accept_move() { static constexpr double epsilon = 1e-20; @@ -218,6 +222,97 @@ struct ThreadState : DetailPlacerThreadState } } + bool cluster_inside_tile(ClusterId cluster, int x, int y) + { + for (auto &c : g.cluster2cells.at(cluster)) { + Loc l = ctx->getBelLocation(c->bel); + if (l.x != x || l.y != y) + return false; + } + return true; + } + + bool do_tile_swap(int x, int y, int xn, int yn) + { + if (xn < p.x0 || xn > p.x1) + return false; + if (yn < p.y0 || yn > p.y1) + return false; + if ((x == xn) && (y == yn)) + return false; + + NPNR_ASSERT(moved_cells.empty()); + + auto move_tile = [&](int sx, int sy, int dx, int dy) -> bool { + for (auto c : tile2cell[std::make_pair(sx, sy)]) { + if (c->belStrength > STRENGTH_STRONG || + ((c->cluster != ClusterId()) && !cluster_inside_tile(c->cluster, sx, sy))) + return false; // check clusters before we start moving stuff + } + for (auto c : tile2cell[std::make_pair(sx, sy)]) { + Loc l = ctx->getBelLocation(c->bel); + l.x = dx; + l.y = dy; + BelId new_bel = ctx->getBelByLocation(l); + if (new_bel == BelId() || !ctx->isValidBelForCellType(c->type, new_bel)) + return false; + if (!add_to_move(c, c->bel, new_bel)) + return false; + } + return true; + }; + + if (!move_tile(x, y, xn, yn)) + goto fail; + if (!move_tile(xn, yn, x, y)) + goto fail; + + compute_total_change(); + // SA acceptance criteria + if (!accept_move()) { + // SA fail + goto fail; + } + // Check validity rules + if (!bind_move()) + goto fail; + if (!check_validity()) + goto fail; + // Accepted! + commit_move(); + reset_move_state(); + std::swap(tile2cell[std::make_pair(x, y)], tile2cell[std::make_pair(xn, yn)]); + return true; + fail: + revert_move(); + reset_move_state(); + return false; + } + + void do_tile_swaps() + { + tile2cell.clear(); + for (auto c : p.cells) { + auto loc = ctx->getBelLocation(c->bel); + tile2cell[std::make_pair(loc.x, loc.y)].push_back(c); + } + std::vector> tiles; + for (auto &t : tile2cell) + tiles.push_back(t.first); + rng.shuffle(tiles); + for (auto &t : tiles) { + int x = t.first, y = t.second; + int lx = std::max(x - g.radius, p.x0), rx = std::min(x + g.radius, p.x1); + int by = std::max(y - g.radius, p.y0), ty = std::min(y + g.radius, p.y1); + int xn = lx + ctx->rng((rx - lx) + 1); + int yn = by + ctx->rng((ty - by) + 1); + ++n_move; + if (do_tile_swap(x, y, xn, yn)) { + ++n_accept; + } + } + } + void run_iter() { setup_initial_state(); @@ -246,6 +341,8 @@ struct ThreadState : DetailPlacerThreadState ++n_accept; } } + if ((m % 2) == 0) + do_tile_swaps(); } } }; @@ -295,6 +392,8 @@ struct ParallelRefine for (auto &cell : ctx->cells) { IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); + if (cell.second->cluster != ClusterId()) + g.cluster2cells[cell.second->cluster].push_back(cell.second.get()); } for (auto cell_type : cell_types_in_use) { From a35c80cc10c69f88ceddee604ea29f172fbae1c0 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 20 Apr 2022 11:29:08 +0100 Subject: [PATCH 135/712] ecp5: Tweak delay prediction Signed-off-by: gatecat --- ecp5/arch.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 50993e2b97..d98ee54cee 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -580,7 +580,7 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y); return (120 - 22 * args.speed) * - (6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5))); + (3 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5))); } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const From 20cfafa109c15ef8f12307cf23f595c893f5a2e1 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 27 Apr 2022 14:18:42 +0100 Subject: [PATCH 136/712] generic: Add missing uarch guard Signed-off-by: gatecat --- generic/arch.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generic/arch.cc b/generic/arch.cc index 0aece64fe0..f622fed3bf 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -390,7 +390,8 @@ void Arch::unbindWire(WireId wire) refreshUiPip(pip); } - uarch->notifyWireChange(wire, nullptr); + if (uarch) + uarch->notifyWireChange(wire, nullptr); net_wires.erase(wire); wire_info(wire).bound_net = nullptr; refreshUiWire(wire); From f0d4e4fbc3847635e0f6151a9eda52b7fb952c19 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 2 May 2022 09:56:36 +0100 Subject: [PATCH 137/712] generic: Add some extra helpers for viaduct uarches Signed-off-by: gatecat --- generic/arch.cc | 24 ++++++++++++++++++++++++ generic/arch.h | 4 ++++ generic/viaduct_helpers.cc | 22 ++++++++++++++++++++-- generic/viaduct_helpers.h | 6 ++++-- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/generic/arch.cc b/generic/arch.cc index f622fed3bf..11b5868b15 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -43,6 +43,30 @@ WireId Arch::addWire(IdStringList name, IdString type, int x, int y) return wire; } +WireId Arch::addWireAsBelInput(BelId bel, IdString name) +{ + Loc l = getBelLocation(bel); + WireId w = addWire(IdStringList::concat(getBelName(bel), name), name, l.x, l.y); + addBelInput(bel, name, w); + return w; +} + +WireId Arch::addWireAsBelOutput(BelId bel, IdString name) +{ + Loc l = getBelLocation(bel); + WireId w = addWire(IdStringList::concat(getBelName(bel), name), name, l.x, l.y); + addBelOutput(bel, name, w); + return w; +} + +WireId Arch::addWireAsBelInout(BelId bel, IdString name) +{ + Loc l = getBelLocation(bel); + WireId w = addWire(IdStringList::concat(getBelName(bel), name), name, l.x, l.y); + addBelInout(bel, name, w); + return w; +} + PipId Arch::addPip(IdStringList name, IdString type, WireId srcWire, WireId dstWire, delay_t delay, Loc loc) { NPNR_ASSERT(pip_by_name.count(name) == 0); diff --git a/generic/arch.h b/generic/arch.h index e96853f1de..157ff8afec 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -201,6 +201,10 @@ struct Arch : BaseArch void addBelOutput(BelId bel, IdString name, WireId wire); void addBelInout(BelId bel, IdString name, WireId wire); + WireId addWireAsBelInput(BelId bel, IdString name); + WireId addWireAsBelOutput(BelId bel, IdString name); + WireId addWireAsBelInout(BelId bel, IdString name); + void addGroupBel(IdStringList group, BelId bel); void addGroupWire(IdStringList group, WireId wire); void addGroupPip(IdStringList group, PipId pip); diff --git a/generic/viaduct_helpers.cc b/generic/viaduct_helpers.cc index a92d0de1b2..153d2a0e6a 100644 --- a/generic/viaduct_helpers.cc +++ b/generic/viaduct_helpers.cc @@ -25,9 +25,9 @@ NEXTPNR_NAMESPACE_BEGIN -void ViaductHelpers::resize_ids(int x, int y) +void ViaductHelpers::resize_ids(int x, int y, int z) { - NPNR_ASSERT(x >= 0 && y >= 0 && x <= 20000 && y <= 20000); + NPNR_ASSERT(x >= 0 && y >= 0 && x <= 20000 && y <= 20000 && z <= 1000); while (int(x_ids.size()) <= x) { IdString next = ctx->id(stringf("X%d", int(x_ids.size()))); x_ids.push_back(next); @@ -36,6 +36,10 @@ void ViaductHelpers::resize_ids(int x, int y) IdString next = ctx->id(stringf("Y%d", int(y_ids.size()))); y_ids.push_back(next); } + while (int(z_ids.size()) <= y) { + IdString next = ctx->id(stringf("Z%d", int(z_ids.size()))); + z_ids.push_back(next); + } } IdStringList ViaductHelpers::xy_id(int x, int y, IdString base) @@ -52,6 +56,20 @@ IdStringList ViaductHelpers::xy_id(int x, int y, IdStringList base) return IdStringList::concat(IdStringList(prefix), base); } +IdStringList ViaductHelpers::xyz_id(int x, int y, int z, IdString base) +{ + resize_ids(x, y, z); + std::array result{x_ids.at(x), y_ids.at(y), z_ids.at(z), base}; + return IdStringList(result); +} + +IdStringList ViaductHelpers::xyz_id(int x, int y, int z, IdStringList base) +{ + resize_ids(x, y, z); + std::array prefix{x_ids.at(x), y_ids.at(y), z_ids.at(z)}; + return IdStringList::concat(IdStringList(prefix), base); +} + void ViaductHelpers::remove_nextpnr_iobs(const pool &top_ports) { std::vector to_remove; diff --git a/generic/viaduct_helpers.h b/generic/viaduct_helpers.h index cac2230421..213995dba1 100644 --- a/generic/viaduct_helpers.h +++ b/generic/viaduct_helpers.h @@ -58,13 +58,15 @@ struct ViaductHelpers Context *ctx; void init(Context *ctx) { this->ctx = ctx; } // IdStringList components for x and y locations - std::vector x_ids, y_ids; - void resize_ids(int x, int y); + std::vector x_ids, y_ids, z_ids; + void resize_ids(int x, int y, int z = 0); // Get an IdStringList for a hierarchical ID // Because this uses an IdStringList with seperate X and Y components; this will be much more efficient than // creating unique strings for each object in each X and Y position IdStringList xy_id(int x, int y, IdString base); IdStringList xy_id(int x, int y, IdStringList base); + IdStringList xyz_id(int x, int y, int z, IdString base); + IdStringList xyz_id(int x, int y, int z, IdStringList base); // Common packing functions // Remove nextpnr-inserted IO buffers; where IO buffer insertion is done in synthesis // expects a set of top-level port types From 15413de3597404d3a68cc95a4b6170004a098ff7 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 2 May 2022 20:40:33 +1000 Subject: [PATCH 138/712] gowin: Add initial syntax support for long wires Only the recognition of the directive in the .CST file and elementary checks are added, but not the long-wire mechanism itself. Signed-off-by: YRabbit --- gowin/arch.cc | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 7bfef36e8e..4fc2cd4321 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -691,13 +691,15 @@ void Arch::read_cst(std::istream &in) std::regex port_attrre = std::regex("([^ =;]+=[^ =;]+) *([^;]*;)"); std::regex iobelre = std::regex("IO([TRBL])([0-9]+)\\[?([A-Z])\\]?"); std::regex inslocre = std::regex("INS_LOC +\"([^\"]+)\" +R([0-9]+)C([0-9]+)\\[([0-9])\\]\\[([AB])\\] *;.*"); + std::regex clockre = std::regex("CLOCK_LOC +\"([^\"]+)\" +BUF([GS])[^;]*;"); std::smatch match, match_attr, match_pinloc; std::string line, pinline; enum { ioloc, ioport, - insloc + insloc, + clock } cst_type; settings.erase(id_cst); @@ -708,24 +710,42 @@ void Arch::read_cst(std::istream &in) if (std::regex_match(line, match, portre)) { cst_type = ioport; } else { - if (std::regex_match(line, match, inslocre)) { - cst_type = insloc; + if (std::regex_match(line, match, clockre)) { + cst_type = clock; } else { - if ((!line.empty()) && (line.rfind("//", 0) == std::string::npos)) { - log_warning("Invalid constraint: %s\n", line.c_str()); + if (std::regex_match(line, match, inslocre)) { + cst_type = insloc; + } else { + if ((!line.empty()) && (line.rfind("//", 0) == std::string::npos)) { + log_warning("Invalid constraint: %s\n", line.c_str()); + } + continue; } - continue; } } } IdString net = id(match[1]); auto it = cells.find(net); - if (it == cells.end()) { + if (cst_type != clock && it == cells.end()) { log_info("Cell %s not found\n", net.c_str(this)); continue; } switch (cst_type) { + case clock: { // CLOCK name BUFG|S + std::string which_clock = match[2]; + if (which_clock.at(0) == 'S') { + auto ni = nets.find(net); + if (ni == nets.end()) { + log_info("Net %s not found\n", net.c_str(this)); + continue; + } + log_info("Long wires are not implemented. The %s network will use normal routing.\n", net.c_str(this)); + } else { + log_info("BUFG isn't supported\n"); + continue; + } + } break; case ioloc: { // IO_LOC name pin IdString pinname = id(match[2]); pinline = match[2]; From 27966f101f52415b671cc794b3926a0fc9e667d7 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 8 May 2022 12:44:03 +0100 Subject: [PATCH 139/712] ice40: Fix propagation of constraints through SB_GB Signed-off-by: gatecat --- ice40/pack.cc | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/ice40/pack.cc b/ice40/pack.cc index 92297e8edd..9eff053e65 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -671,13 +671,6 @@ static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen for (auto &user : keep_users) user.cell->ports[user.port].user_idx = net->users.add(user); - if (net->clkconstr) { - glbnet->clkconstr = std::unique_ptr(new ClockConstraint()); - glbnet->clkconstr->low = net->clkconstr->low; - glbnet->clkconstr->high = net->clkconstr->high; - glbnet->clkconstr->period = net->clkconstr->period; - } - ctx->cells[gb->name] = std::move(gb); } @@ -803,6 +796,29 @@ static void promote_globals(Context *ctx) } } +static void copy_gb_constraints(Context *ctx) +{ + // Copy constraints through GBs and PLLs + bool did_something = false; + do { + did_something = false; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (!is_gbuf(ctx, ci)) + continue; + NetInfo *in = ci->getPort(id_USER_SIGNAL_TO_GLOBAL_BUFFER); + NetInfo *out = ci->getPort(id_GLOBAL_BUFFER_OUTPUT); + if (in && out && in->clkconstr && !out->clkconstr) { + out->clkconstr = std::unique_ptr(new ClockConstraint()); + out->clkconstr->low = in->clkconstr->low; + out->clkconstr->high = in->clkconstr->high; + out->clkconstr->period = in->clkconstr->period; + did_something = true; + } + } + } while (did_something); +} + // Figure out where to place PLLs static void place_plls(Context *ctx) { @@ -1711,6 +1727,7 @@ bool Arch::pack() pack_plls(ctx); if (!bool_or_default(ctx->settings, id_no_promote_globals, false)) promote_globals(ctx); + copy_gb_constraints(ctx); ctx->assignArchInfo(); constrain_chains(ctx); ctx->fixupHierarchy(); From 1aa693732c079eab2f3e49d734aef65d73bfe3d0 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 10 May 2022 21:19:02 +1000 Subject: [PATCH 140/712] common: Correct a minor typo in the message Signed-off-by: YRabbit --- common/kernel/nextpnr_types.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/kernel/nextpnr_types.cc b/common/kernel/nextpnr_types.cc index 57d816c008..82725d6ff5 100644 --- a/common/kernel/nextpnr_types.cc +++ b/common/kernel/nextpnr_types.cc @@ -68,7 +68,7 @@ void CellInfo::connectPort(IdString port_name, NetInfo *net) user.port = port_name; port.user_idx = net->users.add(user); } else { - NPNR_ASSERT_FALSE("invalid port type for connect_port"); + NPNR_ASSERT_FALSE("invalid port type for connectPort"); } } From aafe1a176c821a3919e773b57ac08fb348c91597 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Thu, 28 Apr 2022 17:18:26 +0200 Subject: [PATCH 141/712] Generalized representation of unused LUT pins connections Signed-off-by: Maciej Kurc --- fpga_interchange/arch.cc | 43 ++++++++++-- fpga_interchange/luts.cc | 82 ++++++++++++---------- fpga_interchange/luts.h | 13 +++- fpga_interchange/site_arch.cc | 76 +++++++++++++++++--- fpga_interchange/site_lut_mapping_cache.cc | 4 +- 5 files changed, 160 insertions(+), 58 deletions(-) diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index e55c94afe9..05b50ae270 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -874,17 +874,26 @@ static void prepare_sites_for_routing(Context *ctx) // Fixup LUT vcc pins. IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); + IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); + + IdString const_net_name(ctx->chip_info->constants->best_constant_net); + NPNR_ASSERT(const_net_name == vcc_net_name || const_net_name == gnd_net_name); + for (BelId bel : ctx->getBels()) { CellInfo *cell = ctx->getBoundBelCell(bel); if (cell == nullptr) { continue; } - if (cell->lut_cell.vcc_pins.empty()) { - continue; - } + for (const auto &it : cell->lut_cell.pin_connections) { + const auto &bel_pin = it.first; + const auto &conn = it.second; + + // Connected to an active signal or unconnected + if (conn == LutCell::PinConnection::Signal || conn == LutCell::PinConnection::Unconnected) { + continue; + } - for (auto bel_pin : cell->lut_cell.vcc_pins) { // We can't rely on bel pins not clashing with cell names (for Xilinx they use different naming schemes, for // Nexus they are the same) so add a prefix to the bel pin name to disambiguate it IdString cell_pin = ctx->id(stringf("%s_PHYS", ctx->nameOf(bel_pin))); @@ -896,17 +905,37 @@ static void prepare_sites_for_routing(Context *ctx) #ifdef DEBUG_LUT_MAPPING if (ctx->verbose) { - log_info("%s must be tied to VCC, tying now\n", ctx->nameOfWire(lut_pin_wire)); + log_info("%s must be tied to %s, tying now\n", ctx->nameOfWire(lut_pin_wire), + LutCell::nameOfPinConnection(conn).c_str()); } #endif + IdString tie_net_name; + switch (conn) { + case LutCell::PinConnection::Vcc: + tie_net_name = vcc_net_name; + break; + case LutCell::PinConnection::Gnd: + tie_net_name = gnd_net_name; + break; + case LutCell::PinConnection::Const: + tie_net_name = const_net_name; + break; + default: + // Should never happen + NPNR_ASSERT_FALSE( + stringf("Invalid LUT cell pin connection '%s'", LutCell::nameOfPinConnection(conn).c_str()) + .c_str()); + break; + } + auto result = cell->ports.emplace(cell_pin, port_info); if (result.second) { cell->cell_bel_pins[cell_pin].push_back(bel_pin); - ctx->connectPort(vcc_net_name, cell->name, cell_pin); + ctx->connectPort(tie_net_name, cell->name, cell_pin); cell->const_ports.emplace(cell_pin); } else { - NPNR_ASSERT(result.first->second.net == ctx->getNetByAlias(vcc_net_name)); + NPNR_ASSERT(result.first->second.net == ctx->getNetByAlias(tie_net_name)); auto result2 = cell->cell_bel_pins.emplace(cell_pin, std::vector({bel_pin})); NPNR_ASSERT(result2.first->second.at(0) == bel_pin); NPNR_ASSERT(result2.first->second.size() == 1); diff --git a/fpga_interchange/luts.cc b/fpga_interchange/luts.cc index d9e17ca94c..8c4672c7ad 100644 --- a/fpga_interchange/luts.cc +++ b/fpga_interchange/luts.cc @@ -47,14 +47,6 @@ bool rotate_and_merge_lut_equation(std::vector *result, const LutBel // This address line is 0, so don't translate this bit to the cell // address. if ((bel_address & (1 << bel_pin_idx)) == 0) { - // This pin is unused, so the line will be tied high, this - // address is unreachable. - // - // FIXME: The assumption is that unused pins are tied VCC. - // This is not generally true. - // - // Use Arch::prefered_constant_net_type to determine what - // constant net should be used for unused pins. if ((used_pins & (1 << bel_pin_idx)) == 0) { address_reachable = false; break; @@ -132,6 +124,26 @@ struct LutPin bool operator<(const LutPin &other) const { return max_pin < other.max_pin; } }; +const std::string LutCell::nameOfPinConnection(LutCell::PinConnection conn) +{ + switch (conn) { + case PinConnection::Unconnected: + return std::string("unconnected"); + case PinConnection::Gnd: + return std::string("Gnd"); + case PinConnection::Vcc: + return std::string("Vcc"); + case PinConnection::Const: + return std::string("Const"); + case PinConnection::Signal: + return std::string("Signal"); + default: + // Should never happen + NPNR_ASSERT_FALSE("Invalid value of LutCell::PinConnection"); + return std::string(); + } +} + uint32_t LutMapper::check_wires(const Context *ctx) const { // Unlike the 3 argument version of check_wires, this version needs to @@ -184,12 +196,7 @@ uint32_t LutMapper::check_wires(const std::vector> &bel_to_ } } - // FIXME: The assumption is that unused pins are tied VCC. - // This is not generally true. - // - // Use Arch::prefered_constant_net_type to determine what - // constant net should be used for unused pins. - uint32_t vcc_mask = 0; + uint32_t pin_mask = 0; DynamicBitarray<> wire_equation; wire_equation.resize(2); @@ -248,11 +255,11 @@ uint32_t LutMapper::check_wires(const std::vector> &bel_to_ bool good_for_wire = valid_pin_for_wire && !invalid_pin_for_wire; if (!good_for_wire) { - vcc_mask |= (1 << pin_idx); + pin_mask |= (1 << pin_idx); } } - return vcc_mask; + return pin_mask; } bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping, @@ -381,26 +388,18 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping } // Not all LUT inputs are used - uint32_t vcc_pins = 0; + uint32_t pin_mask = 0; if (cells.size() != element.lut_bels.size()) { - // Look to see if wires can be run from element inputs to unused - // outputs. If not, block the BEL pin by tying to VCC. - // - // FIXME: The assumption is that unused pins are tied VCC. - // This is not generally true. - // - // Use Arch::prefered_constant_net_type to determine what - // constant net should be used for unused pins. - vcc_pins = check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, blocked_luts); + pin_mask = check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, blocked_luts); + } + #if defined(DEBUG_LUT_ROTATION) - log_info("vcc_pins = 0x%x", vcc_pins); - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - CellInfo *cell = cells[cell_idx]; - log(", %s => %s", ctx->nameOfBel(cell->bel), cell->name.c_str(ctx)); - } - log("\n"); -#endif + log_info("Cell bindings:\n"); + for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { + CellInfo *cell = cells[cell_idx]; + log_info(" - %s => %s\n", ctx->nameOfBel(cell->bel), cell->name.c_str(ctx)); } +#endif // Fill in the LUT mapping result @@ -422,28 +421,35 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping cell.belPins[cellPin] = belPin; } - cell.lutCell.vcc_pins.clear(); + cell.lutCell.pin_connections.clear(); // All LUT inputs used if (cells.size() == element.lut_bels.size()) { for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) { if ((used_pins & (1 << bel_pin_idx)) == 0) { NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); - cell.lutCell.vcc_pins.emplace(lutBel.pins.at(bel_pin_idx)); + cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Vcc); } } } // Only some LUT inputs used else { for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) { - if ((vcc_pins & (1 << bel_pin_idx)) != 0) { + if ((pin_mask & (1 << bel_pin_idx)) != 0) { NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); auto pin = lutBel.pins.at(bel_pin_idx); - cell.lutCell.vcc_pins.emplace(pin); + cell.lutCell.pin_connections.emplace(pin, LutCell::PinConnection::Vcc); } } } +#if defined(DEBUG_LUT_ROTATION) + log_info("Pin connections for LUT cell %s:\n", cellInfo->name.c_str(ctx)); + for (const auto &it : cell.lutCell.pin_connections) { + log_info(" - %s : %s\n", it.first.c_str(ctx), LutCell::nameOfPinConnection(it.second).c_str()); + } +#endif + lut_mapping->cells.push_back(cell); } @@ -456,7 +462,7 @@ void check_equation(const LutCell &lut_cell, const dict &cel std::vector pin_map; pin_map.resize(lut_bel.pins.size(), -1); - NPNR_ASSERT(lut_cell.pins.size() < std::numeric_limits::max()); + NPNR_ASSERT(lut_cell.pins.size() < (size_t)std::numeric_limits::max()); for (size_t cell_pin_idx = 0; cell_pin_idx < lut_cell.pins.size(); ++cell_pin_idx) { IdString cell_pin = lut_cell.pins[cell_pin_idx]; diff --git a/fpga_interchange/luts.h b/fpga_interchange/luts.h index 8f33507ae7..892f457c19 100644 --- a/fpga_interchange/luts.h +++ b/fpga_interchange/luts.h @@ -42,11 +42,22 @@ enum LogicLevel struct LutCell { + enum class PinConnection + { + Unconnected, + Gnd, + Vcc, + Const, + Signal + }; + // LUT cell pins for equation, LSB first. std::vector pins; pool lut_pins; - pool vcc_pins; + dict pin_connections; DynamicBitarray<> equation; + + static const std::string nameOfPinConnection(PinConnection conn); }; struct LutBel diff --git a/fpga_interchange/site_arch.cc b/fpga_interchange/site_arch.cc index f78e8af44f..bb9c9a11cb 100644 --- a/fpga_interchange/site_arch.cc +++ b/fpga_interchange/site_arch.cc @@ -133,8 +133,15 @@ SiteArch::SiteArch(const SiteInformation *site_info) } // Create list of out of site sources and sinks. - bool have_vcc_pins = false; + bool have_gnd_pins = false; + + IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); + IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); + + IdString const_net_name(ctx->chip_info->constants->best_constant_net); + NPNR_ASSERT(const_net_name == vcc_net_name || const_net_name == gnd_net_name); + for (CellInfo *cell : site_info->cells_in_site) { for (const auto &pin_pair : cell->cell_bel_pins) { if (!cell->ports.count(pin_pair.first)) @@ -145,8 +152,18 @@ SiteArch::SiteArch(const SiteInformation *site_info) } } - if (!cell->lut_cell.vcc_pins.empty()) { - have_vcc_pins = true; + for (const auto &conn : cell->lut_cell.pin_connections) { + if (conn.second == LutCell::PinConnection::Vcc) { + have_vcc_pins = true; + } else if (conn.second == LutCell::PinConnection::Gnd) { + have_gnd_pins = true; + } else if (conn.second == LutCell::PinConnection::Const) { + if (const_net_name == vcc_net_name) { + have_vcc_pins = true; + } else if (const_net_name == gnd_net_name) { + have_gnd_pins = true; + } + } } } @@ -239,10 +256,9 @@ SiteArch::SiteArch(const SiteInformation *site_info) } } - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get(); - auto iter = nets.find(vcc_net); - if (iter == nets.end() && have_vcc_pins) { + auto vcc_iter = nets.find(vcc_net); + if (vcc_iter == nets.end() && have_vcc_pins) { // VCC net isn't present, add it. SiteNetInfo net_info; net_info.net = vcc_net; @@ -250,13 +266,53 @@ SiteArch::SiteArch(const SiteInformation *site_info) net_info.driver.net = vcc_net; auto result = nets.emplace(vcc_net, net_info); NPNR_ASSERT(result.second); - iter = result.first; + vcc_iter = result.first; + } + + NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get(); + auto gnd_iter = nets.find(gnd_net); + if (gnd_iter == nets.end() && have_gnd_pins) { + // GND net isn't present, add it. + SiteNetInfo net_info; + net_info.net = gnd_net; + net_info.driver.type = SiteWire::OUT_OF_SITE_SOURCE; + net_info.driver.net = gnd_net; + auto result = nets.emplace(gnd_net, net_info); + NPNR_ASSERT(result.second); + gnd_iter = result.first; } for (CellInfo *cell : site_info->cells_in_site) { - for (IdString vcc_pin : cell->lut_cell.vcc_pins) { - SiteWire wire = getBelPinWire(cell->bel, vcc_pin); - iter->second.users.emplace(wire); + for (const auto &it : cell->lut_cell.pin_connections) { + const auto &pin = it.first; + const auto &conn = it.second; + + if (conn == LutCell::PinConnection::Unconnected || conn == LutCell::PinConnection::Signal) { + continue; + } + + if (conn == LutCell::PinConnection::Vcc) { + SiteWire wire = getBelPinWire(cell->bel, pin); + vcc_iter->second.users.emplace(wire); + } else if (conn == LutCell::PinConnection::Gnd) { + SiteWire wire = getBelPinWire(cell->bel, pin); + gnd_iter->second.users.emplace(wire); + } else if (conn == LutCell::PinConnection::Const) { + SiteWire wire = getBelPinWire(cell->bel, pin); + if (const_net_name == vcc_net_name) { + vcc_iter->second.users.emplace(wire); + } + if (const_net_name == gnd_net_name) { + gnd_iter->second.users.emplace(wire); + } + } + +#ifdef DEBUG_LUT_MAPPING + if (ctx->verbose) { + log_info("Tying %s.%s to %s\n", cell->name.c_str(ctx), pin.c_str(ctx), + LutCell::nameOfPinConnection(conn).c_str()); + } +#endif } } diff --git a/fpga_interchange/site_lut_mapping_cache.cc b/fpga_interchange/site_lut_mapping_cache.cc index b7a71397c9..82832ed9e8 100644 --- a/fpga_interchange/site_lut_mapping_cache.cc +++ b/fpga_interchange/site_lut_mapping_cache.cc @@ -138,8 +138,8 @@ bool SiteLutMappingResult::apply(const SiteInformation &siteInfo) } // LUT data - // FIXME: Is there any other info that is being updated than vcc_pins ? - cellInfo->lut_cell.vcc_pins = std::move(cell.lutCell.vcc_pins); + // FIXME: Is there any other info that is being updated than pin_connections ? + cellInfo->lut_cell.pin_connections = std::move(cell.lutCell.pin_connections); } return true; From 7c7a4f095921f4f6dbec27a73862e5d26ffb230e Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 11 May 2022 15:50:18 +0200 Subject: [PATCH 142/712] Added tying unused LUT pins to preferred constant instead of Vcc Signed-off-by: Maciej Kurc --- fpga_interchange/luts.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fpga_interchange/luts.cc b/fpga_interchange/luts.cc index 8c4672c7ad..03c01803c0 100644 --- a/fpga_interchange/luts.cc +++ b/fpga_interchange/luts.cc @@ -428,7 +428,10 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) { if ((used_pins & (1 << bel_pin_idx)) == 0) { NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); - cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Vcc); + cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Const); + } + else { + cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal); } } } @@ -438,7 +441,10 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping if ((pin_mask & (1 << bel_pin_idx)) != 0) { NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); auto pin = lutBel.pins.at(bel_pin_idx); - cell.lutCell.pin_connections.emplace(pin, LutCell::PinConnection::Vcc); + cell.lutCell.pin_connections.emplace(pin, LutCell::PinConnection::Const); + } + else { + cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal); } } } From d75c45c63f444af0349463346101c9d7d7ae6283 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Thu, 12 May 2022 11:55:16 +0200 Subject: [PATCH 143/712] Added fallback to VCC as the preferred constant if the architecture does not specify one. Signed-off-by: Maciej Kurc --- fpga_interchange/arch.cc | 13 ++++++++++++- fpga_interchange/luts.cc | 6 ++---- fpga_interchange/site_arch.cc | 7 ++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 05b50ae270..6a7c4fe137 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -301,6 +301,12 @@ void Arch::init() for (size_t tile_type = 0; tile_type < chip_info->tile_types.size(); ++tile_type) { pseudo_pip_data.init_tile_type(getCtx(), tile_type); } + + // Warn if there is no preferred constant net defined in the architecture + IdString const_net_name(getCtx()->chip_info->constants->best_constant_net); + if (const_net_name == IdString()) { + log_warning("The architecture does not specify preferred constant net. Using VCC as default.\n"); + } } // ----------------------------------------------------------------------- @@ -877,7 +883,12 @@ static void prepare_sites_for_routing(Context *ctx) IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); IdString const_net_name(ctx->chip_info->constants->best_constant_net); - NPNR_ASSERT(const_net_name == vcc_net_name || const_net_name == gnd_net_name); + NPNR_ASSERT(const_net_name == IdString() || const_net_name == vcc_net_name || const_net_name == gnd_net_name); + + // FIXME: Use VCC if the architecture does not device the best constant + if (const_net_name == IdString()) { + const_net_name = vcc_net_name; + } for (BelId bel : ctx->getBels()) { CellInfo *cell = ctx->getBoundBelCell(bel); diff --git a/fpga_interchange/luts.cc b/fpga_interchange/luts.cc index 03c01803c0..2a84725316 100644 --- a/fpga_interchange/luts.cc +++ b/fpga_interchange/luts.cc @@ -429,8 +429,7 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping if ((used_pins & (1 << bel_pin_idx)) == 0) { NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Const); - } - else { + } else { cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal); } } @@ -442,8 +441,7 @@ bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); auto pin = lutBel.pins.at(bel_pin_idx); cell.lutCell.pin_connections.emplace(pin, LutCell::PinConnection::Const); - } - else { + } else { cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal); } } diff --git a/fpga_interchange/site_arch.cc b/fpga_interchange/site_arch.cc index bb9c9a11cb..d0a5c48c2e 100644 --- a/fpga_interchange/site_arch.cc +++ b/fpga_interchange/site_arch.cc @@ -140,7 +140,12 @@ SiteArch::SiteArch(const SiteInformation *site_info) IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); IdString const_net_name(ctx->chip_info->constants->best_constant_net); - NPNR_ASSERT(const_net_name == vcc_net_name || const_net_name == gnd_net_name); + NPNR_ASSERT(const_net_name == IdString() || const_net_name == vcc_net_name || const_net_name == gnd_net_name); + + // FIXME: Use VCC if the architecture does not device the best constant + if (const_net_name == IdString()) { + const_net_name = vcc_net_name; + } for (CellInfo *cell : site_info->cells_in_site) { for (const auto &pin_pair : cell->cell_bel_pins) { From ae67c98f111be066ee806598b8b605dede0688d5 Mon Sep 17 00:00:00 2001 From: Lofty Date: Sat, 21 May 2022 18:26:36 +0100 Subject: [PATCH 144/712] Bump minimum CMake to 3.13 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3120cf402b..e000144784 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.13) project(nextpnr CXX C) # Allow family.cmake add additional dependencies to gui_${family}. From 447b5b905cb274f3abbad64812026240d7044c37 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 22 May 2022 13:56:36 +0100 Subject: [PATCH 145/712] Don't assert on mixed domain paths in report Signed-off-by: gatecat --- common/kernel/report.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/common/kernel/report.cc b/common/kernel/report.cc index 98ff14fb77..8f583b33e2 100644 --- a/common/kernel/report.cc +++ b/common/kernel/report.cc @@ -131,11 +131,6 @@ static Json::array report_detailed_net_timings(const Context *ctx) Json::array endpointsJson; for (const auto &sink_timing : it.second) { - - // FIXME: Is it possible that there are multiple different start - // events for a single net? It has a single driver - NPNR_ASSERT(sink_timing.clock_pair.start == start); - auto endpointJson = Json::object({{"cell", sink_timing.cell_port.first.c_str(ctx)}, {"port", sink_timing.cell_port.second.c_str(ctx)}, {"event", clock_event_name(ctx, sink_timing.clock_pair.end)}, From 8c4e3e91ccd7fc5c994f05e67a4605290d72079e Mon Sep 17 00:00:00 2001 From: Maciej Dudek Date: Fri, 27 May 2022 10:58:24 +0200 Subject: [PATCH 146/712] Change write_dcc to work with tilegroups from prjoxide Signed-off-by: Maciej Dudek --- nexus/fasm.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 3ed5785eab..de03fb8226 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -533,11 +533,10 @@ struct NexusFasmWriter void write_dcc(const CellInfo *cell) { BelId bel = cell->bel; - push_tile(bel.tile); - push_belname(bel); + push_bel(bel); write_bit("DCCEN.1"); // Explicit DCC cell implies a clock buffer write_cell_muxes(cell); - pop(2); + pop(); } // Write config for DCS void write_dcs(const CellInfo *cell) From db696af2fe53ab4dc341a09303bb6cd371d76519 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 27 May 2022 22:44:21 +1000 Subject: [PATCH 147/712] gowin: Add support for long wires Gowin chips have a highly sophisticated system of long wires that are wired to each cell and allow the clock or logic to spread quickly. This commit implements some of the capabilities of the long wire system for quadrants, leaving out the fine-tuning of them for each column. To make use of the long wire system, the specified wire is cut at the driver and a special cell is placed between the driver and the rest of the wire. * VCC and GND can not use long wires because they are in every cell and there is no point in using a net * Long wire numbers can be specified manually or assigned automatically. * The route from the driver to the port of the new cell can be quite long, this will have to be solved somehow. * It might make sense to add a mechanism for automatically finding candidates for long wires. Signed-off-by: YRabbit --- gowin/arch.cc | 206 ++++++++++++++++++++++++++++++++++++++++++++- gowin/arch.h | 10 ++- gowin/cells.cc | 3 + gowin/constids.inc | 80 ++++++++++++++++++ gowin/pack.cc | 8 +- 5 files changed, 299 insertions(+), 8 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 4fc2cd4321..a991879ebc 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -175,6 +176,145 @@ DecalXY Arch::getWireDecal(WireId wire) const return wires.at(wire).decalxy_active; } +bool Arch::allocate_longwire(NetInfo *ni, int lw_idx) +{ + NPNR_ASSERT(ni != nullptr); + if (ni->driver.cell == nullptr) { + return false; + } + if (ni->name == id("$PACKER_VCC_NET") || ni->name == id("$PACKER_GND_NET")) { + return false; + } + // So far only for OBUF + switch (ni->driver.cell->type.index) { + case ID_ODDR: /* fall-through*/ + case ID_ODDRC: /* fall-through*/ + case ID_IOBUF: /* fall-through*/ + case ID_TBUF: + return false; + case ID_OBUF: + if (getCtx()->debug) { + log_info("Long wire for IO %s\n", nameOf(ni)); + } + ni = ni->driver.cell->ports.at(id_I).net; + return allocate_longwire(ni, lw_idx); + break; + default: + break; + } + + if (getCtx()->debug) { + log_info("Requested index:%d\n", lw_idx); + } + if (avail_longwires == 0 || (lw_idx != -1 && (avail_longwires & (1 << lw_idx)) == 0)) { + return false; + } + int longwire = lw_idx; + if (lw_idx == -1) { + for (longwire = 7; longwire >= 0; --longwire) { + if (avail_longwires & (1 << longwire)) { + break; + } + } + } + avail_longwires &= ~(1 << longwire); + + // BUFS cell + CellInfo *bufs; + char buf[40]; + snprintf(buf, sizeof(buf), "$PACKER_BUFS%d", longwire); + std::unique_ptr new_cell = create_generic_cell(getCtx(), id_BUFS, buf); + bufs = new_cell.get(); + cells[bufs->name] = std::move(new_cell); + if (lw_idx != -1) { + bufs->cluster = bufs->name; + bufs->constr_z = lw_idx + BelZ::bufs_0_z; + bufs->constr_abs_z = true; + bufs->constr_children.clear(); + } + + // old driver -> bufs LW input net + snprintf(buf, sizeof(buf), "$PACKER_BUFS_%c", longwire + 'A'); + auto net = std::make_unique(id(buf)); + NetInfo *bufs_net = net.get(); + nets[net->name] = std::move(net); + + // split the net + CellInfo *driver_cell = ni->driver.cell; + IdString driver_port = ni->driver.port; + driver_cell->disconnectPort(driver_port); + + bufs->connectPort(id_O, ni); + bufs->connectPort(id_I, bufs_net); + driver_cell->connectPort(driver_port, bufs_net); + + if (getCtx()->debug) { + log_info("Long wire %d was allocated\n", longwire); + } + return true; +} + +void Arch::fix_longwire_bels() +{ + // After routing, it is clear which wires and in which bus SS00 and SS40 are used and + // in which quadrant they are routed. Here we write it in the attributes. + for (auto &cell : cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_BUFS) { + continue; + } + const NetInfo *ni = ci->getPort(id_O); + if (ni == nullptr) { + continue; + } + // bus wire is one of the wires + // value does not matter, but the L/R parameter itself + for (auto &wire : ni->wires) { + WireId w = wires[wire.first].type; + switch (w.hash()) { + case ID_LWSPINETL0: + case ID_LWSPINETL1: + case ID_LWSPINETL2: + case ID_LWSPINETL3: + case ID_LWSPINETL4: + case ID_LWSPINETL5: + case ID_LWSPINETL6: + case ID_LWSPINETL7: + case ID_LWSPINEBL0: + case ID_LWSPINEBL1: + case ID_LWSPINEBL2: + case ID_LWSPINEBL3: + case ID_LWSPINEBL4: + case ID_LWSPINEBL5: + case ID_LWSPINEBL6: + case ID_LWSPINEBL7: + ci->setParam(id("L"), Property(w.str(this))); + break; + case ID_LWSPINETR0: + case ID_LWSPINETR1: + case ID_LWSPINETR2: + case ID_LWSPINETR3: + case ID_LWSPINETR4: + case ID_LWSPINETR5: + case ID_LWSPINETR6: + case ID_LWSPINETR7: + case ID_LWSPINEBR0: + case ID_LWSPINEBR1: + case ID_LWSPINEBR2: + case ID_LWSPINEBR3: + case ID_LWSPINEBR4: + case ID_LWSPINEBR5: + case ID_LWSPINEBR6: + case ID_LWSPINEBR7: + ci->setParam(id("R"), Property(w.str(this))); + break; + default: + break; + } + } + } +} + WireInfo &Arch::wire_info(IdString wire) { auto w = wires.find(wire); @@ -628,6 +768,28 @@ DelayQuad Arch::getWireTypeDelay(IdString wire) case ID_W830: len = id_X8; break; + case ID_LT02: + case ID_LT13: + glbsrc = id_SPINE_TAP_SCLK_0; + break; + case ID_LT01: + case ID_LT04: + glbsrc = id_SPINE_TAP_SCLK_1; + break; + case ID_LBO0: + case ID_LBO1: + glbsrc = id_TAP_BRANCH_SCLK; + break; + case ID_LB01: + case ID_LB11: + case ID_LB21: + case ID_LB31: + case ID_LB41: + case ID_LB51: + case ID_LB61: + case ID_LB71: + glbsrc = id_BRANCH_SCLK; + break; case ID_GT00: case ID_GT10: glbsrc = id_SPINE_TAP_PCLK; @@ -647,7 +809,9 @@ DelayQuad Arch::getWireTypeDelay(IdString wire) glbsrc = id_BRANCH_PCLK; break; default: - if (wire.str(this).rfind("SPINE", 0) == 0) { + if (wire.str(this).rfind("LWSPINE", 0) == 0) { + glbsrc = IdString(ID_CENT_SPINE_SCLK); + } else if (wire.str(this).rfind("SPINE", 0) == 0) { glbsrc = IdString(ID_CENT_SPINE_PCLK); } else if (wire.str(this).rfind("UNK", 0) == 0) { glbsrc = IdString(ID_PIO_CENT_PCLK); @@ -691,7 +855,7 @@ void Arch::read_cst(std::istream &in) std::regex port_attrre = std::regex("([^ =;]+=[^ =;]+) *([^;]*;)"); std::regex iobelre = std::regex("IO([TRBL])([0-9]+)\\[?([A-Z])\\]?"); std::regex inslocre = std::regex("INS_LOC +\"([^\"]+)\" +R([0-9]+)C([0-9]+)\\[([0-9])\\]\\[([AB])\\] *;.*"); - std::regex clockre = std::regex("CLOCK_LOC +\"([^\"]+)\" +BUF([GS])[^;]*;"); + std::regex clockre = std::regex("CLOCK_LOC +\"([^\"]+)\" +BUF([GS])(\\[([0-7])\\])?[^;]*;.*"); std::smatch match, match_attr, match_pinloc; std::string line, pinline; enum @@ -732,15 +896,23 @@ void Arch::read_cst(std::istream &in) continue; } switch (cst_type) { - case clock: { // CLOCK name BUFG|S + case clock: { // CLOCK name BUFG|S=# std::string which_clock = match[2]; + std::string lw = match[4]; + int lw_idx = -1; + if (lw.length() > 0) { + lw_idx = atoi(lw.c_str()); + log_info("lw_idx:%d\n", lw_idx); + } if (which_clock.at(0) == 'S') { auto ni = nets.find(net); if (ni == nets.end()) { log_info("Net %s not found\n", net.c_str(this)); continue; } - log_info("Long wires are not implemented. The %s network will use normal routing.\n", net.c_str(this)); + if (!allocate_longwire(ni->second.get(), lw_idx)) { + log_info("Can't use the long wires. The %s network will use normal routing.\n", net.c_str(this)); + } } else { log_info("BUFG isn't supported\n"); continue; @@ -1021,6 +1193,31 @@ Arch::Arch(ArchArgs args) : args(args) bool dff = true; bool oddrc = false; switch (static_cast(bel->type_id)) { + case ID_BUFS7: + z++; /* fall-through*/ + case ID_BUFS6: + z++; /* fall-through*/ + case ID_BUFS5: + z++; /* fall-through*/ + case ID_BUFS4: + z++; /* fall-through*/ + case ID_BUFS3: + z++; /* fall-through*/ + case ID_BUFS2: + z++; /* fall-through*/ + case ID_BUFS1: + z++; /* fall-through*/ + case ID_BUFS0: + snprintf(buf, 32, "R%dC%d_BUFS%d", row + 1, col + 1, z); + belname = id(buf); + addBel(belname, id_BUFS, Loc(col, row, BelZ::bufs_0_z + z), false); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_I)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelInput(belname, id_I, id(buf)); + portname = IdString(pairLookup(bel->ports.get(), bel->num_ports, ID_O)->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); + addBelOutput(belname, id_O, id(buf)); + break; case ID_GSR0: snprintf(buf, 32, "R%dC%d_GSR0", row + 1, col + 1); belname = id(buf); @@ -1675,6 +1872,7 @@ bool Arch::route() } getCtx()->settings[id_route] = 1; archInfoToAttributes(); + fix_longwire_bels(); return result; } diff --git a/gowin/arch.h b/gowin/arch.h index 14181d793b..8bbbd51488 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -459,6 +459,9 @@ struct Arch : BaseArch void assignArchInfo() override; bool cellsCompatible(const CellInfo **cells, int count) const; bool haveBelType(int x, int y, IdString bel_type); + bool allocate_longwire(NetInfo *ni, int lw_idx = -1); + void fix_longwire_bels(); + // chip db version unsigned int const chipdb_version = 1; @@ -475,6 +478,9 @@ struct Arch : BaseArch // XXX GW1NR-9 iobuf quirk bool gw1n9_quirk = false; + // 8 Long wires + uint8_t avail_longwires = 0xff; + // Permissible combinations of modes in a single slice std::map dff_comp_mode; }; @@ -487,7 +493,9 @@ enum iologic_0_z = 20, // start Z for the IOLOGIC bels vcc_0_z = 277, // virtual VCC bel Z gnd_0_z = 278, // virtual VSS bel Z - osc_z = 280 // Z for the oscillator bels + osc_z = 280, // Z for the oscillator bels + bufs_0_z = 281, // Z for long wire buffer bel + free_z = 289 // Must be the last, one can use z starting from this value, adjust accordingly. }; } diff --git a/gowin/cells.cc b/gowin/cells.cc index c3b2178259..6010164a70 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -69,6 +69,9 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addOutput(id_G); } else if (type == id_VCC) { new_cell->addOutput(id_V); + } else if (type == id_BUFS) { + new_cell->addInput(id_I); + new_cell->addOutput(id_O); } else { log_error("unable to create generic cell of type %s\n", type.c_str(ctx)); } diff --git a/gowin/constids.inc b/gowin/constids.inc index 3691c50606..99c791f890 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -679,6 +679,81 @@ X(IOBHS) X(IOBIS) X(IOBJS) +// long wires +X(BUFS) +X(BUFS0) +X(BUFS1) +X(BUFS2) +X(BUFS3) +X(BUFS4) +X(BUFS5) +X(BUFS6) +X(BUFS7) +X(LWT0) +X(LWB0) +X(LWT1) +X(LWB1) +X(LWT2) +X(LWB2) +X(LWT3) +X(LWB3) +X(LWT4) +X(LWB4) +X(LWT5) +X(LWB5) +X(LWT6) +X(LWB6) +X(LWT7) +X(LWB7) +X(LWSPINETL0) +X(LWSPINETL1) +X(LWSPINETL2) +X(LWSPINETL3) +X(LWSPINETL4) +X(LWSPINETL5) +X(LWSPINETL6) +X(LWSPINETL7) +X(LWSPINETR0) +X(LWSPINETR1) +X(LWSPINETR2) +X(LWSPINETR3) +X(LWSPINETR4) +X(LWSPINETR5) +X(LWSPINETR6) +X(LWSPINETR7) +X(LWSPINEBL0) +X(LWSPINEBL1) +X(LWSPINEBL2) +X(LWSPINEBL3) +X(LWSPINEBL4) +X(LWSPINEBL5) +X(LWSPINEBL6) +X(LWSPINEBL7) +X(LWSPINEBR0) +X(LWSPINEBR1) +X(LWSPINEBR2) +X(LWSPINEBR3) +X(LWSPINEBR4) +X(LWSPINEBR5) +X(LWSPINEBR6) +X(LWSPINEBR7) +X(LWI0) +X(LWI1) +X(LWI2) +X(LWI3) +X(LWI4) +X(LWI5) +X(LWI6) +X(LWI7) +X(LWO0) +X(LWO1) +X(LWO2) +X(LWO3) +X(LWO4) +X(LWO5) +X(LWO6) +X(LWO7) + // IOLOGIC X(TX) X(XXX_VSS) @@ -812,6 +887,11 @@ X(CENT_SPINE_PCLK) X(SPINE_TAP_PCLK) X(TAP_BRANCH_PCLK) X(BRANCH_PCLK) +X(CENT_SPINE_SCLK) +X(SPINE_TAP_SCLK_0) +X(SPINE_TAP_SCLK_1) +X(TAP_BRANCH_SCLK) +X(BRANCH_SCLK) X(clksetpos) X(clkholdpos) X(clk_qpos) diff --git a/gowin/pack.cc b/gowin/pack.cc index 4adfec1aa2..838201423d 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -947,9 +947,11 @@ static void pack_io(Context *ctx) if (constr_bel != ci->attrs.end()) { constr_bel_name = constr_bel->second.as_string(); } - constr_bel = iob->attrs.find(id_BEL); - if (constr_bel != iob->attrs.end()) { - constr_bel_name = constr_bel->second.as_string(); + if (iob != nullptr) { + constr_bel = iob->attrs.find(id_BEL); + if (constr_bel != iob->attrs.end()) { + constr_bel_name = constr_bel->second.as_string(); + } } if (!constr_bel_name.empty()) { BelId constr_bel = ctx->getBelByNameStr(constr_bel_name); From 490dddf636bc50945ee6e6858f7c1942faa3aaf5 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Sun, 5 Jun 2022 16:59:06 +0200 Subject: [PATCH 148/712] WIP shadowram --- gowin/arch.cc | 36 ++++++++++++++++++++ gowin/arch.h | 2 ++ gowin/cells.cc | 33 ++++++++++++++++++ gowin/cells.h | 8 +++++ gowin/constids.inc | 13 ++++++++ gowin/pack.cc | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 175 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index 4fc2cd4321..d554bd6454 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -791,6 +791,39 @@ void Arch::read_cst(std::istream &in) settings[id_cst] = 1; } +void Arch::addShadowRamBels(const DatabasePOD *db, int row, int col) +{ + IdString belname, bel_id; + char buf[32]; + snprintf(buf, 32, "R%dC%d_RAMW", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_RAMW, Loc(col, row, BelZ::lutram_0_z), false); + + snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 4); + addBelInput(belname, id_A4, id(buf)); + snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 4); + addBelInput(belname, id_B4, id(buf)); + snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 4); + addBelInput(belname, id_C4, id(buf)); + snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 4); + addBelInput(belname, id_D4, id(buf)); + + snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 5); + addBelInput(belname, id_A5, id(buf)); + snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 5); + addBelInput(belname, id_B5, id(buf)); + snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 5); + addBelInput(belname, id_C5, id(buf)); + snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 5); + addBelInput(belname, id_D5, id(buf)); + + snprintf(buf, 32, "R%dC%d_CLK%d", row + 1, col + 1, 2); + addBelInput(belname, id_CLK, id(buf)); + snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, 2); + addBelInput(belname, id_LSR, id(buf)); +} + + // Add all MUXes for the cell void Arch::addMuxBels(const DatabasePOD *db, int row, int col) { @@ -1112,6 +1145,9 @@ Arch::Arch(ArchArgs args) : args(args) if (z == 0) { addMuxBels(db, row, col); } + if (z == 4) { + addShadowRamBels(db, row, col); + } if (z % 2 == 0) { snprintf(buf, 32, "R%dC%d_LUT_GRP%d", row + 1, col + 1, z); grpname = id(buf); diff --git a/gowin/arch.h b/gowin/arch.h index 14181d793b..80f887bb36 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -344,6 +344,7 @@ struct Arch : BaseArch DelayQuad getWireTypeDelay(IdString wire); void read_cst(std::istream &in); void addMuxBels(const DatabasePOD *db, int row, int col); + void addShadowRamBels(const DatabasePOD *db, int row, int col); // --------------------------------------------------------------- // Common Arch API. Every arch must provide the following methods. @@ -485,6 +486,7 @@ enum { mux_0_z = 10, // start Z for the MUX2LUT5 bels iologic_0_z = 20, // start Z for the IOLOGIC bels + lutram_0_z = 30, // start Z for the IOLOGIC bels vcc_0_z = 277, // virtual VCC bel Z gnd_0_z = 278, // virtual VSS bel Z osc_z = 280 // Z for the oscillator bels diff --git a/gowin/cells.cc b/gowin/cells.cc index c3b2178259..8285dc4e36 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -166,4 +166,37 @@ void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, pool &to } } +void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw) +{ + if (ramw->hierpath == IdString()) + ramw->hierpath = ramw->hierpath; + ram->movePortTo(ctx->id("WAD[0]"), ramw, id_A4); + ram->movePortTo(ctx->id("WAD[1]"), ramw, id_B4); + ram->movePortTo(ctx->id("WAD[2]"), ramw, id_C4); + ram->movePortTo(ctx->id("WAD[3]"), ramw, id_D4); + + ram->movePortTo(ctx->id("DI[0]"), ramw, id_A5); + ram->movePortTo(ctx->id("DI[1]"), ramw, id_B5); + ram->movePortTo(ctx->id("DI[2]"), ramw, id_C5); + ram->movePortTo(ctx->id("DI[3]"), ramw, id_D5); + + ram->movePortTo(ctx->id("CLK"), ramw, id_CLK); + ram->movePortTo(ctx->id("WRE"), ramw, id_LSR); +} + +void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index) +{ + char buf1[32]; + if (slice->hierpath == IdString()) + slice->hierpath = slice->hierpath; + + snprintf(buf1, 32, "DO[%d]", index); + ram->movePortTo(ctx->id(buf1), slice, id_F); + + ram->copyPortTo(ctx->id("RAD[0]"), slice, id_A); + ram->copyPortTo(ctx->id("RAD[1]"), slice, id_B); + ram->copyPortTo(ctx->id("RAD[2]"), slice, id_C); + ram->copyPortTo(ctx->id("RAD[3]"), slice, id_D); +} + NEXTPNR_NAMESPACE_END diff --git a/gowin/cells.h b/gowin/cells.h index 8f0636b812..b6d8649733 100644 --- a/gowin/cells.h +++ b/gowin/cells.h @@ -111,6 +111,8 @@ inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SLICE; } +inline bool is_sram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_RAM16SDP4; } + // Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports // as needed. Set no_dff if a DFF is not being used, so that the output // can be reconnected @@ -125,6 +127,12 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l // Convert a Gowin IO buffer to a IOB bel void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, pool &todelete_cells); +// Convert RAM16 to write port +void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw); + +// Convert RAM16 to slice +void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index); + NEXTPNR_NAMESPACE_END #endif diff --git a/gowin/constids.inc b/gowin/constids.inc index 3691c50606..d9ca1f0c95 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -748,6 +748,19 @@ X(DFFNPE) X(DFFNC) X(DFFNCE) +// Shadow RAM +X(RAMW) +X(RAM16SDP4) +X(WADA) +X(WADB) +X(WADC) +X(WADD) +X(DIA) +X(DIB) +X(DIC) +X(DID) +X(WRE) + // IOB types X(IBUF) X(OBUF) diff --git a/gowin/pack.cc b/gowin/pack.cc index 4adfec1aa2..fa88772a92 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -692,6 +692,89 @@ static void pack_gsr(Context *ctx) } } +// Pack shadow RAM +void pack_sram(Context *ctx) +{ + pool packed_cells; + std::vector> new_cells; + + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_sram(ctx, ci)) { + + // Create RAMW slice + std::unique_ptr ramw_slice = + create_generic_cell(ctx, id_RAMW, ci->name.str(ctx) + "$RAMW_SLICE"); + sram_to_ramw_split(ctx, ci, ramw_slice.get()); + + // Create actual RAM slices + std::unique_ptr ram_comb[4]; + for (int i = 0; i < 4; i++) { + ram_comb[i] = create_generic_cell(ctx, id_SLICE, + ci->name.str(ctx) + "$SRAM_SLICE" + std::to_string(i)); + sram_to_slice(ctx, ci, ram_comb[i].get(), i); + } + // Create 'block' SLICEs as a placement hint that these cells are mutually exclusive with the RAMW + std::unique_ptr ramw_block[2]; + for (int i = 0; i < 2; i++) { + ramw_block[i] = create_generic_cell(ctx, id_SLICE, + ci->name.str(ctx) + "$RAMW_BLOCK" + std::to_string(i)); + ramw_block[i]->params[id_FF_TYPE] = std::string("RAMW_BLOCK"); + } + + // Disconnect ports of original cell after packing + // ci->disconnectPort(id_WCK); + // ci->disconnectPort(id_WRE); + + for (int i = 0; i < 4; i++) + ci->disconnectPort(ctx->id(stringf("RAD[%d]", i))); + + // Setup placement constraints + // Use the 0th bit as an anchor + ram_comb[0]->constr_abs_z = true; + ram_comb[0]->constr_z = 0; + ram_comb[0]->cluster = ram_comb[0]->name; + for (int i = 1; i < 4; i++) { + ram_comb[i]->cluster = ram_comb[0]->name; + ram_comb[i]->constr_abs_z = true; + ram_comb[i]->constr_x = 0; + ram_comb[i]->constr_y = 0; + ram_comb[i]->constr_z = i; + ram_comb[0]->constr_children.push_back(ram_comb[i].get()); + } + for (int i = 0; i < 2; i++) { + ramw_block[i]->cluster = ram_comb[0]->name; + ramw_block[i]->constr_abs_z = true; + ramw_block[i]->constr_x = 0; + ramw_block[i]->constr_y = 0; + ramw_block[i]->constr_z = i + 4; + ram_comb[0]->constr_children.push_back(ramw_block[i].get()); + } + + ramw_slice->cluster = ram_comb[0]->name; + ramw_slice->constr_abs_z = true; + ramw_slice->constr_x = 0; + ramw_slice->constr_y = 0; + ramw_slice->constr_z = 4; + ram_comb[0]->constr_children.push_back(ramw_slice.get()); + + for (int i = 0; i < 4; i++) + new_cells.push_back(std::move(ram_comb[i])); + for (int i = 0; i < 2; i++) + new_cells.push_back(std::move(ramw_block[i])); + new_cells.push_back(std::move(ramw_slice)); + packed_cells.insert(ci->name); + } + } + for (auto pcell : packed_cells) { + ctx->cells.erase(pcell); + } + for (auto &ncell : new_cells) { + ctx->cells[ncell->name] = std::move(ncell); + } +} + + static bool is_nextpnr_iob(const Context *ctx, CellInfo *cell) { return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") || From de1bee9352c9f760cb21a35912c48ed2f830c023 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Mon, 6 Jun 2022 14:35:33 +0200 Subject: [PATCH 149/712] lutram actually PnRs --- gowin/arch.cc | 64 ++++++++++++++++++++-------------------------- gowin/arch.h | 1 - gowin/cells.cc | 10 ++++++++ gowin/constids.inc | 1 + gowin/pack.cc | 5 +++- 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index d554bd6454..cba492abe1 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -791,39 +791,6 @@ void Arch::read_cst(std::istream &in) settings[id_cst] = 1; } -void Arch::addShadowRamBels(const DatabasePOD *db, int row, int col) -{ - IdString belname, bel_id; - char buf[32]; - snprintf(buf, 32, "R%dC%d_RAMW", row + 1, col + 1); - belname = id(buf); - addBel(belname, id_RAMW, Loc(col, row, BelZ::lutram_0_z), false); - - snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 4); - addBelInput(belname, id_A4, id(buf)); - snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 4); - addBelInput(belname, id_B4, id(buf)); - snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 4); - addBelInput(belname, id_C4, id(buf)); - snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 4); - addBelInput(belname, id_D4, id(buf)); - - snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 5); - addBelInput(belname, id_A5, id(buf)); - snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 5); - addBelInput(belname, id_B5, id(buf)); - snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 5); - addBelInput(belname, id_C5, id(buf)); - snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 5); - addBelInput(belname, id_D5, id(buf)); - - snprintf(buf, 32, "R%dC%d_CLK%d", row + 1, col + 1, 2); - addBelInput(belname, id_CLK, id(buf)); - snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, 2); - addBelInput(belname, id_LSR, id(buf)); -} - - // Add all MUXes for the cell void Arch::addMuxBels(const DatabasePOD *db, int row, int col) { @@ -1100,6 +1067,34 @@ Arch::Arch(ArchArgs args) : args(args) snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this)); addBelInput(belname, id_OSCEN, id(buf)); break; + case ID_RAM16: + snprintf(buf, 32, "R%dC%d_RAMW", row + 1, col + 1); + belname = id(buf); + addBel(belname, id_RAMW, Loc(col, row, BelZ::lutram_0_z), false); + + snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 4); + addBelInput(belname, id_A4, id(buf)); + snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 4); + addBelInput(belname, id_B4, id(buf)); + snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 4); + addBelInput(belname, id_C4, id(buf)); + snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 4); + addBelInput(belname, id_D4, id(buf)); + + snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, 5); + addBelInput(belname, id_A5, id(buf)); + snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, 5); + addBelInput(belname, id_B5, id(buf)); + snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, 5); + addBelInput(belname, id_C5, id(buf)); + snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, 5); + addBelInput(belname, id_D5, id(buf)); + + snprintf(buf, 32, "R%dC%d_CLK%d", row + 1, col + 1, 2); + addBelInput(belname, id_CLK, id(buf)); + snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, 2); + addBelInput(belname, id_LSR, id(buf)); + break; // fall through the ++ case ID_LUT7: z++; @@ -1145,9 +1140,6 @@ Arch::Arch(ArchArgs args) : args(args) if (z == 0) { addMuxBels(db, row, col); } - if (z == 4) { - addShadowRamBels(db, row, col); - } if (z % 2 == 0) { snprintf(buf, 32, "R%dC%d_LUT_GRP%d", row + 1, col + 1, z); grpname = id(buf); diff --git a/gowin/arch.h b/gowin/arch.h index 80f887bb36..c13cdf093f 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -344,7 +344,6 @@ struct Arch : BaseArch DelayQuad getWireTypeDelay(IdString wire); void read_cst(std::istream &in); void addMuxBels(const DatabasePOD *db, int row, int col); - void addShadowRamBels(const DatabasePOD *db, int row, int col); // --------------------------------------------------------------- // Common Arch API. Every arch must provide the following methods. diff --git a/gowin/cells.cc b/gowin/cells.cc index 8285dc4e36..63ee71ed9a 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -48,6 +48,13 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addOutput(id_Q); new_cell->addInput(id_CE); new_cell->addInput(id_LSR); + } else if (type == id_RAMW) { + IdString names[8] = {id_A4, id_B4, id_C4, id_D4, id_A5, id_B5, id_C5, id_D5}; + for (int i = 0; i < 8; i++) { + new_cell->addInput(names[i]); + } + new_cell->addInput(id_CLK); + new_cell->addInput(id_LSR); } else if (type == id_GW_MUX2_LUT5 || type == id_GW_MUX2_LUT6 || type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT8) { new_cell->addInput(id_I0); @@ -190,6 +197,9 @@ void sram_to_slice(Context *ctx, CellInfo *ram, CellInfo *slice, int index) if (slice->hierpath == IdString()) slice->hierpath = slice->hierpath; + snprintf(buf1, 32, "INIT_%d", index); + slice->params[id_INIT] = ram->params[ctx->id(buf1)]; + snprintf(buf1, 32, "DO[%d]", index); ram->movePortTo(ctx->id(buf1), slice, id_F); diff --git a/gowin/constids.inc b/gowin/constids.inc index d9ca1f0c95..0c788ecd83 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -749,6 +749,7 @@ X(DFFNC) X(DFFNCE) // Shadow RAM +X(RAM16) X(RAMW) X(RAM16SDP4) X(WADA) diff --git a/gowin/pack.cc b/gowin/pack.cc index fa88772a92..7d3a8e98ed 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -695,6 +695,8 @@ static void pack_gsr(Context *ctx) // Pack shadow RAM void pack_sram(Context *ctx) { + log_info("Packing Shadow RAM..\n"); + pool packed_cells; std::vector> new_cells; @@ -755,7 +757,7 @@ void pack_sram(Context *ctx) ramw_slice->constr_abs_z = true; ramw_slice->constr_x = 0; ramw_slice->constr_y = 0; - ramw_slice->constr_z = 4; + ramw_slice->constr_z = BelZ::lutram_0_z; ram_comb[0]->constr_children.push_back(ramw_slice.get()); for (int i = 0; i < 4; i++) @@ -1091,6 +1093,7 @@ bool Arch::pack() try { log_break(); pack_constants(ctx); + pack_sram(ctx); pack_gsr(ctx); pack_io(ctx); pack_diff_io(ctx); From bd0af4052c434b1cf3ed8522360b5acc78bd17a2 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 9 Jun 2022 08:20:29 +1000 Subject: [PATCH 150/712] gowin: Use local aliases In the Gowin chips, the tiles are connected to each other by a one-hop wire, among others. There are 4 one-hop wires, of which 2 are shared between north/south and east/west, have three names: e.g. SN10 and N110 and S110. But only one of them, the first, occurs as a sink for PIP, that is, you can not get a route that would pass through the S110 for example. This commit corrects the names to SN?0 and EW?0 at the wire creation stage to avoid dead wires. In addition, the SN?0 and EW?0 are among the few sinks for global clock wires and now there is the possibility of a more optimal clock routing. Signed-off-by: YRabbit --- gowin/arch.cc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index a991879ebc..82f5018b32 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -34,6 +34,8 @@ NEXTPNR_NAMESPACE_BEGIN +const PairPOD *pairLookup(const PairPOD *list, const size_t len, const int dest); + // GUI void Arch::fixClockSpineDecals(void) { @@ -627,7 +629,15 @@ IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString } snprintf(buf, 32, "%c%d0", direction, num); wire = id(buf); - snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num); + // local aliases + const TilePOD *tile = db->grid[row * db->cols + col].get(); + auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, wire.index); + if (local_alias != nullptr) { + wire = IdString(local_alias->src_id); + snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wire.c_str(this)); + } else { + snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num); + } return id(buf); } @@ -1491,12 +1501,6 @@ Arch::Arch(ArchArgs args) : args(args) snprintf(buf, 32, "R%dC%d_%s_%s", row + 1, col + 1, srcid.c_str(this), destid.c_str(this)); IdString pipname = id(buf); DelayQuad delay = getWireTypeDelay(destid); - // local alias - auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, srcid.index); - if (local_alias != nullptr) { - srcid = IdString(local_alias->src_id); - gsrcname = wireToGlobal(srcrow, srccol, db, srcid); - } // global alias srcid = IdString(pip.src_id); GlobalAliasPOD alias; From eac864ebdcdf7cf366e0dcbd5497e3c5cce3c479 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 9 Jun 2022 20:05:20 +0100 Subject: [PATCH 151/712] ecp5: Bind write_bitstream to Python Signed-off-by: gatecat --- ecp5/arch_pybindings.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc index 593feb335f..693d3199ec 100644 --- a/ecp5/arch_pybindings.cc +++ b/ecp5/arch_pybindings.cc @@ -23,6 +23,7 @@ #include "arch_pybindings.h" #include "nextpnr.h" #include "pybindings.h" +#include "bitstream.h" NEXTPNR_NAMESPACE_BEGIN @@ -69,6 +70,8 @@ void arch_wrap_python(py::module &m) WRAP_MAP_UPTR(m, CellMap, "IdCellMap"); WRAP_MAP_UPTR(m, NetMap, "IdNetMap"); WRAP_MAP(m, HierarchyMap, wrap_context, "HierarchyMap"); + + m.def("write_bitstream", &write_bitstream); } NEXTPNR_NAMESPACE_END From 8d063d38b148b1e7095a032ffc9cf957c2407f32 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sun, 12 Jun 2022 07:59:36 +0100 Subject: [PATCH 152/712] clangformat Signed-off-by: gatecat --- ecp5/arch_pybindings.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc index 693d3199ec..1621eda55a 100644 --- a/ecp5/arch_pybindings.cc +++ b/ecp5/arch_pybindings.cc @@ -21,9 +21,9 @@ #ifndef NO_PYTHON #include "arch_pybindings.h" +#include "bitstream.h" #include "nextpnr.h" #include "pybindings.h" -#include "bitstream.h" NEXTPNR_NAMESPACE_BEGIN From b7992ec7724000f813ab053ac6042326f3795471 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Thu, 16 Jun 2022 11:38:23 +0200 Subject: [PATCH 153/712] hook up CE maybe --- gowin/arch.cc | 2 ++ gowin/cells.cc | 1 + gowin/pack.cc | 1 + 3 files changed, 4 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index cba492abe1..65f1520477 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1094,6 +1094,8 @@ Arch::Arch(ArchArgs args) : args(args) addBelInput(belname, id_CLK, id(buf)); snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, 2); addBelInput(belname, id_LSR, id(buf)); + snprintf(buf, 32, "R%dC%d_CE%d", row + 1, col + 1, 2); + addBelInput(belname, id_CE, id(buf)); break; // fall through the ++ case ID_LUT7: diff --git a/gowin/cells.cc b/gowin/cells.cc index 63ee71ed9a..0dc0ce06fa 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -54,6 +54,7 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addInput(names[i]); } new_cell->addInput(id_CLK); + new_cell->addInput(id_CE); new_cell->addInput(id_LSR); } else if (type == id_GW_MUX2_LUT5 || type == id_GW_MUX2_LUT6 || type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT8) { diff --git a/gowin/pack.cc b/gowin/pack.cc index 7d3a8e98ed..0ba71705b7 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -708,6 +708,7 @@ void pack_sram(Context *ctx) std::unique_ptr ramw_slice = create_generic_cell(ctx, id_RAMW, ci->name.str(ctx) + "$RAMW_SLICE"); sram_to_ramw_split(ctx, ci, ramw_slice.get()); + ramw_slice->connectPort(id_CE, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); // Create actual RAM slices std::unique_ptr ram_comb[4]; From b950f5cb6de869658564855eb64c46c50c4bc249 Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 21 Jun 2022 12:35:13 +0100 Subject: [PATCH 154/712] Disable broken and failing interchange CI Signed-off-by: gatecat --- .../workflows/{interchange_ci.yml => interchange_ci.yml.disable} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{interchange_ci.yml => interchange_ci.yml.disable} (100%) diff --git a/.github/workflows/interchange_ci.yml b/.github/workflows/interchange_ci.yml.disable similarity index 100% rename from .github/workflows/interchange_ci.yml rename to .github/workflows/interchange_ci.yml.disable From b502499a1e7cfc97bf8a69ffa0365c040dadf2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chandler=20Kl=C3=BCser?= Date: Wed, 22 Jun 2022 21:03:04 -0300 Subject: [PATCH 155/712] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 14128b87ec..36ff9a8b6b 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,8 @@ An example of how to use the generic flow is in [generic/examples](generic/examp The nextpnr GUI is not built by default, to reduce the number of dependencies for a standard headless build. To enable it, add `-DBUILD_GUI=ON` to the CMake command line and ensure that Qt5 and OpenGL are available: - - On Ubuntu, install `qt5-default` + - On Ubuntu 22.04 LTS, install `qtcreator qtbase5-dev qt5-qmake` + - On other Ubuntu versions, install `qt5-default` - For MSVC vcpkg, install `qt5-base` (32-bit) or `qt5-base:x64-windows` (64-bit) - For Homebrew, install `qt5` and add qt5 in path: `echo 'export PATH="/usr/local/opt/qt/bin:$PATH"' >> ~/.bash_profile` ` - this change is effective in next terminal session, so please re-open terminal window before building From 590b9050ff5cc618b4df50e0877b4bf6d9e7949d Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 23 Jun 2022 11:42:58 +1000 Subject: [PATCH 156/712] gowin: add a separate router for the clocks A simple router that takes advantage of the fact that in each cell with DFFs their CLK inputs can directly connect to the global clock network. Networks with a large number of such sinks are sought and then each network is assigned to the available independent global clock networks. There are limited possibilities for routing mixed networks, that is, when the sinks are not only CLKs: in this case an attempt is made to use wires such as SN10/20 and EW10/20, that is, one short transition can be added between the global clock network and the sink. * At this time, networks with a source other than the I/O pin are not supported. This is typical for Tangnano4k and runber boards. * Router is disabled by default, you need to specify option --enable-globals to activate * No new chip bases are required. This may change in the distant future. Signed-off-by: YRabbit --- gowin/arch.cc | 15 +++ gowin/arch.h | 1 + gowin/globals.cc | 335 +++++++++++++++++++++++++++++++++++++++++++++++ gowin/globals.h | 26 ++++ gowin/main.cc | 16 ++- 5 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 gowin/globals.cc create mode 100644 gowin/globals.h diff --git a/gowin/arch.cc b/gowin/arch.cc index 82f5018b32..7b9097c93d 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -25,6 +25,7 @@ #include #include "embed.h" #include "gfx.h" +#include "globals.h" #include "nextpnr.h" #include "placer1.h" #include "placer_heap.h" @@ -341,6 +342,14 @@ BelInfo &Arch::bel_info(IdString bel) return b->second; } +NetInfo &Arch::net_info(IdString net) +{ + auto b = nets.find(net); + if (b == nets.end()) + NPNR_ASSERT_FALSE_STR("no net named " + net.str(this)); + return *b->second; +} + void Arch::addWire(IdString name, IdString type, int x, int y) { NPNR_ASSERT(wires.count(name) == 0); @@ -657,6 +666,7 @@ bool aliasCompare(GlobalAliasPOD i, GlobalAliasPOD j) return (i.dest_row < j.dest_row) || (i.dest_row == j.dest_row && i.dest_col < j.dest_col) || (i.dest_row == j.dest_row && i.dest_col == j.dest_col && i.dest_id < j.dest_id); } + bool timingCompare(TimingPOD i, TimingPOD j) { return i.name_id < j.name_id; } template const T *genericLookup(const T *first, int len, const T val, C compare) @@ -1865,6 +1875,11 @@ bool Arch::place() bool Arch::route() { std::string router = str_or_default(settings, id_router, defaultRouter); + + if (bool_or_default(settings, id("arch.enable-globals"))) { + route_gowin_globals(getCtx()); + } + bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); diff --git a/gowin/arch.h b/gowin/arch.h index 8bbbd51488..c59f8eb353 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -289,6 +289,7 @@ struct Arch : BaseArch WireInfo &wire_info(IdString wire); PipInfo &pip_info(IdString pip); BelInfo &bel_info(IdString bel); + NetInfo &net_info(IdString net); std::vector bel_ids, wire_ids, pip_ids; diff --git a/gowin/globals.cc b/gowin/globals.cc new file mode 100644 index 0000000000..5010cf79e7 --- /dev/null +++ b/gowin/globals.cc @@ -0,0 +1,335 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 gatecat + * + * 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 "globals.h" +#include +#include +#include +#include +#include "cells.h" +#include "log.h" +#include "nextpnr.h" +#include "place_common.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +class GowinGlobalRouter +{ + public: + GowinGlobalRouter(Context *ctx) : ctx(ctx){}; + + private: + // wire -> clock# + dict used_wires; + + // ordered nets + struct onet_t + { + IdString name; + int clock_ports; + WireId clock_io_wire; // IO wire if there is one + + onet_t() : name(IdString()), clock_ports(0), clock_io_wire(WireId()) {} + onet_t(IdString _name) : name(_name), clock_ports(0), clock_io_wire(WireId()) {} + + // sort + bool operator<(const onet_t &other) const + { + if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) { + return !(clock_io_wire != WireId()); + } + return clock_ports < other.clock_ports; + } + // search + bool operator==(const onet_t &other) const { return name == other.name; } + }; + + bool is_clock_port(PortRef const &user) + { + if (user.cell->type == id_SLICE && user.port == id_CLK) { + return true; + } + return false; + } + + WireId clock_io(PortRef const &driver) + { + // XXX normally all alternative functions of the pins should be passed + // in the chip database, but at the moment we find them from aliases/pips + // XXX check diff inputs too + if (driver.cell == nullptr || driver.cell->bel == BelId()) { + return WireId(); + } + // clock IOs have pips output->SPINExx + BelInfo &bel = ctx->bels.at(driver.cell->bel); + if (bel.type != id_IOB) { + return WireId(); + } + WireId wire = bel.pins[id_O].wire; + for (auto const &pip : ctx->getPipsDownhill(wire)) { + if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { + return wire; + } + } + return WireId(); + } + + // gather the clock nets + void gather_clock_nets(std::vector &clock_nets) + { + for (auto const &net : ctx->nets) { + NetInfo const *ni = net.second.get(); + auto new_clock = clock_nets.end(); + WireId clock_wire = clock_io(ni->driver); + if (clock_wire != WireId()) { + clock_nets.emplace_back(net.first); + new_clock = --clock_nets.end(); + new_clock->clock_io_wire = clock_wire; + } + for (auto const &user : ni->users) { + if (is_clock_port(user)) { + if (new_clock == clock_nets.end()) { + clock_nets.emplace_back(net.first); + new_clock = --clock_nets.end(); + } + ++(new_clock->clock_ports); + } + } + } + // need to prioritize the nets + std::sort(clock_nets.begin(), clock_nets.end()); + + if (ctx->verbose) { + for (auto const &net : clock_nets) { + log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports, + net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx)); + } + } + } + + // non clock port + // returns GB pip + IdString route_to_non_clock_port(WireId const dstWire, int clock, pool &used_pips, + pool &undo_wires) + { + static std::vector one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; + char buf[40]; + // uphill pips + for (auto const &uphill : ctx->getPipsUphill(dstWire)) { + WireId srcWire = ctx->getPipSrcWire(uphill); + if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != + one_hop.end()) { + // found one hop pip + if (used_wires.count(srcWire)) { + if (used_wires[srcWire] != clock) { + continue; + } + } + WireInfo wi = ctx->wire_info(srcWire); + std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1); + snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str()); + IdString gb = ctx->id(buf); + auto up_pips = ctx->getPipsUphill(srcWire); + if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) { + if (!used_wires.count(srcWire)) { + used_wires.insert(std::make_pair(srcWire, clock)); + undo_wires.insert(srcWire); + } + used_pips.insert(uphill); + if (ctx->verbose) { + log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx)); + } + return gb; + } + } + } + return IdString(); + } + + // route one net + void route_net(onet_t const &net, int clock) + { + // For failed routing undo + pool used_pips; + pool undo_wires; + + log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), clock); + for (auto const &user : ctx->net_info(net.name).users) { + // >>> port <- GB0 + WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0); + if (ctx->verbose) { + log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), + dstWire.c_str(ctx)); + } + + char buf[30]; + PipId gb_pip_id; + if (user.port == id_CLK) { + WireInfo const wi = ctx->wire_info(dstWire); + snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, + ctx->wire_info(dstWire).type.c_str(ctx)); + gb_pip_id = ctx->id(buf); + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) != + ctx->getPipsUphill(dstWire).end()); + } else { + // Non clock port + gb_pip_id = route_to_non_clock_port(dstWire, clock, used_pips, undo_wires); + if (gb_pip_id == IdString()) { + if (ctx->verbose) { + log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", + dstWire.c_str(ctx), net.name.c_str(ctx)); + } + for (IdString const &undo : undo_wires) { + used_wires.erase(undo); + } + return; + } + } + if (ctx->verbose) { + log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx)); + } + + if (used_pips.count(gb_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + continue; + } + used_pips.insert(gb_pip_id); + + // >>> GBOx <- GTx0 + dstWire = ctx->getPipSrcWire(gb_pip_id); + WireInfo dstWireInfo = ctx->wire_info(dstWire); + int branch_tap_idx = clock > 3 ? 1 : 0; + snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, + branch_tap_idx); + PipId gt_pip_id = ctx->id(buf); + if (ctx->verbose) { + log_info(" GT Pip:%s\n", buf); + } + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) != + ctx->getPipsUphill(dstWire).end()); + // if already routed + if (used_pips.count(gt_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + continue; + } + used_pips.insert(gt_pip_id); + + // >>> GTx0 <- SPINExx + // XXX no optimization here, we need to store + // the SPINE <-> clock# correspondence in the database. In the + // meantime, we define in run-time in a completely suboptimal way. + std::vector clock_spine; + dstWire = ctx->getPipSrcWire(gt_pip_id); + for (auto const &uphill_pip : ctx->getPipsUphill(dstWire)) { + std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx); + if (name.rfind("SPINE", 0) == 0) { + clock_spine.push_back(name); + } + } + sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool { + return (a.size() < b.size()) || (a.size() == b.size() && a < b); + }); + dstWireInfo = ctx->wire_info(dstWire); + snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1, + clock_spine[clock - branch_tap_idx * 4].c_str(), branch_tap_idx); + PipId spine_pip_id = ctx->id(buf); + if (ctx->verbose) { + log_info(" Spine Pip:%s\n", buf); + } + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) != + ctx->getPipsUphill(dstWire).end()); + // if already routed + if (used_pips.count(spine_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + continue; + } + used_pips.insert(spine_pip_id); + + // >>> SPINExx <- IO + dstWire = ctx->getPipSrcWire(spine_pip_id); + dstWireInfo = ctx->wire_info(dstWire); + PipId io_pip_id = PipId(); + for (auto const &uphill_pip : ctx->getPipsUphill(dstWire)) { + if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { + io_pip_id = uphill_pip; + } + } + NPNR_ASSERT(io_pip_id != PipId()); + if (ctx->verbose) { + log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx)); + } + // if already routed + if (used_pips.count(io_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + continue; + } + used_pips.insert(io_pip_id); + } + log_info(" Net %s is routed.\n", net.name.c_str(ctx)); + for (auto const pip : used_pips) { + ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED); + } + ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED); + } + + public: + Context *ctx; + void route_globals() + { + log_info("Routing globals...\n"); + + std::vector clock_nets; + gather_clock_nets(clock_nets); + // XXX we need to use the list of indexes of clocks from the database + // use 6 clocks (XXX 3 for GW1NZ-1) + int max_clock = 3, cur_clock = -1; + for (auto const &net : clock_nets) { + // XXX only IO clock for now + if (net.clock_io_wire == WireId()) { + log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx)); + continue; + } + if (++cur_clock >= max_clock) { + log_info(" No more clock wires left, skip the remaining nets.\n"); + break; + } + route_net(net, cur_clock); + } + } +}; + +void route_gowin_globals(Context *ctx) +{ + GowinGlobalRouter router(ctx); + router.route_globals(); +} + +NEXTPNR_NAMESPACE_END diff --git a/gowin/globals.h b/gowin/globals.h new file mode 100644 index 0000000000..4731447cab --- /dev/null +++ b/gowin/globals.h @@ -0,0 +1,26 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 gatecat + * + * 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" + +NEXTPNR_NAMESPACE_BEGIN + +void route_gowin_globals(Context *ctx); + +NEXTPNR_NAMESPACE_END diff --git a/gowin/main.cc b/gowin/main.cc index a45a49d40e..36bd865669 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -51,6 +51,8 @@ po::options_description GowinCommandHandler::getArchOptions() specific.add_options()("device", po::value(), "device name"); specific.add_options()("family", po::value(), "family name"); specific.add_options()("cst", po::value(), "physical constraints file"); + specific.add_options()("enable-globals", "separate routing of the clocks"); + specific.add_options()("enable-auto-longwires", "automatic detection and routing of long wires"); return specific; } @@ -79,7 +81,19 @@ std::unique_ptr GowinCommandHandler::createContext(dict(new Context(chipArgs)); + + auto ctx = std::unique_ptr(new Context(chipArgs)); + // routing options + // the default values will change in the future + ctx->settings[ctx->id("arch.enable-globals")] = 0; + ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0; + if (vm.count("enable-globals")) { + ctx->settings[ctx->id("arch.enable-globals")] = 1; + } + if (vm.count("enable-auto-longwires")) { + ctx->settings[ctx->id("arch.enable-auto-longwires")] = 1; + } + return ctx; } void GowinCommandHandler::customAfterLoad(Context *ctx) From 63f2acd42adf3884d153381696cf7845c6d74162 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 24 Jun 2022 08:20:06 +1000 Subject: [PATCH 157/712] gowin: process the CLK ports of the ODDR[C] primitives Also removed the useless references. Signed-off-by: YRabbit --- gowin/globals.cc | 15 ++++++++------- gowin/globals.h | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/gowin/globals.cc b/gowin/globals.cc index 5010cf79e7..ed75a93888 100644 --- a/gowin/globals.cc +++ b/gowin/globals.cc @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 gatecat + * Copyright (C) 2022 YRabbit * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -63,7 +64,7 @@ class GowinGlobalRouter bool is_clock_port(PortRef const &user) { - if (user.cell->type == id_SLICE && user.port == id_CLK) { + if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && user.port == id_CLK) { return true; } return false; @@ -78,12 +79,12 @@ class GowinGlobalRouter return WireId(); } // clock IOs have pips output->SPINExx - BelInfo &bel = ctx->bels.at(driver.cell->bel); + BelInfo &bel = ctx->bel_info(driver.cell->bel); if (bel.type != id_IOB) { return WireId(); } WireId wire = bel.pins[id_O].wire; - for (auto const &pip : ctx->getPipsDownhill(wire)) { + for (auto const pip : ctx->getPipsDownhill(wire)) { if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { return wire; } @@ -132,7 +133,7 @@ class GowinGlobalRouter static std::vector one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; char buf[40]; // uphill pips - for (auto const &uphill : ctx->getPipsUphill(dstWire)) { + for (auto const uphill : ctx->getPipsUphill(dstWire)) { WireId srcWire = ctx->getPipSrcWire(uphill); if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != one_hop.end()) { @@ -197,7 +198,7 @@ class GowinGlobalRouter log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", dstWire.c_str(ctx), net.name.c_str(ctx)); } - for (IdString const &undo : undo_wires) { + for (IdString const undo : undo_wires) { used_wires.erase(undo); } return; @@ -243,7 +244,7 @@ class GowinGlobalRouter // meantime, we define in run-time in a completely suboptimal way. std::vector clock_spine; dstWire = ctx->getPipSrcWire(gt_pip_id); - for (auto const &uphill_pip : ctx->getPipsUphill(dstWire)) { + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx); if (name.rfind("SPINE", 0) == 0) { clock_spine.push_back(name); @@ -275,7 +276,7 @@ class GowinGlobalRouter dstWire = ctx->getPipSrcWire(spine_pip_id); dstWireInfo = ctx->wire_info(dstWire); PipId io_pip_id = PipId(); - for (auto const &uphill_pip : ctx->getPipsUphill(dstWire)) { + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { io_pip_id = uphill_pip; } diff --git a/gowin/globals.h b/gowin/globals.h index 4731447cab..41a8727abe 100644 --- a/gowin/globals.h +++ b/gowin/globals.h @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 gatecat + * Copyright (C) 2022 YRabbit * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above From 6122f172e3f15115cfb755ed702cc0438c341269 Mon Sep 17 00:00:00 2001 From: gatecat Date: Sat, 25 Jun 2022 15:56:16 +0100 Subject: [PATCH 158/712] ice40: Fix accidental creation of empty ports Signed-off-by: gatecat --- ice40/arch_place.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index 3b024d8175..67ddf77763 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -95,7 +95,7 @@ bool Arch::isBelLocationValid(BelId bel) const } return logic_cells_compatible(bel_cells.data(), num_cells); } else { - CellInfo *cell = getBoundBelCell(bel); + const CellInfo *cell = getBoundBelCell(bel); if (cell == nullptr) return true; else if (cell->type == id_SB_IO) { @@ -107,7 +107,7 @@ bool Arch::isBelLocationValid(BelId bel) const for (auto pin : getWireBelPins(wire)) { if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) { // Is there a PLL there ? - auto pll_cell = getBoundBelCell(pin.bel); + const CellInfo *pll_cell = getBoundBelCell(pin.bel); if (pll_cell == nullptr) break; @@ -116,11 +116,11 @@ bool Arch::isBelLocationValid(BelId bel) const break; // Is that SB_IO used at an input ? - if ((cell->ports[id_D_IN_0].net == nullptr) && (cell->ports[id_D_IN_1].net == nullptr)) + if ((cell->getPort(id_D_IN_0) == nullptr) && (cell->getPort(id_D_IN_1) == nullptr)) break; // Are we perhaps a PAD INPUT Bel that can be placed here? - if (pll_cell->attrs[id_BEL_PAD_INPUT] == getBelName(bel).str(getCtx())) + if (str_or_default(pll_cell->attrs, id_BEL_PAD_INPUT, "") == getBelName(bel).str(getCtx())) return true; // Conflict @@ -144,7 +144,7 @@ bool Arch::isBelLocationValid(BelId bel) const } else { // Check LVDS IO is not placed at complement location BelId compBel = getBelByLocation(compLoc); - CellInfo *compCell = getBoundBelCell(compBel); + const CellInfo *compCell = getBoundBelCell(compBel); if (compCell && compCell->ioInfo.lvds) return false; @@ -161,10 +161,10 @@ bool Arch::isBelLocationValid(BelId bel) const _io_pintype_need_clk_en(cell->ioInfo.pintype), _io_pintype_need_clk_en(compCell->ioInfo.pintype), }; - NetInfo *nets[] = { - cell->ports[id_INPUT_CLK].net, compCell->ports[id_INPUT_CLK].net, - cell->ports[id_OUTPUT_CLK].net, compCell->ports[id_OUTPUT_CLK].net, - cell->ports[id_CLOCK_ENABLE].net, compCell->ports[id_CLOCK_ENABLE].net, + const NetInfo *nets[] = { + cell->getPort(id_INPUT_CLK), compCell->getPort(id_INPUT_CLK), + cell->getPort(id_OUTPUT_CLK), compCell->getPort(id_OUTPUT_CLK), + cell->getPort(id_CLOCK_ENABLE), compCell->getPort(id_CLOCK_ENABLE), }; for (int i = 0; i < 6; i++) From 6f56ad298c1a94259698cbd487f03efbd68f0342 Mon Sep 17 00:00:00 2001 From: Pepijn de Vos Date: Sat, 2 Jul 2022 20:44:59 +0200 Subject: [PATCH 159/712] use DFF RAM mode --- gowin/pack.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gowin/pack.cc b/gowin/pack.cc index fbd2092f1c..9f4b54e2a7 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -715,6 +715,8 @@ void pack_sram(Context *ctx) for (int i = 0; i < 4; i++) { ram_comb[i] = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "$SRAM_SLICE" + std::to_string(i)); + ram_comb[i]->params[id_FF_USED] = 1; + ram_comb[i]->params[id_FF_TYPE] = std::string("RAM"); sram_to_slice(ctx, ci, ram_comb[i].get(), i); } // Create 'block' SLICEs as a placement hint that these cells are mutually exclusive with the RAMW @@ -722,7 +724,8 @@ void pack_sram(Context *ctx) for (int i = 0; i < 2; i++) { ramw_block[i] = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "$RAMW_BLOCK" + std::to_string(i)); - ramw_block[i]->params[id_FF_TYPE] = std::string("RAMW_BLOCK"); + ram_comb[i]->params[id_FF_USED] = 1; + ramw_block[i]->params[id_FF_TYPE] = std::string("RAM"); } // Disconnect ports of original cell after packing From 3172a38daeb4588d3aaa686816c09d64ccf08587 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 4 Jul 2022 10:32:39 +1000 Subject: [PATCH 160/712] gowin: Let the placer know about global networks Refactor in order to detect networks that will be routed in a special mode earlier. This makes it possible to mark the source of such networks as a global buffer, thereby removing their influence on element placement. In addition, timing classes are set for some cells. Signed-off-by: YRabbit --- gowin/arch.cc | 56 +++++- gowin/arch.h | 14 ++ gowin/globals.cc | 486 ++++++++++++++++++++++------------------------- gowin/globals.h | 66 ++++++- gowin/pack.cc | 4 + 5 files changed, 367 insertions(+), 259 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 7b9097c93d..a968c112bb 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -25,7 +25,6 @@ #include #include "embed.h" #include "gfx.h" -#include "globals.h" #include "nextpnr.h" #include "placer1.h" #include "placer_heap.h" @@ -257,6 +256,8 @@ bool Arch::allocate_longwire(NetInfo *ni, int lw_idx) return true; } +void Arch::auto_longwires() {} + void Arch::fix_longwire_bels() { // After routing, it is clear which wires and in which bus SS00 and SS40 are used and @@ -552,6 +553,28 @@ void Arch::setDelayScaling(double scale, double offset) args.delayOffset = offset; } +void Arch::addCellTimingCombIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_INPUT; } + +void Arch::addCellTimingCombOut(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_OUTPUT; } + +void Arch::addCellTimingRegIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT; } + +void Arch::addCellTimingRegOut(IdString cell, IdString port) +{ + cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT; +} + +void Arch::addCellTimingIO(IdString cell, IdString port) +{ + if (port == id_I) { + cellTiming[cell].portClasses[port] = TMG_ENDPOINT; + } else { + if (port == id_O) { + cellTiming[cell].portClasses[port] = TMG_STARTPOINT; + } + } +} + void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; } void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay) @@ -1989,6 +2012,8 @@ void Arch::assignArchInfo() // add timing paths addCellTimingClock(cname, id_CLK); + addCellTimingRegIn(cname, id_CE); + addCellTimingRegIn(cname, id_LSR); IdString ports[4] = {id_A, id_B, id_C, id_D}; for (int i = 0; i < 4; i++) { DelayPair setup = @@ -2019,7 +2044,18 @@ void Arch::assignArchInfo() delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); addCellTimingDelay(cname, id_I0, id_OF, delay); addCellTimingDelay(cname, id_I1, id_OF, delay); + addCellTimingCombIn(cname, id_SEL); } + case ID_IOB: + /* FALLTHRU */ + case ID_IOBS: + addCellTimingIO(cname, id_I); + addCellTimingIO(cname, id_O); + break; + case ID_BUFS: + addCellTimingCombIn(cname, id_I); + addCellTimingCombOut(cname, id_O); + break; default: break; } @@ -2063,4 +2099,22 @@ bool Arch::cellsCompatible(const CellInfo **cells, int count) const return true; } +void Arch::route_gowin_globals(Context *ctx) { globals_router.route_globals(ctx); } + +void Arch::mark_gowin_globals(Context *ctx) { globals_router.mark_globals(ctx); } +// --------------------------------------------------------------- +void Arch::pre_pack(Context *ctx) +{ + if (bool_or_default(settings, id("arch.enable-auto-longwires"))) { + auto_longwires(); + } +} + +void Arch::post_pack(Context *ctx) +{ + if (bool_or_default(settings, id("arch.enable-globals"))) { + mark_gowin_globals(ctx); + } +} + NEXTPNR_NAMESPACE_END diff --git a/gowin/arch.h b/gowin/arch.h index c59f8eb353..cd43aa7a90 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -31,6 +31,8 @@ #include "nextpnr_namespaces.h" #include "nextpnr_types.h" +#include "globals.h" + NEXTPNR_NAMESPACE_BEGIN template struct RelPtr @@ -337,6 +339,11 @@ struct Arch : BaseArch void setDelayScaling(double scale, double offset); void addCellTimingClock(IdString cell, IdString port); + void addCellTimingIO(IdString cell, IdString port); + void addCellTimingCombIn(IdString cell, IdString port); + void addCellTimingCombOut(IdString cell, IdString port); + void addCellTimingRegIn(IdString cell, IdString port); + void addCellTimingRegOut(IdString cell, IdString port); void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay); void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold); void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayQuad clktoq); @@ -462,6 +469,13 @@ struct Arch : BaseArch bool haveBelType(int x, int y, IdString bel_type); bool allocate_longwire(NetInfo *ni, int lw_idx = -1); void fix_longwire_bels(); + void pre_pack(Context *ctx); + void post_pack(Context *ctx); + void auto_longwires(); + + GowinGlobalRouter globals_router; + void mark_gowin_globals(Context *ctx); + void route_gowin_globals(Context *ctx); // chip db version unsigned int const chipdb_version = 1; diff --git a/gowin/globals.cc b/gowin/globals.cc index ed75a93888..1794dd4c39 100644 --- a/gowin/globals.cc +++ b/gowin/globals.cc @@ -18,7 +18,6 @@ * */ -#include "globals.h" #include #include #include @@ -31,306 +30,279 @@ NEXTPNR_NAMESPACE_BEGIN -class GowinGlobalRouter +bool GowinGlobalRouter::is_clock_port(PortRef const &user) { - public: - GowinGlobalRouter(Context *ctx) : ctx(ctx){}; - - private: - // wire -> clock# - dict used_wires; - - // ordered nets - struct onet_t - { - IdString name; - int clock_ports; - WireId clock_io_wire; // IO wire if there is one - - onet_t() : name(IdString()), clock_ports(0), clock_io_wire(WireId()) {} - onet_t(IdString _name) : name(_name), clock_ports(0), clock_io_wire(WireId()) {} - - // sort - bool operator<(const onet_t &other) const - { - if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) { - return !(clock_io_wire != WireId()); - } - return clock_ports < other.clock_ports; - } - // search - bool operator==(const onet_t &other) const { return name == other.name; } - }; + if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && + user.port == id_CLK) { + return true; + } + return false; +} - bool is_clock_port(PortRef const &user) - { - if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && user.port == id_CLK) { - return true; - } - return false; +std::pair GowinGlobalRouter::clock_io(Context *ctx, PortRef const &driver) +{ + // XXX normally all alternative functions of the pins should be passed + // in the chip database, but at the moment we find them from aliases/pips + // XXX check diff inputs too + if (driver.cell == nullptr || driver.cell->type != id_IOB || !driver.cell->attrs.count(id_BEL)) { + return std::make_pair(WireId(), BelId()); } + // clock IOs have pips output->SPINExx - WireId clock_io(PortRef const &driver) - { - // XXX normally all alternative functions of the pins should be passed - // in the chip database, but at the moment we find them from aliases/pips - // XXX check diff inputs too - if (driver.cell == nullptr || driver.cell->bel == BelId()) { - return WireId(); - } - // clock IOs have pips output->SPINExx - BelInfo &bel = ctx->bel_info(driver.cell->bel); - if (bel.type != id_IOB) { - return WireId(); + BelInfo &bel = ctx->bel_info(ctx->id(driver.cell->attrs[id_BEL].as_string())); + WireId wire = bel.pins[id_O].wire; + for (auto const pip : ctx->getPipsDownhill(wire)) { + if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { + return std::make_pair(wire, bel.name); } - WireId wire = bel.pins[id_O].wire; - for (auto const pip : ctx->getPipsDownhill(wire)) { - if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { - return wire; - } - } - return WireId(); } + return std::make_pair(WireId(), BelId()); +} - // gather the clock nets - void gather_clock_nets(std::vector &clock_nets) - { - for (auto const &net : ctx->nets) { - NetInfo const *ni = net.second.get(); - auto new_clock = clock_nets.end(); - WireId clock_wire = clock_io(ni->driver); - if (clock_wire != WireId()) { - clock_nets.emplace_back(net.first); - new_clock = --clock_nets.end(); - new_clock->clock_io_wire = clock_wire; - } - for (auto const &user : ni->users) { - if (is_clock_port(user)) { - if (new_clock == clock_nets.end()) { - clock_nets.emplace_back(net.first); - new_clock = --clock_nets.end(); - } - ++(new_clock->clock_ports); +// gather the clock nets +void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector &clock_nets) +{ + for (auto const &net : ctx->nets) { + NetInfo const *ni = net.second.get(); + auto new_clock = clock_nets.end(); + auto clock_wire_bel = clock_io(ctx, ni->driver); + if (clock_wire_bel.first != WireId()) { + clock_nets.emplace_back(net.first); + new_clock = --clock_nets.end(); + new_clock->clock_io_wire = clock_wire_bel.first; + new_clock->clock_io_bel = clock_wire_bel.second; + } + for (auto const &user : ni->users) { + if (is_clock_port(user)) { + if (new_clock == clock_nets.end()) { + clock_nets.emplace_back(net.first); + new_clock = --clock_nets.end(); } + ++(new_clock->clock_ports); } } - // need to prioritize the nets - std::sort(clock_nets.begin(), clock_nets.end()); + } + // need to prioritize the nets + std::sort(clock_nets.begin(), clock_nets.end()); - if (ctx->verbose) { - for (auto const &net : clock_nets) { - log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports, - net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx)); - } + if (ctx->verbose) { + for (auto const &net : clock_nets) { + log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports, + net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx)); } } +} - // non clock port - // returns GB pip - IdString route_to_non_clock_port(WireId const dstWire, int clock, pool &used_pips, - pool &undo_wires) - { - static std::vector one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; - char buf[40]; - // uphill pips - for (auto const uphill : ctx->getPipsUphill(dstWire)) { - WireId srcWire = ctx->getPipSrcWire(uphill); - if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != - one_hop.end()) { - // found one hop pip - if (used_wires.count(srcWire)) { - if (used_wires[srcWire] != clock) { - continue; - } +// non clock port +// returns GB pip +IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, + pool &used_pips, pool &undo_wires) +{ + static std::vector one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; + char buf[40]; + // uphill pips + for (auto const uphill : ctx->getPipsUphill(dstWire)) { + WireId srcWire = ctx->getPipSrcWire(uphill); + if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != one_hop.end()) { + // found one hop pip + if (used_wires.count(srcWire)) { + if (used_wires[srcWire] != clock) { + continue; + } + } + WireInfo wi = ctx->wire_info(srcWire); + std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1); + snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str()); + IdString gb = ctx->id(buf); + auto up_pips = ctx->getPipsUphill(srcWire); + if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) { + if (!used_wires.count(srcWire)) { + used_wires.insert(std::make_pair(srcWire, clock)); + undo_wires.insert(srcWire); } - WireInfo wi = ctx->wire_info(srcWire); - std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1); - snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str()); - IdString gb = ctx->id(buf); - auto up_pips = ctx->getPipsUphill(srcWire); - if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) { - if (!used_wires.count(srcWire)) { - used_wires.insert(std::make_pair(srcWire, clock)); - undo_wires.insert(srcWire); - } - used_pips.insert(uphill); - if (ctx->verbose) { - log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx)); - } - return gb; + used_pips.insert(uphill); + if (ctx->verbose) { + log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx)); } + return gb; } } - return IdString(); } + return IdString(); +} - // route one net - void route_net(onet_t const &net, int clock) - { - // For failed routing undo - pool used_pips; - pool undo_wires; - - log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), clock); - for (auto const &user : ctx->net_info(net.name).users) { - // >>> port <- GB0 - WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0); - if (ctx->verbose) { - log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), - dstWire.c_str(ctx)); - } - - char buf[30]; - PipId gb_pip_id; - if (user.port == id_CLK) { - WireInfo const wi = ctx->wire_info(dstWire); - snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, - ctx->wire_info(dstWire).type.c_str(ctx)); - gb_pip_id = ctx->id(buf); - // sanity - NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) != - ctx->getPipsUphill(dstWire).end()); - } else { - // Non clock port - gb_pip_id = route_to_non_clock_port(dstWire, clock, used_pips, undo_wires); - if (gb_pip_id == IdString()) { - if (ctx->verbose) { - log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", - dstWire.c_str(ctx), net.name.c_str(ctx)); - } - for (IdString const undo : undo_wires) { - used_wires.erase(undo); - } - return; - } - } - if (ctx->verbose) { - log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx)); - } +// route one net +void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net) +{ + // For failed routing undo + pool used_pips; + pool undo_wires; - if (used_pips.count(gb_pip_id)) { - if (ctx->verbose) { - log_info(" ^routed already^\n"); - } - continue; - } - used_pips.insert(gb_pip_id); + log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), net.clock); + for (auto const &user : ctx->net_info(net.name).users) { + // >>> port <- GB0 + WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0); + if (ctx->verbose) { + log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), + dstWire.c_str(ctx)); + } - // >>> GBOx <- GTx0 - dstWire = ctx->getPipSrcWire(gb_pip_id); - WireInfo dstWireInfo = ctx->wire_info(dstWire); - int branch_tap_idx = clock > 3 ? 1 : 0; - snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, - branch_tap_idx); - PipId gt_pip_id = ctx->id(buf); - if (ctx->verbose) { - log_info(" GT Pip:%s\n", buf); - } + char buf[30]; + PipId gb_pip_id; + if (user.port == id_CLK) { + WireInfo const wi = ctx->wire_info(dstWire); + snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, net.clock, + ctx->wire_info(dstWire).type.c_str(ctx)); + gb_pip_id = ctx->id(buf); // sanity - NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) != + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) != ctx->getPipsUphill(dstWire).end()); - // if already routed - if (used_pips.count(gt_pip_id)) { + } else { + // Non clock port + gb_pip_id = route_to_non_clock_port(ctx, dstWire, net.clock, used_pips, undo_wires); + if (gb_pip_id == IdString()) { if (ctx->verbose) { - log_info(" ^routed already^\n"); + log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", dstWire.c_str(ctx), + net.name.c_str(ctx)); } - continue; - } - used_pips.insert(gt_pip_id); - - // >>> GTx0 <- SPINExx - // XXX no optimization here, we need to store - // the SPINE <-> clock# correspondence in the database. In the - // meantime, we define in run-time in a completely suboptimal way. - std::vector clock_spine; - dstWire = ctx->getPipSrcWire(gt_pip_id); - for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { - std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx); - if (name.rfind("SPINE", 0) == 0) { - clock_spine.push_back(name); + for (IdString const undo : undo_wires) { + used_wires.erase(undo); } + return; } - sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool { - return (a.size() < b.size()) || (a.size() == b.size() && a < b); - }); - dstWireInfo = ctx->wire_info(dstWire); - snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1, - clock_spine[clock - branch_tap_idx * 4].c_str(), branch_tap_idx); - PipId spine_pip_id = ctx->id(buf); + } + if (ctx->verbose) { + log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx)); + } + + if (used_pips.count(gb_pip_id)) { if (ctx->verbose) { - log_info(" Spine Pip:%s\n", buf); + log_info(" ^routed already^\n"); } - // sanity - NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) != - ctx->getPipsUphill(dstWire).end()); - // if already routed - if (used_pips.count(spine_pip_id)) { - if (ctx->verbose) { - log_info(" ^routed already^\n"); - } - continue; + continue; + } + used_pips.insert(gb_pip_id); + + // >>> GBOx <- GTx0 + dstWire = ctx->getPipSrcWire(gb_pip_id); + WireInfo dstWireInfo = ctx->wire_info(dstWire); + int branch_tap_idx = net.clock > 3 ? 1 : 0; + snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, + branch_tap_idx); + PipId gt_pip_id = ctx->id(buf); + if (ctx->verbose) { + log_info(" GT Pip:%s\n", buf); + } + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) != + ctx->getPipsUphill(dstWire).end()); + // if already routed + if (used_pips.count(gt_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); } - used_pips.insert(spine_pip_id); + continue; + } + used_pips.insert(gt_pip_id); - // >>> SPINExx <- IO - dstWire = ctx->getPipSrcWire(spine_pip_id); - dstWireInfo = ctx->wire_info(dstWire); - PipId io_pip_id = PipId(); - for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { - if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { - io_pip_id = uphill_pip; - } + // >>> GTx0 <- SPINExx + // XXX no optimization here, we need to store + // the SPINE <-> clock# correspondence in the database. In the + // meantime, we define in run-time in a completely suboptimal way. + std::vector clock_spine; + dstWire = ctx->getPipSrcWire(gt_pip_id); + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { + std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx); + if (name.rfind("SPINE", 0) == 0) { + clock_spine.push_back(name); } - NPNR_ASSERT(io_pip_id != PipId()); + } + sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool { + return (a.size() < b.size()) || (a.size() == b.size() && a < b); + }); + dstWireInfo = ctx->wire_info(dstWire); + snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1, + clock_spine[net.clock - branch_tap_idx * 4].c_str(), branch_tap_idx); + PipId spine_pip_id = ctx->id(buf); + if (ctx->verbose) { + log_info(" Spine Pip:%s\n", buf); + } + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) != + ctx->getPipsUphill(dstWire).end()); + // if already routed + if (used_pips.count(spine_pip_id)) { if (ctx->verbose) { - log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx)); + log_info(" ^routed already^\n"); } - // if already routed - if (used_pips.count(io_pip_id)) { - if (ctx->verbose) { - log_info(" ^routed already^\n"); - } - continue; + continue; + } + used_pips.insert(spine_pip_id); + + // >>> SPINExx <- IO + dstWire = ctx->getPipSrcWire(spine_pip_id); + dstWireInfo = ctx->wire_info(dstWire); + PipId io_pip_id = PipId(); + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { + if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { + io_pip_id = uphill_pip; } - used_pips.insert(io_pip_id); } - log_info(" Net %s is routed.\n", net.name.c_str(ctx)); - for (auto const pip : used_pips) { - ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED); + NPNR_ASSERT(io_pip_id != PipId()); + if (ctx->verbose) { + log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx)); + } + // if already routed + if (used_pips.count(io_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + continue; } - ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED); + used_pips.insert(io_pip_id); } + log_info(" Net %s is routed.\n", net.name.c_str(ctx)); + for (auto const pip : used_pips) { + ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED); + } + ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED); +} - public: - Context *ctx; - void route_globals() - { - log_info("Routing globals...\n"); +void GowinGlobalRouter::route_globals(Context *ctx) +{ + log_info("Routing globals...\n"); - std::vector clock_nets; - gather_clock_nets(clock_nets); - // XXX we need to use the list of indexes of clocks from the database - // use 6 clocks (XXX 3 for GW1NZ-1) - int max_clock = 3, cur_clock = -1; - for (auto const &net : clock_nets) { - // XXX only IO clock for now - if (net.clock_io_wire == WireId()) { - log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx)); - continue; - } - if (++cur_clock >= max_clock) { - log_info(" No more clock wires left, skip the remaining nets.\n"); - break; - } - route_net(net, cur_clock); - } + for (auto const &net : nets) { + route_net(ctx, net); } -}; +} -void route_gowin_globals(Context *ctx) +// Allocate networks that will be routed through the global system. +// Mark their driver cells as global buffers to exclude them from the analysis. +void GowinGlobalRouter::mark_globals(Context *ctx) { - GowinGlobalRouter router(ctx); - router.route_globals(); + log_info("Find global nets...\n"); + + std::vector clock_nets; + gather_clock_nets(ctx, clock_nets); + // XXX we need to use the list of indexes of clocks from the database + // use 6 clocks (XXX 3 for GW1NZ-1) + int max_clock = 3, cur_clock = -1; + for (auto &net : clock_nets) { + // XXX only IO clock for now + if (net.clock_io_wire == WireId()) { + log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx)); + continue; + } + if (++cur_clock >= max_clock) { + log_info(" No more clock wires left, skip the remaining nets.\n"); + break; + } + net.clock = cur_clock; + BelInfo &bi = ctx->bel_info(net.clock_io_bel); + bi.gb = true; + nets.emplace_back(net); + } } NEXTPNR_NAMESPACE_END diff --git a/gowin/globals.h b/gowin/globals.h index 41a8727abe..69232d7c88 100644 --- a/gowin/globals.h +++ b/gowin/globals.h @@ -18,10 +18,74 @@ * */ +#ifndef GOWIN_GLOBALS_H +#define GOWIN_GLOBALS_H + #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN -void route_gowin_globals(Context *ctx); +class GowinGlobalRouter +{ + public: + GowinGlobalRouter() {} + + private: + // wire -> clock# + dict used_wires; + + // ordered nets + struct globalnet_t + { + IdString name; + int clock_ports; + BelId clock_io_bel; + WireId clock_io_wire; // IO wire if there is one + int clock; // clock # + + globalnet_t() + { + name = IdString(); + clock_ports = 0; + clock_io_bel = BelId(); + clock_io_wire = WireId(); + clock = -1; + } + globalnet_t(IdString _name) + { + name = _name; + clock_ports = 0; + clock_io_bel = BelId(); + clock_io_wire = WireId(); + clock = -1; + } + + // sort + bool operator<(const globalnet_t &other) const + { + if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) { + return !(clock_io_wire != WireId()); + } + return clock_ports < other.clock_ports; + } + // search + bool operator==(const globalnet_t &other) const { return name == other.name; } + }; + + // discovered nets + std::vector nets; + + bool is_clock_port(PortRef const &user); + std::pair clock_io(Context *ctx, PortRef const &driver); + void gather_clock_nets(Context *ctx, std::vector &clock_nets); + IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool &used_pips, + pool &undo_wires); + void route_net(Context *ctx, globalnet_t const &net); + + public: + void mark_globals(Context *ctx); + void route_globals(Context *ctx); +}; NEXTPNR_NAMESPACE_END +#endif // GOWIN_GLOBALS_H diff --git a/gowin/pack.cc b/gowin/pack.cc index 838201423d..5b304f10b4 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -26,6 +26,8 @@ #include "log.h" #include "util.h" +#include "globals.h" + NEXTPNR_NAMESPACE_BEGIN static void make_dummy_alu(Context *ctx, int alu_idx, CellInfo *ci, CellInfo *packed_head, @@ -1009,6 +1011,7 @@ bool Arch::pack() Context *ctx = getCtx(); try { log_break(); + pre_pack(ctx); pack_constants(ctx); pack_gsr(ctx); pack_io(ctx); @@ -1018,6 +1021,7 @@ bool Arch::pack() pack_alus(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); + post_pack(ctx); ctx->settings[id_pack] = 1; ctx->assignArchInfo(); log_info("Checksum: 0x%08x\n", ctx->checksum()); From 5d915a550ba8a825fa29a2cab3851adea20f40fb Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 4 Jul 2022 11:23:21 +1000 Subject: [PATCH 161/712] gowin: fix compilation Signed-off-by: YRabbit --- gowin/arch.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index a968c112bb..cdb932a621 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -2045,6 +2045,7 @@ void Arch::assignArchInfo() addCellTimingDelay(cname, id_I0, id_OF, delay); addCellTimingDelay(cname, id_I1, id_OF, delay); addCellTimingCombIn(cname, id_SEL); + break; } case ID_IOB: /* FALLTHRU */ From e1ba379fb7633c7bc3f0545bd62906f1b0665b4b Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 4 Jul 2022 18:24:15 +0200 Subject: [PATCH 162/712] generic: Use arch_pybindings_shared Signed-off-by: gatecat --- common/kernel/arch_pybindings_shared.h | 6 ++ generic/arch_pybindings.cc | 117 +++---------------------- 2 files changed, 18 insertions(+), 105 deletions(-) diff --git a/common/kernel/arch_pybindings_shared.h b/common/kernel/arch_pybindings_shared.h index b3dc0506d0..bfb58f11ab 100644 --- a/common/kernel/arch_pybindings_shared.h +++ b/common/kernel/arch_pybindings_shared.h @@ -145,3 +145,9 @@ fn_wrapper_1a, conv_from_str, conv_from_str>::def_wrap(ctx_cls, "isValidBelForCellType"); + +fn_wrapper_1a, + pass_through>::def_wrap(ctx_cls, "getDelayFromNS"); + +fn_wrapper_1a, + pass_through>::def_wrap(ctx_cls, "getDelayNS"); \ No newline at end of file diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index 6d3d981c53..e0a2f0f285 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -46,6 +46,13 @@ void arch_wrap_python(py::module &m) typedef linear_range WireRange; typedef linear_range AllPipRange; + typedef const std::vector &UphillPipRange; + typedef const std::vector &DownhillPipRange; + + typedef const std::vector &BelBucketRange; + typedef const std::vector &BelRangeForBelBucket; + typedef const std::vector &BelPinRange; + auto arch_cls = py::class_(m, "Arch").def(py::init()); auto dxy_cls = py::class_>(m, "DecalXY_"); @@ -62,82 +69,8 @@ void arch_wrap_python(py::module &m) .def("place", &Context::place) .def("route", &Context::route); - py::class_(m, "BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); - - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBelType"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "checkBelAvail"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBelChecksum"); - fn_wrapper_3a_v, - addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindBel"); - fn_wrapper_1a_v>::def_wrap( - ctx_cls, "unbindBel"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBoundBelCell"); - fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); - fn_wrapper_0a>::def_wrap(ctx_cls, - "getBels"); - - fn_wrapper_2a, - conv_from_str, conv_from_str>::def_wrap(ctx_cls, "getBelPinWire"); - fn_wrapper_1a &>, conv_from_str>::def_wrap(ctx_cls, - "getWireBelPins"); - - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getWireChecksum"); - fn_wrapper_3a_v, - addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindWire"); - fn_wrapper_1a_v>::def_wrap( - ctx_cls, "unbindWire"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "checkWireAvail"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBoundWireNet"); - fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingWireNet"); - - fn_wrapper_0a>::def_wrap( - ctx_cls, "getWires"); - - fn_wrapper_0a>::def_wrap( - ctx_cls, "getPips"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getPipChecksum"); - fn_wrapper_3a_v, - addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindPip"); - fn_wrapper_1a_v>::def_wrap( - ctx_cls, "unbindPip"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "checkPipAvail"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getBoundPipNet"); - fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingPipNet"); - - fn_wrapper_1a &>, conv_from_str>::def_wrap(ctx_cls, - "getPipsDownhill"); - fn_wrapper_1a &>, conv_from_str>::def_wrap(ctx_cls, "getPipsUphill"); - - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getPipSrcWire"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getPipDstWire"); - fn_wrapper_1a, - conv_from_str>::def_wrap(ctx_cls, "getPipDelay"); - - fn_wrapper_1a, - pass_through>::def_wrap(ctx_cls, "getDelayFromNS"); - - fn_wrapper_0a>::def_wrap( - ctx_cls, "getChipName"); - fn_wrapper_0a>::def_wrap(ctx_cls, - "archId"); + auto belpin_cls = + py::class_(m, "BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); fn_wrapper_3a, conv_from_str, pass_through, pass_through>::def_wrap(ctx_cls, "DecalXY"); @@ -145,14 +78,10 @@ void arch_wrap_python(py::module &m) typedef dict> CellMap; typedef dict> NetMap; typedef dict HierarchyMap; + typedef dict AliasMap; + typedef dict HierarchyMap; - readonly_wrapper>::def_wrap(ctx_cls, - "cells"); - readonly_wrapper>::def_wrap(ctx_cls, - "nets"); - - fn_wrapper_2a_v, - pass_through>::def_wrap(ctx_cls, "addClock"); +#include "arch_pybindings_shared.h" // Generic arch construction API fn_wrapper_4a_v, @@ -236,28 +165,6 @@ void arch_wrap_python(py::module &m) conv_from_str>::def_wrap(ctx_cls, "addCellBelPinMapping", "cell"_a, "cell_pin"_a, "bel_pin"_a); - // const\_range\ getBelBuckets() const - fn_wrapper_0a &>>::def_wrap(ctx_cls, "getBelBuckets"); - - // BelBucketId getBelBucketForBel(BelId bel) const - fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getBelBucketForBel"); - - // BelBucketId getBelBucketForCellType(IdString cell\_type) const - fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getBelBucketForCellType"); - - // const\_range\ getBelsInBucket(BelBucketId bucket) const - fn_wrapper_1a &>, conv_from_str>::def_wrap(ctx_cls, - "getBelsInBucket"); - - // bool isValidBelForCellType(IdString cell\_type, BelId bel) const - fn_wrapper_2a, conv_from_str, conv_from_str>::def_wrap(ctx_cls, - "isValidBelForCellType"); - WRAP_RANGE(m, Bel, conv_to_str); WRAP_RANGE(m, Wire, conv_to_str); WRAP_RANGE(m, AllPip, conv_to_str); From 9914f56137e152a46a921f88bc894bbced3d4c27 Mon Sep 17 00:00:00 2001 From: Gabriel Somlo Date: Mon, 4 Jul 2022 13:43:46 -0400 Subject: [PATCH 163/712] Enable building against unbundled pybind11 Signed-off-by: Gabriel Somlo --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e000144784..3812f7dcc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,7 +216,12 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/common/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/version.h ) -include_directories(common/kernel/ common/place/ common/route/ json/ frontend/ 3rdparty/json11/ 3rdparty/pybind11/include ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) +if (NOT DEFINED PYBIND11_INCLUDE_DIR) + # Use bundled pybind11 + set(PYBIND11_INCLUDE_DIR "3rdparty/pybind11/include") +endif() + +include_directories(common/kernel/ common/place/ common/route/ json/ frontend/ 3rdparty/json11/ ${PYBIND11_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) if(BUILD_HEAP) find_package (Eigen3 REQUIRED NO_MODULE) From 1ebfe67daf2ec3e1e64150f09ab4c194f41d1d9d Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 5 Jul 2022 20:02:12 +1000 Subject: [PATCH 164/712] gowin: Remove unnecessary functions Signed-off-by: YRabbit --- gowin/arch.cc | 36 ++++++++---------------------------- gowin/arch.h | 6 +----- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 82b44f91dc..6fe40f76d6 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -553,27 +553,7 @@ void Arch::setDelayScaling(double scale, double offset) args.delayOffset = offset; } -void Arch::addCellTimingCombIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_INPUT; } - -void Arch::addCellTimingCombOut(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_OUTPUT; } - -void Arch::addCellTimingRegIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT; } - -void Arch::addCellTimingRegOut(IdString cell, IdString port) -{ - cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT; -} - -void Arch::addCellTimingIO(IdString cell, IdString port) -{ - if (port == id_I) { - cellTiming[cell].portClasses[port] = TMG_ENDPOINT; - } else { - if (port == id_O) { - cellTiming[cell].portClasses[port] = TMG_STARTPOINT; - } - } -} +void Arch::addCellTimingClass(IdString cell, IdString port, TimingPortClass cls) {cellTiming[cell].portClasses[port] = cls;} void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; } @@ -2042,8 +2022,8 @@ void Arch::assignArchInfo() // add timing paths addCellTimingClock(cname, id_CLK); - addCellTimingRegIn(cname, id_CE); - addCellTimingRegIn(cname, id_LSR); + addCellTimingClass(cname, id_CE, TMG_REGISTER_INPUT); + addCellTimingClass(cname, id_LSR, TMG_REGISTER_INPUT); IdString ports[4] = {id_A, id_B, id_C, id_D}; for (int i = 0; i < 4; i++) { DelayPair setup = @@ -2074,18 +2054,18 @@ void Arch::assignArchInfo() delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); addCellTimingDelay(cname, id_I0, id_OF, delay); addCellTimingDelay(cname, id_I1, id_OF, delay); - addCellTimingCombIn(cname, id_SEL); + addCellTimingClass(cname, id_SEL, TMG_COMB_INPUT); break; } case ID_IOB: /* FALLTHRU */ case ID_IOBS: - addCellTimingIO(cname, id_I); - addCellTimingIO(cname, id_O); + addCellTimingClass(cname, id_I, TMG_ENDPOINT); + addCellTimingClass(cname, id_O, TMG_STARTPOINT); break; case ID_BUFS: - addCellTimingCombIn(cname, id_I); - addCellTimingCombOut(cname, id_O); + addCellTimingClass(cname, id_I, TMG_ENDPOINT); + addCellTimingClass(cname, id_O, TMG_STARTPOINT); break; default: break; diff --git a/gowin/arch.h b/gowin/arch.h index d02a2488ef..034e4b8604 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -339,11 +339,7 @@ struct Arch : BaseArch void setDelayScaling(double scale, double offset); void addCellTimingClock(IdString cell, IdString port); - void addCellTimingIO(IdString cell, IdString port); - void addCellTimingCombIn(IdString cell, IdString port); - void addCellTimingCombOut(IdString cell, IdString port); - void addCellTimingRegIn(IdString cell, IdString port); - void addCellTimingRegOut(IdString cell, IdString port); + void addCellTimingClass(IdString cell, IdString port, TimingPortClass cls); void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay); void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold); void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayQuad clktoq); From 09e388f453d9cf998391495349c88e5478b62e34 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 23 Jun 2022 18:48:31 +0100 Subject: [PATCH 165/712] netlist: Add PseudoCell API When implementing concepts such as partition pins or deliberately split nets, there's a need for something that looks like a cell (starts/ends routing with pins on nets, has timing data) but isn't mapped to a fixed bel in the architecture, but instead can have pin mappings defined at runtime. The PseudoCell allows this by providing an alternate, virtual-function based API for such cells. When a cell has `pseudo_cell` used, instead of calling functions such as getBelPinWire, getBelLocation or getCellDelay in the Arch API; such data is provided by the cell itself, fully flexible at runtime regardless of arch, via methods on the PseudoCell implementation. --- common/kernel/arch_pybindings_shared.h | 8 ++- common/kernel/basectx.cc | 24 +++++++ common/kernel/basectx.h | 4 ++ common/kernel/context.cc | 5 ++ common/kernel/context.h | 18 ++++++ common/kernel/nextpnr_types.cc | 10 +++ common/kernel/nextpnr_types.h | 86 ++++++++++++++++++-------- common/place/detail_place_core.cc | 12 +++- common/place/parallel_refine.cc | 2 + common/place/place_common.cc | 6 +- common/place/placer1.cc | 20 +++--- common/place/placer_heap.cc | 24 ++++++- common/route/router2.cc | 4 +- docs/netlist.md | 16 ++++- fpga_interchange/arch.h | 6 +- generic/arch.cc | 2 +- 16 files changed, 198 insertions(+), 49 deletions(-) diff --git a/common/kernel/arch_pybindings_shared.h b/common/kernel/arch_pybindings_shared.h index bfb58f11ab..d78d240c90 100644 --- a/common/kernel/arch_pybindings_shared.h +++ b/common/kernel/arch_pybindings_shared.h @@ -150,4 +150,10 @@ fn_wrapper_1a>::def_wrap(ctx_cls, "getDelayFromNS"); fn_wrapper_1a, - pass_through>::def_wrap(ctx_cls, "getDelayNS"); \ No newline at end of file + pass_through>::def_wrap(ctx_cls, "getDelayNS"); + +fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "createRegionPlug"); +fn_wrapper_4a_v, + conv_from_str, pass_through, conv_from_str>::def_wrap(ctx_cls, + "addPlugPin"); diff --git a/common/kernel/basectx.cc b/common/kernel/basectx.cc index 83a2deea7b..82cdd835b9 100644 --- a/common/kernel/basectx.cc +++ b/common/kernel/basectx.cc @@ -131,6 +131,30 @@ void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name) if (!matched) log_warning("No cell matched '%s' when constraining to region '%s'\n", nameOf(cell), nameOf(region_name)); } + +void BaseCtx::createRegionPlug(IdString name, IdString type, Loc approx_loc) +{ + CellInfo *cell = nullptr; + if (cells.count(name)) + cell = cells.at(name).get(); + else + cell = createCell(name, type); + cell->pseudo_cell = std::make_unique(approx_loc); +} + +void BaseCtx::addPlugPin(IdString plug, IdString pin, PortType dir, WireId wire) +{ + if (!cells.count(plug)) + log_error("no cell named '%s' found\n", plug.c_str(this)); + CellInfo *ci = cells.at(plug).get(); + RegionPlug *rplug = dynamic_cast(ci->pseudo_cell.get()); + if (!rplug) + log_error("cell '%s' is not a RegionPlug\n", plug.c_str(this)); + rplug->port_wires[pin] = wire; + ci->ports[pin].name = pin; + ci->ports[pin].type = dir; +} + DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y) { DecalXY dxy; diff --git a/common/kernel/basectx.h b/common/kernel/basectx.h index 21d6d63a9b..5775e47f21 100644 --- a/common/kernel/basectx.h +++ b/common/kernel/basectx.h @@ -220,6 +220,10 @@ struct BaseCtx void addBelToRegion(IdString name, BelId bel); void constrainCellToRegion(IdString cell, IdString region_name); + // Helper functions for the partial reconfiguration plug API using PseudoCells + void createRegionPlug(IdString name, IdString type, Loc approx_loc); + void addPlugPin(IdString plug, IdString pin, PortType dir, WireId wire); + // Helper functions for Python bindings NetInfo *createNet(IdString name); void connectPort(IdString net, IdString cell, IdString port); diff --git a/common/kernel/context.cc b/common/kernel/context.cc index e35d3e49ae..014394a60d 100644 --- a/common/kernel/context.cc +++ b/common/kernel/context.cc @@ -30,6 +30,9 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const if (net_info->driver.cell == nullptr) return WireId(); + if (net_info->driver.cell->isPseudo()) + return net_info->driver.cell->pseudo_cell->getPortWire(net_info->driver.port); + auto src_bel = net_info->driver.cell->bel; if (src_bel == BelId()) @@ -47,6 +50,8 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const SSOArray Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const { + if (user_info.cell->isPseudo()) + return SSOArray(1, user_info.cell->pseudo_cell->getPortWire(user_info.port)); auto dst_bel = user_info.cell->bel; if (dst_bel == BelId()) return SSOArray(0, WireId()); diff --git a/common/kernel/context.h b/common/kernel/context.h index cb8fd25707..4a5667d039 100644 --- a/common/kernel/context.h +++ b/common/kernel/context.h @@ -64,6 +64,24 @@ struct Context : Arch, DeterministicRNG bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr, dict *route = nullptr, bool useEstimate = true); + // -------------------------------------------------------------- + // Dispatch to the Arch API or pseudo-cell API accordingly + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const override + { + return cell->pseudo_cell ? cell->pseudo_cell->getDelay(fromPort, toPort, delay) + : Arch::getCellDelay(cell, fromPort, toPort, delay); + } + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const override + { + return cell->pseudo_cell ? cell->pseudo_cell->getPortTimingClass(port, clockInfoCount) + : Arch::getPortTimingClass(cell, port, clockInfoCount); + } + TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override + { + return cell->pseudo_cell ? cell->pseudo_cell->getPortClockingInfo(port, index) + : Arch::getPortClockingInfo(cell, port, index); + } + // -------------------------------------------------------------- // call after changing hierpath or adding/removing nets and cells void fixupHierarchy(); diff --git a/common/kernel/nextpnr_types.cc b/common/kernel/nextpnr_types.cc index 82725d6ff5..8563eb27ad 100644 --- a/common/kernel/nextpnr_types.cc +++ b/common/kernel/nextpnr_types.cc @@ -177,4 +177,14 @@ void CellInfo::copyPortBusTo(IdString old_name, int old_offset, bool old_bracket } } +Loc CellInfo::getLocation() const +{ + if (pseudo_cell) { + return pseudo_cell->getLocation(); + } else { + NPNR_ASSERT(bel != BelId()); + return ctx->getBelLocation(bel); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/kernel/nextpnr_types.h b/common/kernel/nextpnr_types.h index c21182cc97..e4042aecc3 100644 --- a/common/kernel/nextpnr_types.h +++ b/common/kernel/nextpnr_types.h @@ -160,6 +160,59 @@ struct PortInfo struct Context; +enum TimingPortClass +{ + TMG_CLOCK_INPUT, // Clock input to a sequential cell + TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc) + TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too) + TMG_REGISTER_OUTPUT, // Output from a register + TMG_COMB_INPUT, // Combinational input, no paths end here + TMG_COMB_OUTPUT, // Combinational output, no paths start here + TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output + TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input + TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis +}; + +enum ClockEdge +{ + RISING_EDGE, + FALLING_EDGE +}; + +struct TimingClockingInfo +{ + IdString clock_port; // Port name of clock domain + ClockEdge edge; + DelayPair setup, hold; // Input timing checks + DelayQuad clockToQ; // Output clock-to-Q time +}; + +struct PseudoCell +{ + virtual Loc getLocation() const = 0; + virtual WireId getPortWire(IdString port) const = 0; + + virtual bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const = 0; + virtual TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const = 0; + virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const = 0; + virtual ~PseudoCell(){}; +}; + +struct RegionPlug : PseudoCell +{ + RegionPlug(Loc loc) : loc(loc){}; // 'loc' is a notional location for the placer only + Loc getLocation() const override { return loc; } + WireId getPortWire(IdString port) const override { return port_wires.at(port); } + + // TODO: partial reconfiguration region timing + bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const { return false; } + TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const { return TMG_IGNORE; } + virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const { return TimingClockingInfo{}; } + + dict port_wires; + Loc loc; +}; + struct CellInfo : ArchCellInfo { CellInfo(Context *ctx, IdString name, IdString type) : ctx(ctx), name(name), type(type){}; @@ -179,6 +232,8 @@ struct CellInfo : ArchCellInfo Region *region = nullptr; + std::unique_ptr pseudo_cell{}; + void addInput(IdString name); void addOutput(IdString name); void addInout(IdString name); @@ -190,6 +245,10 @@ struct CellInfo : ArchCellInfo // check whether a bel complies with the cell's region constraint bool testRegion(BelId bel) const; + bool isPseudo() const { return bool(pseudo_cell); } + + Loc getLocation() const; + NetInfo *getPort(IdString name) { auto found = ports.find(name); @@ -212,33 +271,6 @@ struct CellInfo : ArchCellInfo int new_offset, bool new_brackets, int width); }; -enum TimingPortClass -{ - TMG_CLOCK_INPUT, // Clock input to a sequential cell - TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc) - TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too) - TMG_REGISTER_OUTPUT, // Output from a register - TMG_COMB_INPUT, // Combinational input, no paths end here - TMG_COMB_OUTPUT, // Combinational output, no paths start here - TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output - TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input - TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis -}; - -enum ClockEdge -{ - RISING_EDGE, - FALLING_EDGE -}; - -struct TimingClockingInfo -{ - IdString clock_port; // Port name of clock domain - ClockEdge edge; - DelayPair setup, hold; // Input timing checks - DelayQuad clockToQ; // Output clock-to-Q time -}; - struct ClockConstraint { DelayPair high; diff --git a/common/place/detail_place_core.cc b/common/place/detail_place_core.cc index 7e629f24e1..18118fc873 100644 --- a/common/place/detail_place_core.cc +++ b/common/place/detail_place_core.cc @@ -37,6 +37,8 @@ PlacePartition::PlacePartition(Context *ctx) x1 = 0; y1 = 0; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; Loc l = ctx->getBelLocation(cell.second->bel); x0 = std::min(x0, l.x); x1 = std::max(x1, l.x); @@ -110,6 +112,8 @@ NetBB NetBB::compute(const Context *ctx, const NetInfo *net, const dictdriver.cell) return result; auto bel_loc = [&](const CellInfo *cell) { + if (cell->isPseudo()) + return cell->getLocation(); BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel; return ctx->getBelLocation(bel); }; @@ -176,10 +180,12 @@ void DetailPlacerThreadState::set_partition(const PlacePartition &part) // Set up the original cell-bel map for all nets inside the thread local_cell2bel.clear(); for (NetInfo *net : thread_nets) { - if (net->driver.cell) + if (net->driver.cell && !net->driver.cell->isPseudo()) local_cell2bel[net->driver.cell->name] = net->driver.cell->bel; - for (auto &usr : net->users) - local_cell2bel[usr.cell->name] = usr.cell->bel; + for (auto &usr : net->users) { + if (!usr.cell->isPseudo()) + local_cell2bel[usr.cell->name] = usr.cell->bel; + } } } diff --git a/common/place/parallel_refine.cc b/common/place/parallel_refine.cc index de71b8e093..0fb99be52b 100644 --- a/common/place/parallel_refine.cc +++ b/common/place/parallel_refine.cc @@ -390,6 +390,8 @@ struct ParallelRefine // Setup fast bels map pool cell_types_in_use; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); if (cell.second->cluster != ClusterId()) diff --git a/common/place/place_common.cc b/common/place/place_common.cc index e03fca553f..c2fc3b7db4 100644 --- a/common/place/place_common.cc +++ b/common/place/place_common.cc @@ -293,6 +293,8 @@ class ConstraintLegaliseWorker { if (cell->cluster != ClusterId() && ctx->getClusterRootCell(cell->cluster) != cell) return true; // Only process chain roots + if (cell->isPseudo()) + return true; if (constraints_satisfied(cell)) { if (cell->cluster != ClusterId()) lockdown_chain(cell); @@ -415,7 +417,7 @@ class ConstraintLegaliseWorker { log_info("Legalising relative constraints...\n"); for (auto &cell : ctx->cells) { - oldLocations[cell.first] = ctx->getBelLocation(cell.second->bel); + oldLocations[cell.first] = cell.second->getLocation(); } for (auto &cell : ctx->cells) { bool res = legalise_cell(cell.second.get()); @@ -448,6 +450,8 @@ bool legalise_relative_constraints(Context *ctx) { return ConstraintLegaliseWork int get_constraints_distance(const Context *ctx, const CellInfo *cell) { int dist = 0; + if (cell->isPseudo()) + return 0; if (cell->bel == BelId()) return 100000; Loc loc = ctx->getBelLocation(cell->bel); diff --git a/common/place/placer1.cc b/common/place/placer1.cc index a6ba389504..23264ce2d5 100644 --- a/common/place/placer1.cc +++ b/common/place/placer1.cc @@ -76,6 +76,8 @@ class SAPlacer pool cell_types_in_use; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); } @@ -120,7 +122,7 @@ class SAPlacer } for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->cluster == ClusterId()) + if (ci->isPseudo() || ci->cluster == ClusterId()) continue; cluster2cell[ci->cluster].push_back(ci); } @@ -145,6 +147,8 @@ class SAPlacer // Initial constraints placer for (auto &cell_entry : ctx->cells) { CellInfo *cell = cell_entry.second.get(); + if (cell->isPseudo()) + continue; auto loc = cell->attrs.find(ctx->id("BEL")); if (loc != cell->attrs.end()) { std::string loc_name = loc->second.as_string(); @@ -187,7 +191,7 @@ class SAPlacer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->bel == BelId()) { + if (!ci->isPseudo() && (ci->bel == BelId())) { autoplaced.push_back(cell.second.get()); } } @@ -217,7 +221,7 @@ class SAPlacer } else { for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->belStrength > STRENGTH_STRONG) { + if (ci->isPseudo() || ci->belStrength > STRENGTH_STRONG) { continue; } else if (ci->cluster != ClusterId()) { if (ctx->getClusterRootCell(ci->cluster) == ci) @@ -353,6 +357,8 @@ class SAPlacer autoplaced.clear(); chain_basis.clear(); for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() && ctx->getClusterRootCell(cell.second->cluster) == cell.second.get()) chain_basis.push_back(cell.second.get()); @@ -814,7 +820,7 @@ class SAPlacer { BoundingBox bb; NPNR_ASSERT(net->driver.cell != nullptr); - Loc dloc = ctx->getBelLocation(net->driver.cell->bel); + Loc dloc = net->driver.cell->getLocation(); bb.x0 = dloc.x; bb.x1 = dloc.x; bb.y0 = dloc.y; @@ -824,9 +830,9 @@ class SAPlacer bb.ny0 = 1; bb.ny1 = 1; for (auto user : net->users) { - if (user.cell->bel == BelId()) + if (!user.cell->isPseudo() && user.cell->bel == BelId()) continue; - Loc uloc = ctx->getBelLocation(user.cell->bel); + Loc uloc = user.cell->getLocation(); if (bb.x0 == uloc.x) ++bb.nx0; else if (uloc.x < bb.x0) { @@ -1173,7 +1179,7 @@ class SAPlacer nets_by_tile.resize(max_x + 1, std::vector>(max_y + 1)); for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (int(ci->ports.size()) > large_cell_thresh) + if (ci->isPseudo() || (int(ci->ports.size()) > large_cell_thresh)) continue; Loc loc = ctx->getBelLocation(ci->bel); auto &nbt = nets_by_tile.at(loc.x).at(loc.y); diff --git a/common/place/placer_heap.cc b/common/place/placer_heap.cc index 4c9ffb2326..bd8cd37def 100644 --- a/common/place/placer_heap.cc +++ b/common/place/placer_heap.cc @@ -147,7 +147,7 @@ class HeAPPlacer tmg.setup(); for (auto &cell : ctx->cells) - if (cell.second->cluster != ClusterId()) + if (!cell.second->isPseudo() && cell.second->cluster != ClusterId()) cluster2cells[cell.second->cluster].push_back(cell.second.get()); } @@ -284,6 +284,8 @@ class HeAPPlacer // Save solution solution.clear(); for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; solution.emplace_back(cell.second.get(), cell.second->bel, cell.second->belStrength); } } else { @@ -312,6 +314,8 @@ class HeAPPlacer } for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; if (cell.second->bel == BelId()) log_error("Found unbound cell %s\n", cell.first.c_str(ctx)); if (ctx->getBoundBelCell(cell.second->bel) != cell.second.get()) @@ -411,7 +415,8 @@ class HeAPPlacer // Initial constraints placer for (auto &cell_entry : ctx->cells) { CellInfo *cell = cell_entry.second.get(); - + if (cell->isPseudo()) + continue; auto loc = cell->attrs.find(ctx->id("BEL")); if (loc != cell->attrs.end()) { std::string loc_name = loc->second.as_string(); @@ -461,6 +466,8 @@ class HeAPPlacer pool cell_types_in_use; pool buckets_in_use; for (auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; IdString cell_type = cell.second->type; cell_types_in_use.insert(cell_type); BelBucketId bucket = ctx->getBelBucketForCellType(cell_type); @@ -527,6 +534,8 @@ class HeAPPlacer { pool cell_types; for (const auto &cell : ctx->cells) { + if (cell.second->isPseudo()) + continue; cell_types.insert(cell.second->type); } @@ -551,6 +560,14 @@ class HeAPPlacer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); + if (ci->isPseudo()) { + Loc loc = ci->pseudo_cell->getLocation(); + cell_locs[cell.first].x = loc.x; + cell_locs[cell.first].y = loc.y; + cell_locs[cell.first].locked = true; + cell_locs[cell.first].global = false; + continue; + } if (ci->bel != BelId()) { Loc loc = ctx->getBelLocation(ci->bel); cell_locs[cell.first].x = loc.x; @@ -627,8 +644,9 @@ class HeAPPlacer int row = 0; solve_cells.clear(); // First clear the udata of all cells - for (auto &cell : ctx->cells) + for (auto &cell : ctx->cells) { cell.second->udata = dont_solve; + } // Then update cells to be placed, which excludes cell children for (auto cell : place_cells) { if (buckets && !buckets->count(ctx->getBelBucketForCellType(cell->type))) diff --git a/common/route/router2.cc b/common/route/router2.cc index e943e493bc..1153f054bc 100644 --- a/common/route/router2.cc +++ b/common/route/router2.cc @@ -128,7 +128,7 @@ struct Router2 nets.at(i).cy = 0; if (ni->driver.cell != nullptr) { - Loc drv_loc = ctx->getBelLocation(ni->driver.cell->bel); + Loc drv_loc = ni->driver.cell->getLocation(); nets.at(i).cx += drv_loc.x; nets.at(i).cy += drv_loc.y; } @@ -159,7 +159,7 @@ struct Router2 nets.at(i).bb.y1 = std::max(nets.at(i).bb.y1, ad.bb.y1); } // Add location to centroid sum - Loc usr_loc = ctx->getBelLocation(usr.value.cell->bel); + Loc usr_loc = usr.value.cell->getLocation(); nets.at(i).cx += usr_loc.x; nets.at(i).cy += usr_loc.y; } diff --git a/docs/netlist.md b/docs/netlist.md index 5d8ca572df..43a96dde04 100644 --- a/docs/netlist.md +++ b/docs/netlist.md @@ -25,10 +25,24 @@ Other structures used by these basic structures include: - `params` and `attrs` store parameters and attributes - from the input JSON or assigned in flows to add metadata - by mapping from parameter name `IdString` to `Property`. - `cluster` is used to specify that the cell is inside a placement cluster, with the details of the placement within the cluster provided by the architecture. - `region` is a reference to a `Region` if the cell is constrained to a placement region (e.g. for partial reconfiguration or out-of-context flows) or `nullptr` otherwise. + - `pseudo_cell` is an optional pointer to an implementation of the pseudo-cell API, used for cells implementing virtual functions such as partition pins without a mapped bel. `bel` will always be `BelId()` for pseudo-cells. + +## PseudoCellAPI + +Pseudo-cells can be used to implement cells with runtime-defined cell pin to wire mappings. This means they don't have to be a fixed part of the architecture, example use cases could be for implementing partition pins for partial reconfiguration regions; or forcing splits between SLRs. Pseudo-cells implement a series of virtual functions to provide data that for an ordinary cell would be obtained by calling 'bel' ArchAPI functions + +The pseudo-cell API is as follows: + - `Loc getLocation() const` : get an approximate location of the pseudocell + - `WireId getPortWire(IdString port) const`: gets the wire corresponding to a port (or WireId if it has no wire) + +It also implements functions for getting timing data, mirroring that of the Arch API: + - `bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const` + - `TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const` + - `TimingClockingInfo getPortClockingInfo(IdString port, int index) const` ## NetInfo -`NetInfo` instances have the following fields: +`NetInfo` instances have the following fields:\ - `name` is the IdString name of the net - for nets with multiple names, one name is chosen according to a set of rules by the JSON frontend - `hierpath` is name of the hierarchical cell containing the instance, for designs with hierarchy diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 789b188e65..aeb5578f87 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -748,11 +748,11 @@ struct Arch : ArchAPI // Get the delay through a cell from one port to another, returning false // if no path exists. This only considers combinational delays, as required by the Arch API - bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const final; + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const; // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port - TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const final; + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; // Get the TimingClockingInfo of a port - TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const final; + TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const; // ------------------------------------------------- diff --git a/generic/arch.cc b/generic/arch.cc index 11b5868b15..3df58c9beb 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -605,7 +605,7 @@ bool Arch::place() bool have_iobuf_or_constr = false; for (auto &cell : cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id("GENERIC_IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { + if (ci->isPseudo() || ci->type == id("GENERIC_IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { have_iobuf_or_constr = true; break; } From 6969782a4b76dd7846bb206d08768736b9cac341 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 19 Jul 2022 18:51:25 +1000 Subject: [PATCH 166/712] gowin: Remove incomprehensible names of the muxes There is no need to multiply item names, it is a rudiment of my very first addition to nextpnr. Fully compatible with older versions of Apicula. Note: the cosmetic changes in lines with RAM are not my initiative, but the result of applying clang-format. Signed-off-by: YRabbit --- gowin/arch.cc | 15 +++++++++------ gowin/arch.h | 2 +- gowin/cells.cc | 6 +++--- gowin/cells.h | 8 ++++---- gowin/constids.inc | 4 ---- gowin/gfx.cc | 8 ++++---- gowin/pack.cc | 21 +++++++++------------ 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 6fe40f76d6..b37d444cee 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -553,7 +553,10 @@ void Arch::setDelayScaling(double scale, double offset) args.delayOffset = offset; } -void Arch::addCellTimingClass(IdString cell, IdString port, TimingPortClass cls) {cellTiming[cell].portClasses[port] = cls;} +void Arch::addCellTimingClass(IdString cell, IdString port, TimingPortClass cls) +{ + cellTiming[cell].portClasses[port] = cls; +} void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; } @@ -1019,7 +1022,7 @@ void Arch::addMuxBels(const DatabasePOD *db, int row, int col) // bel snprintf(buf, 40, "R%dC%d_MUX2_LUT%c%c", grow, gcol, mux_names[j].type, mux_names[j].bel_idx); belname = id(buf); - snprintf(buf, 40, "GW_MUX2_LUT%c", mux_names[j].type); + snprintf(buf, 40, "MUX2_LUT%c", mux_names[j].type); bel_id = id(buf); addBel(belname, bel_id, Loc(col, row, z), false); @@ -2041,16 +2044,16 @@ void Arch::assignArchInfo() } break; } - case ID_GW_MUX2_LUT8: + case ID_MUX2_LUT8: delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); /* FALLTHRU */ - case ID_GW_MUX2_LUT7: + case ID_MUX2_LUT7: delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); /* FALLTHRU */ - case ID_GW_MUX2_LUT6: + case ID_MUX2_LUT6: delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); /* FALLTHRU */ - case ID_GW_MUX2_LUT5: { + case ID_MUX2_LUT5: { delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); addCellTimingDelay(cname, id_I0, id_OF, delay); addCellTimingDelay(cname, id_I1, id_OF, delay); diff --git a/gowin/arch.h b/gowin/arch.h index 034e4b8604..a1fc25ae7a 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -502,7 +502,7 @@ enum { mux_0_z = 10, // start Z for the MUX2LUT5 bels iologic_0_z = 20, // start Z for the IOLOGIC bels - lutram_0_z = 30, // start Z for the IOLOGIC bels + lutram_0_z = 30, // start Z for the IOLOGIC bels vcc_0_z = 277, // virtual VCC bel Z gnd_0_z = 278, // virtual VSS bel Z osc_z = 280, // Z for the oscillator bels diff --git a/gowin/cells.cc b/gowin/cells.cc index d83b07c8a7..0c027d38a4 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -56,8 +56,8 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addInput(id_CLK); new_cell->addInput(id_CE); new_cell->addInput(id_LSR); - } else if (type == id_GW_MUX2_LUT5 || type == id_GW_MUX2_LUT6 || type == id_GW_MUX2_LUT7 || - type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT8) { + } else if (type == id_MUX2_LUT5 || type == id_MUX2_LUT6 || type == id_MUX2_LUT7 || type == id_MUX2_LUT7 || + type == id_MUX2_LUT8) { new_cell->addInput(id_I0); new_cell->addInput(id_I1); new_cell->addInput(id_SEL); @@ -190,7 +190,7 @@ void sram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw) ram->movePortTo(ctx->id("DI[1]"), ramw, id_B5); ram->movePortTo(ctx->id("DI[2]"), ramw, id_C5); ram->movePortTo(ctx->id("DI[3]"), ramw, id_D5); - + ram->movePortTo(ctx->id("CLK"), ramw, id_CLK); ram->movePortTo(ctx->id("WRE"), ramw, id_LSR); } diff --git a/gowin/cells.h b/gowin/cells.h index b6d8649733..84de70bdf0 100644 --- a/gowin/cells.h +++ b/gowin/cells.h @@ -62,22 +62,22 @@ inline bool is_alu(const BaseCtx *ctx, const CellInfo *cell) { return (cell->typ // is MUX2_LUT5 inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); } -inline bool is_gw_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT5); } +inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); } // is MUX2_LUT6 inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); } -inline bool is_gw_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT6); } +inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); } // is MUX2_LUT7 inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); } -inline bool is_gw_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT7); } +inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); } // is MUX2_LUT8 inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); } -inline bool is_gw_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT8); } +inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); } // Return true if a cell is a flipflop inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) diff --git a/gowin/constids.inc b/gowin/constids.inc index 8916f093ff..ec1137fc59 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -775,10 +775,6 @@ X(MUX2_LUT5) X(MUX2_LUT6) X(MUX2_LUT7) X(MUX2_LUT8) -X(GW_MUX2_LUT5) -X(GW_MUX2_LUT6) -X(GW_MUX2_LUT7) -X(GW_MUX2_LUT8) X(I0MUX0) X(I1MUX0) X(I0MUX1) diff --git a/gowin/gfx.cc b/gowin/gfx.cc index fcd42c7c74..9c9bc3fc02 100644 --- a/gowin/gfx.cc +++ b/gowin/gfx.cc @@ -5637,28 +5637,28 @@ void gfxSetBelDefaultDecal(Arch *arch, BelInfo &bel) } arch->setBelDecal(bel.name, active, inactive); break; - case ID_GW_MUX2_LUT5: + case ID_MUX2_LUT5: active.x = inactive.x = bel.x + mux2lut5_x; active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut5_y[(bel.z - BelZ::mux_0_z) >> 1]; active.decal = id_DECAL_MUXUPPER_ACTIVE; inactive.decal = id_DECAL_MUXUPPER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); break; - case ID_GW_MUX2_LUT6: + case ID_MUX2_LUT6: active.x = inactive.x = bel.x + mux2lut6_x; active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut6_y[(bel.z - BelZ::mux_0_z) / 5]; active.decal = id_DECAL_MUXLOWER_ACTIVE; inactive.decal = id_DECAL_MUXLOWER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); break; - case ID_GW_MUX2_LUT7: + case ID_MUX2_LUT7: active.x = inactive.x = bel.x + mux2lut7_x; active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut7_y; active.decal = id_DECAL_MUXLOWER_ACTIVE; inactive.decal = id_DECAL_MUXLOWER_INACTIVE; arch->setBelDecal(bel.name, active, inactive); break; - case ID_GW_MUX2_LUT8: + case ID_MUX2_LUT8: active.x = inactive.x = bel.x + mux2lut8_x; active.y = inactive.y = arch->gridDimY - 1. - bel.y + mux2lut8_y; active.decal = id_DECAL_MUXUPPER_ACTIVE; diff --git a/gowin/pack.cc b/gowin/pack.cc index dbf949d4f1..4b5bc81db6 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -266,7 +266,7 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce return; } - std::unique_ptr packed = create_generic_cell(ctx, id_GW_MUX2_LUT5, ci->name.str(ctx) + "_LC"); + std::unique_ptr packed = create_generic_cell(ctx, id_MUX2_LUT5, ci->name.str(ctx) + "_LC"); if (ctx->verbose) { log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get())); } @@ -309,7 +309,7 @@ static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool &packed_ce return; } - std::unique_ptr packed = create_generic_cell(ctx, id_GW_MUX2_LUT5, ci->name.str(ctx) + "_LC"); + std::unique_ptr packed = create_generic_cell(ctx, id_MUX2_LUT5, ci->name.str(ctx) + "_LC"); if (ctx->verbose) { log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get())); } @@ -413,7 +413,7 @@ static void pack_mux2_lut6(Context *ctx, CellInfo *ci, pool &packed_ce { static int x[] = {0, 0}; static int z[] = {+1, -1}; - pack_mux2_lut(ctx, ci, is_gw_mux2_lut5, '6', id_GW_MUX2_LUT6, x, z, packed_cells, delete_nets, new_cells); + pack_mux2_lut(ctx, ci, is_mux2_lut5, '6', id_MUX2_LUT6, x, z, packed_cells, delete_nets, new_cells); } // pack MUX2_LUT7 @@ -422,7 +422,7 @@ static void pack_mux2_lut7(Context *ctx, CellInfo *ci, pool &packed_ce { static int x[] = {0, 0}; static int z[] = {+2, -2}; - pack_mux2_lut(ctx, ci, is_gw_mux2_lut6, '7', id_GW_MUX2_LUT7, x, z, packed_cells, delete_nets, new_cells); + pack_mux2_lut(ctx, ci, is_mux2_lut6, '7', id_MUX2_LUT7, x, z, packed_cells, delete_nets, new_cells); } // pack MUX2_LUT8 @@ -431,7 +431,7 @@ static void pack_mux2_lut8(Context *ctx, CellInfo *ci, pool &packed_ce { static int x[] = {1, 0}; static int z[] = {-4, -4}; - pack_mux2_lut(ctx, ci, is_gw_mux2_lut7, '8', id_GW_MUX2_LUT8, x, z, packed_cells, delete_nets, new_cells); + pack_mux2_lut(ctx, ci, is_mux2_lut7, '8', id_MUX2_LUT8, x, z, packed_cells, delete_nets, new_cells); } // Pack wide LUTs @@ -707,16 +707,14 @@ void pack_sram(Context *ctx) if (is_sram(ctx, ci)) { // Create RAMW slice - std::unique_ptr ramw_slice = - create_generic_cell(ctx, id_RAMW, ci->name.str(ctx) + "$RAMW_SLICE"); + std::unique_ptr ramw_slice = create_generic_cell(ctx, id_RAMW, ci->name.str(ctx) + "$RAMW_SLICE"); sram_to_ramw_split(ctx, ci, ramw_slice.get()); ramw_slice->connectPort(id_CE, ctx->nets[ctx->id("$PACKER_VCC_NET")].get()); // Create actual RAM slices std::unique_ptr ram_comb[4]; for (int i = 0; i < 4; i++) { - ram_comb[i] = create_generic_cell(ctx, id_SLICE, - ci->name.str(ctx) + "$SRAM_SLICE" + std::to_string(i)); + ram_comb[i] = create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "$SRAM_SLICE" + std::to_string(i)); ram_comb[i]->params[id_FF_USED] = 1; ram_comb[i]->params[id_FF_TYPE] = std::string("RAM"); sram_to_slice(ctx, ci, ram_comb[i].get(), i); @@ -724,8 +722,8 @@ void pack_sram(Context *ctx) // Create 'block' SLICEs as a placement hint that these cells are mutually exclusive with the RAMW std::unique_ptr ramw_block[2]; for (int i = 0; i < 2; i++) { - ramw_block[i] = create_generic_cell(ctx, id_SLICE, - ci->name.str(ctx) + "$RAMW_BLOCK" + std::to_string(i)); + ramw_block[i] = + create_generic_cell(ctx, id_SLICE, ci->name.str(ctx) + "$RAMW_BLOCK" + std::to_string(i)); ram_comb[i]->params[id_FF_USED] = 1; ramw_block[i]->params[id_FF_TYPE] = std::string("RAM"); } @@ -782,7 +780,6 @@ void pack_sram(Context *ctx) } } - static bool is_nextpnr_iob(const Context *ctx, CellInfo *cell) { return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") || From ad502bf64b9d4100da131eb76814351b4aa11bce Mon Sep 17 00:00:00 2001 From: gatecat Date: Tue, 19 Jul 2022 09:58:00 +0100 Subject: [PATCH 167/712] nexus: Fix CSDECODE parsing Signed-off-by: gatecat --- nexus/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/pack.cc b/nexus/pack.cc index 0870bf4066..15752171e4 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -43,7 +43,7 @@ Property Arch::parse_lattice_param_from_cell(const CellInfo *ci, IdString prop, // Parse a possibly-Lattice-style (C literal in Verilog string) style parameter Property Arch::parse_lattice_param(const Property &val, IdString prop, int width, const char *ci) const { - if (val.is_string) { + if (val.is_string && !prop.in(id_CSDECODE_A, id_CSDECODE_B, id_CSDECODE_R, id_CSDECODE_W)) { const std::string &s = val.str; Property temp; From ce2335bc0065df5217585f341f7ed46aa8f66c36 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 19 Jul 2022 20:20:26 +1000 Subject: [PATCH 168/712] gowin: fix compilation Signed-off-by: YRabbit --- gowin/cells.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/gowin/cells.h b/gowin/cells.h index 84de70bdf0..3a570f9740 100644 --- a/gowin/cells.h +++ b/gowin/cells.h @@ -62,23 +62,15 @@ inline bool is_alu(const BaseCtx *ctx, const CellInfo *cell) { return (cell->typ // is MUX2_LUT5 inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); } -inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); } - // is MUX2_LUT6 inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); } -inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); } - // is MUX2_LUT7 inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); } -inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); } - // is MUX2_LUT8 inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); } -inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); } - // Return true if a cell is a flipflop inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { From 37f0886cb9e949cb49250f973a852c816b5ab892 Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 4 Aug 2022 10:55:19 +0200 Subject: [PATCH 169/712] generic: addBelPin with direction as an arg Signed-off-by: gatecat --- generic/arch.cc | 30 +++++------------------------- generic/arch.h | 1 + generic/arch_pybindings.cc | 6 ++++++ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/generic/arch.cc b/generic/arch.cc index 3df58c9beb..9992e1cd2e 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -134,40 +134,20 @@ BelId Arch::addBel(IdStringList name, IdString type, Loc loc, bool gb, bool hidd return bel; } -void Arch::addBelInput(BelId bel, IdString name, WireId wire) -{ - auto &bi = bel_info(bel); - NPNR_ASSERT(bi.pins.count(name) == 0); - PinInfo &pi = bi.pins[name]; - pi.name = name; - pi.wire = wire; - pi.type = PORT_IN; - - if (wire != WireId()) - wire_info(wire).bel_pins.push_back(BelPin{bel, name}); -} +void Arch::addBelInput(BelId bel, IdString name, WireId wire) { addBelPin(bel, name, wire, PORT_IN); } -void Arch::addBelOutput(BelId bel, IdString name, WireId wire) -{ - auto &bi = bel_info(bel); - NPNR_ASSERT(bi.pins.count(name) == 0); - PinInfo &pi = bi.pins[name]; - pi.name = name; - pi.wire = wire; - pi.type = PORT_OUT; +void Arch::addBelOutput(BelId bel, IdString name, WireId wire) { addBelPin(bel, name, wire, PORT_OUT); } - if (wire != WireId()) - wire_info(wire).bel_pins.push_back(BelPin{bel, name}); -} +void Arch::addBelInout(BelId bel, IdString name, WireId wire) { addBelPin(bel, name, wire, PORT_INOUT); } -void Arch::addBelInout(BelId bel, IdString name, WireId wire) +void Arch::addBelPin(BelId bel, IdString name, WireId wire, PortType type) { auto &bi = bel_info(bel); NPNR_ASSERT(bi.pins.count(name) == 0); PinInfo &pi = bi.pins[name]; pi.name = name; pi.wire = wire; - pi.type = PORT_INOUT; + pi.type = type; if (wire != WireId()) wire_info(wire).bel_pins.push_back(BelPin{bel, name}); diff --git a/generic/arch.h b/generic/arch.h index 157ff8afec..688392da2d 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -200,6 +200,7 @@ struct Arch : BaseArch void addBelInput(BelId bel, IdString name, WireId wire); void addBelOutput(BelId bel, IdString name, WireId wire); void addBelInout(BelId bel, IdString name, WireId wire); + void addBelPin(BelId bel, IdString name, WireId wire, PortType type); WireId addWireAsBelInput(BelId bel, IdString name); WireId addWireAsBelOutput(BelId bel, IdString name); diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc index e0a2f0f285..a5a0bed94c 100644 --- a/generic/arch_pybindings.cc +++ b/generic/arch_pybindings.cc @@ -105,6 +105,12 @@ void arch_wrap_python(py::module &m) fn_wrapper_3a_v, conv_from_str, conv_from_str>::def_wrap(ctx_cls, "addBelInout", "bel"_a, "name"_a, "wire"_a); + fn_wrapper_4a_v, + conv_from_str, conv_from_str, pass_through>::def_wrap(ctx_cls, + "addBelPin", + "bel"_a, "name"_a, + "wire"_a, + "type"_a); fn_wrapper_2a_v, conv_from_str>::def_wrap(ctx_cls, "addGroupBel", "group"_a, "bel"_a); From 77c82b0fbf15892b0c8222bac89564f3f024493e Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Aug 2022 10:57:17 +0100 Subject: [PATCH 170/712] refactor: id(stringf(...)) to new idf(...) helper Signed-off-by: gatecat --- common/kernel/basectx.cc | 12 ++++++ common/kernel/basectx.h | 2 + common/kernel/nextpnr_types.cc | 8 ++-- ecp5/arch.cc | 8 ++-- ecp5/pack.cc | 2 +- fpga_interchange/arch.cc | 2 +- fpga_interchange/macros.cc | 2 +- generic/viaduct/example/example.cc | 28 +++++++------- generic/viaduct/okami/okami.cc | 61 +++++++++++++++--------------- generic/viaduct_helpers.cc | 6 +-- gowin/pack.cc | 2 +- ice40/arch.cc | 4 +- machxo2/arch.cc | 8 ++-- mistral/arch.cc | 2 +- mistral/globals.cc | 6 +-- mistral/io.cc | 4 +- mistral/lab.cc | 38 +++++++++---------- mistral/m10k.cc | 34 +++++++---------- mistral/pack.cc | 11 ++---- nexus/arch.cc | 10 ++--- nexus/fasm.cc | 4 +- nexus/pack.cc | 51 ++++++++++++------------- 22 files changed, 152 insertions(+), 153 deletions(-) diff --git a/common/kernel/basectx.cc b/common/kernel/basectx.cc index 82cdd835b9..777d06e15d 100644 --- a/common/kernel/basectx.cc +++ b/common/kernel/basectx.cc @@ -26,6 +26,18 @@ NEXTPNR_NAMESPACE_BEGIN +IdString BaseCtx::idf(const char *fmt, ...) const +{ + std::string string; + va_list ap; + + va_start(ap, fmt); + string = vstringf(fmt, ap); + va_end(ap); + + return id(string); +} + const char *BaseCtx::nameOfBel(BelId bel) const { const Context *ctx = getCtx(); diff --git a/common/kernel/basectx.h b/common/kernel/basectx.h index 5775e47f21..c8791a2b96 100644 --- a/common/kernel/basectx.h +++ b/common/kernel/basectx.h @@ -162,6 +162,8 @@ struct BaseCtx IdString id(const char *s) const { return IdString(this, s); } + IdString idf(const char *fmt, ...) const; // create IdString using printf formatting + Context *getCtx() { return as_ctx; } const Context *getCtx() const { return as_ctx; } diff --git a/common/kernel/nextpnr_types.cc b/common/kernel/nextpnr_types.cc index 8563eb27ad..6da37763bf 100644 --- a/common/kernel/nextpnr_types.cc +++ b/common/kernel/nextpnr_types.cc @@ -152,8 +152,8 @@ void CellInfo::movePortBusTo(IdString old_name, int old_offset, bool old_bracket IdString new_name, int new_offset, bool new_brackets, int width) { for (int i = 0; i < width; i++) { - IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); - IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + IdString old_port = ctx->idf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset); + IdString new_port = ctx->idf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset); movePortTo(old_port, new_cell, new_port); } } @@ -171,8 +171,8 @@ void CellInfo::copyPortBusTo(IdString old_name, int old_offset, bool old_bracket IdString new_name, int new_offset, bool new_brackets, int width) { for (int i = 0; i < width; i++) { - IdString old_port = ctx->id(stringf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset)); - IdString new_port = ctx->id(stringf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset)); + IdString old_port = ctx->idf(old_brackets ? "%s[%d]" : "%s%d", old_name.c_str(ctx), i + old_offset); + IdString new_port = ctx->idf(new_brackets ? "%s[%d]" : "%s%d", new_name.c_str(ctx), i + new_offset); copyPortTo(old_port, new_cell, new_port); } } diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d98ee54cee..0b763612de 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -121,17 +121,17 @@ Arch::Arch(ArchArgs args) : args(args) BaseArch::init_bel_buckets(); for (int i = 0; i < chip_info->width; i++) - x_ids.push_back(id(stringf("X%d", i))); + x_ids.push_back(idf("X%d", i)); for (int i = 0; i < chip_info->height; i++) - y_ids.push_back(id(stringf("Y%d", i))); + y_ids.push_back(idf("Y%d", i)); for (int i = 0; i < chip_info->width; i++) { - IdString x_id = id(stringf("X%d", i)); + IdString x_id = idf("X%d", i); x_ids.push_back(x_id); id_to_x[x_id] = i; } for (int i = 0; i < chip_info->height; i++) { - IdString y_id = id(stringf("Y%d", i)); + IdString y_id = idf("Y%d", i); y_ids.push_back(y_id); id_to_y[y_id] = i; } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 7aa9b4c4b0..4cd33deee3 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -801,7 +801,7 @@ class Ecp5Packer ci->disconnectPort(id_WRE); for (int i = 0; i < 4; i++) - ci->disconnectPort(ctx->id(stringf("RAD[%d]", i))); + ci->disconnectPort(ctx->idf("RAD[%d]", i)); // Setup placement constraints // Use the 0th bit as an anchor diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 6a7c4fe137..3ce9f79e4a 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -907,7 +907,7 @@ static void prepare_sites_for_routing(Context *ctx) // We can't rely on bel pins not clashing with cell names (for Xilinx they use different naming schemes, for // Nexus they are the same) so add a prefix to the bel pin name to disambiguate it - IdString cell_pin = ctx->id(stringf("%s_PHYS", ctx->nameOf(bel_pin))); + IdString cell_pin = ctx->idf("%s_PHYS", ctx->nameOf(bel_pin)); PortInfo port_info; port_info.name = cell_pin; diff --git a/fpga_interchange/macros.cc b/fpga_interchange/macros.cc index 8f7f823162..cc67833aba 100644 --- a/fpga_interchange/macros.cc +++ b/fpga_interchange/macros.cc @@ -45,7 +45,7 @@ static const MacroExpansionPOD *lookup_macro_rules(const ChipInfoPOD *chip, IdSt static IdString derived_name(Context *ctx, IdString base_name, IdString suffix) { - return ctx->id(stringf("%s/%s", base_name.c_str(ctx), suffix.c_str(ctx))); + return ctx->idf("%s/%s", base_name.c_str(ctx), suffix.c_str(ctx)); } void Arch::expand_macros() diff --git a/generic/viaduct/example/example.cc b/generic/viaduct/example/example.cc index 49b3679269..987c323697 100644 --- a/generic/viaduct/example/example.cc +++ b/generic/viaduct/example/example.cc @@ -108,24 +108,23 @@ struct ExampleImpl : ViaductAPI auto &w = row_wires.at(x); for (int z = 0; z < N; z++) { // Clock input - w.clk.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("CLK%d", z))), ctx->id("CLK"), x, y)); + w.clk.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("CLK%d", z)), ctx->id("CLK"), x, y)); // FF input - w.d.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("D%d", z))), ctx->id("D"), x, y)); + w.d.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("D%d", z)), ctx->id("D"), x, y)); // FF and LUT outputs - w.q.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("Q%d", z))), ctx->id("Q"), x, y)); - w.f.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("F%d", z))), ctx->id("F"), x, y)); + w.q.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("Q%d", z)), ctx->id("Q"), x, y)); + w.f.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("F%d", z)), ctx->id("F"), x, y)); // LUT inputs for (int i = 0; i < K; i++) - w.i.push_back( - ctx->addWire(h.xy_id(x, y, ctx->id(stringf("L%dI%d", z, i))), ctx->id("I"), x, y)); + w.i.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("L%dI%d", z, i)), ctx->id("I"), x, y)); } // Local wires for (int l = 0; l < Wl; l++) - w.l.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("LOCAL%d", l))), ctx->id("LOCAL"), x, y)); + w.l.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("LOCAL%d", l)), ctx->id("LOCAL"), x, y)); // Pad wires for IO if (is_io(x, y) && x != y) for (int z = 0; z < 2; z++) - w.pad.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("PAD%d", z))), id_PAD, x, y)); + w.pad.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("PAD%d", z)), id_PAD, x, y)); } } } @@ -139,7 +138,7 @@ struct ExampleImpl : ViaductAPI { auto &w = wires_by_tile.at(y).at(x); for (int z = 0; z < 2; z++) { - BelId b = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("IO%d", z))), id_IOB, Loc(x, y, z), false, false); + BelId b = ctx->addBel(h.xy_id(x, y, ctx->idf("IO%d", z)), id_IOB, Loc(x, y, z), false, false); ctx->addBelInout(b, id_PAD, w.pad.at(z)); ctx->addBelInput(b, id_I, w.i.at(z * K + 0)); ctx->addBelInput(b, id_EN, w.i.at(z * K + 1)); @@ -157,17 +156,16 @@ struct ExampleImpl : ViaductAPI auto &w = wires_by_tile.at(y).at(x); for (int z = 0; z < N; z++) { // Create LUT bel - BelId lut = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_LUT", z))), id_LUT4, Loc(x, y, z * 2), false, - false); + BelId lut = ctx->addBel(h.xy_id(x, y, ctx->idf("SLICE%d_LUT", z)), id_LUT4, Loc(x, y, z * 2), false, false); for (int k = 0; k < K; k++) - ctx->addBelInput(lut, ctx->id(stringf("I[%d]", k)), w.i.at(z * K + k)); + ctx->addBelInput(lut, ctx->idf("I[%d]", k), w.i.at(z * K + k)); ctx->addBelOutput(lut, id_F, w.f.at(z)); // FF data can come from LUT output or LUT I3 add_pip(Loc(x, y, 0), w.f.at(z), w.d.at(z)); add_pip(Loc(x, y, 0), w.i.at(z * K + (K - 1)), w.d.at(z)); // Create DFF bel - BelId dff = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_FF", z))), id_DFF, Loc(x, y, z * 2 + 1), - false, false); + BelId dff = + ctx->addBel(h.xy_id(x, y, ctx->idf("SLICE%d_FF", z)), id_DFF, Loc(x, y, z * 2 + 1), false, false); ctx->addBelInput(dff, id_CLK, w.clk.at(z)); ctx->addBelInput(dff, id_D, w.d.at(z)); ctx->addBelOutput(dff, id_Q, w.q.at(z)); @@ -254,7 +252,7 @@ struct ExampleImpl : ViaductAPI auto &fc = fast_cell_info.at(ci->flat_index); if (ci->type == id_LUT4) { fc.lut_f = ci->getPort(id_F); - fc.lut_i3_used = (ci->getPort(ctx->id(stringf("I[%d]", K - 1))) != nullptr); + fc.lut_i3_used = (ci->getPort(ctx->idf("I[%d]", K - 1)) != nullptr); } else if (ci->type == id_DFF) { fc.ff_d = ci->getPort(id_D); } diff --git a/generic/viaduct/okami/okami.cc b/generic/viaduct/okami/okami.cc index 864bdb456d..8142756fec 100644 --- a/generic/viaduct/okami/okami.cc +++ b/generic/viaduct/okami/okami.cc @@ -120,45 +120,45 @@ struct OkamiImpl : ViaductAPI auto &w = row_wires.at(x); for (int z = 0; z < N; z++) { // Clock input - w.clk.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("CLK%d", z))), ctx->id("CLK"), x, y)); + w.clk.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("CLK%d", z)), ctx->id("CLK"), x, y)); // FF input - w.d.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("D%d", z))), ctx->id("D"), x, y)); + w.d.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("D%d", z)), ctx->id("D"), x, y)); // FF and LUT outputs - w.q.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("Q%d", z))), ctx->id("Q"), x, y)); - w.f.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("F%d", z))), ctx->id("F"), x, y)); + w.q.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("Q%d", z)), ctx->id("Q"), x, y)); + w.f.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("F%d", z)), ctx->id("F"), x, y)); // LUT inputs for (int i = 0; i < K; i++) w.slice_inputs.push_back( - ctx->addWire(h.xy_id(x, y, ctx->id(stringf("L%dI%d", z, i))), ctx->id("I"), x, y)); - w.slice_outputs.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("SLICEOUT[%d]", z))), - ctx->id("SLICEOUT"), x, y)); + ctx->addWire(h.xy_id(x, y, ctx->idf("L%dI%d", z, i)), ctx->id("I"), x, y)); + w.slice_outputs.push_back( + ctx->addWire(h.xy_id(x, y, ctx->idf("SLICEOUT[%d]", z)), ctx->id("SLICEOUT"), x, y)); } // Tile inputs for (int tile_input = 0; tile_input < InputMuxCount; tile_input++) { - w.tile_inputs_north.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEINN[%d]", tile_input))), ctx->id("TILEINN"), x, y)); - w.tile_inputs_east.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEINE[%d]", tile_input))), ctx->id("TILEINE"), x, y)); - w.tile_inputs_south.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEINS[%d]", tile_input))), ctx->id("TILEINS"), x, y)); - w.tile_inputs_west.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEINW[%d]", tile_input))), ctx->id("TILEINW"), x, y)); + w.tile_inputs_north.push_back( + ctx->addWire(h.xy_id(x, y, ctx->idf("TILEINN[%d]", tile_input)), ctx->id("TILEINN"), x, y)); + w.tile_inputs_east.push_back( + ctx->addWire(h.xy_id(x, y, ctx->idf("TILEINE[%d]", tile_input)), ctx->id("TILEINE"), x, y)); + w.tile_inputs_south.push_back( + ctx->addWire(h.xy_id(x, y, ctx->idf("TILEINS[%d]", tile_input)), ctx->id("TILEINS"), x, y)); + w.tile_inputs_west.push_back( + ctx->addWire(h.xy_id(x, y, ctx->idf("TILEINW[%d]", tile_input)), ctx->id("TILEINW"), x, y)); } // Tile outputs for (int tile_output = 0; tile_output < OutputMuxCount; tile_output++) { - w.tile_outputs_north.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEOUTN[%d]", tile_output))), ctx->id("TILEOUTN"), x, y)); - w.tile_outputs_east.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEOUTE[%d]", tile_output))), ctx->id("TILEOUTE"), x, y)); - w.tile_outputs_south.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEOUTS[%d]", tile_output))), ctx->id("TILEOUTS"), x, y)); - w.tile_outputs_west.push_back(ctx->addWire( - h.xy_id(x, y, ctx->id(stringf("TILEOUTW[%d]", tile_output))), ctx->id("TILEOUTW"), x, y)); + w.tile_outputs_north.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("TILEOUTN[%d]", tile_output)), + ctx->id("TILEOUTN"), x, y)); + w.tile_outputs_east.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("TILEOUTE[%d]", tile_output)), + ctx->id("TILEOUTE"), x, y)); + w.tile_outputs_south.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("TILEOUTS[%d]", tile_output)), + ctx->id("TILEOUTS"), x, y)); + w.tile_outputs_west.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("TILEOUTW[%d]", tile_output)), + ctx->id("TILEOUTW"), x, y)); } // Pad wires for IO if (is_io(x, y) && x != y) for (int z = 0; z < 2; z++) - w.pad.push_back(ctx->addWire(h.xy_id(x, y, ctx->id(stringf("PAD%d", z))), id_PAD, x, y)); + w.pad.push_back(ctx->addWire(h.xy_id(x, y, ctx->idf("PAD%d", z)), id_PAD, x, y)); } } } @@ -172,7 +172,7 @@ struct OkamiImpl : ViaductAPI { auto &w = wires_by_tile.at(y).at(x); for (int z = 0; z < 2; z++) { - BelId b = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("IO%d", z))), id_IOB, Loc(x, y, z), false, false); + BelId b = ctx->addBel(h.xy_id(x, y, ctx->idf("IO%d", z)), id_IOB, Loc(x, y, z), false, false); ctx->addBelInout(b, id_PAD, w.pad.at(z)); ctx->addBelInput(b, id_I, w.slice_inputs.at(z * K + 0)); ctx->addBelInput(b, id_EN, w.slice_inputs.at(z * K + 1)); @@ -190,17 +190,16 @@ struct OkamiImpl : ViaductAPI auto &w = wires_by_tile.at(y).at(x); for (int z = 0; z < N; z++) { // Create LUT bel - BelId lut = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_LUT", z))), id_LUT4, Loc(x, y, z * 2), false, - false); + BelId lut = ctx->addBel(h.xy_id(x, y, ctx->idf("SLICE%d_LUT", z)), id_LUT4, Loc(x, y, z * 2), false, false); for (int k = 0; k < K; k++) - ctx->addBelInput(lut, ctx->id(stringf("I[%d]", k)), w.slice_inputs.at(z * K + k)); + ctx->addBelInput(lut, ctx->idf("I[%d]", k), w.slice_inputs.at(z * K + k)); ctx->addBelOutput(lut, id_F, w.f.at(z)); // FF data can come from LUT output or LUT I3 add_pip(Loc(x, y, 0), w.f.at(z), w.d.at(z)); add_pip(Loc(x, y, 0), w.slice_inputs.at(z * K + (K - 1)), w.d.at(z)); // Create DFF bel - BelId dff = ctx->addBel(h.xy_id(x, y, ctx->id(stringf("SLICE%d_FF", z))), id_DFF, Loc(x, y, z * 2 + 1), - false, false); + BelId dff = + ctx->addBel(h.xy_id(x, y, ctx->idf("SLICE%d_FF", z)), id_DFF, Loc(x, y, z * 2 + 1), false, false); ctx->addBelInput(dff, id_CLK, w.clk.at(z)); ctx->addBelInput(dff, id_D, w.d.at(z)); ctx->addBelOutput(dff, id_Q, w.q.at(z)); @@ -495,7 +494,7 @@ struct OkamiImpl : ViaductAPI auto &fc = fast_cell_info.at(ci->flat_index); if (ci->type == id_LUT4) { fc.lut_f = ci->getPort(id_F); - fc.lut_i3_used = (ci->getPort(ctx->id(stringf("I[%d]", K - 1))) != nullptr); + fc.lut_i3_used = (ci->getPort(ctx->idf("I[%d]", K - 1)) != nullptr); } else if (ci->type == id_DFF) { fc.ff_d = ci->getPort(id_D); } diff --git a/generic/viaduct_helpers.cc b/generic/viaduct_helpers.cc index 153d2a0e6a..53df625cf9 100644 --- a/generic/viaduct_helpers.cc +++ b/generic/viaduct_helpers.cc @@ -29,15 +29,15 @@ void ViaductHelpers::resize_ids(int x, int y, int z) { NPNR_ASSERT(x >= 0 && y >= 0 && x <= 20000 && y <= 20000 && z <= 1000); while (int(x_ids.size()) <= x) { - IdString next = ctx->id(stringf("X%d", int(x_ids.size()))); + IdString next = ctx->idf("X%d", int(x_ids.size())); x_ids.push_back(next); } while (int(y_ids.size()) <= y) { - IdString next = ctx->id(stringf("Y%d", int(y_ids.size()))); + IdString next = ctx->idf("Y%d", int(y_ids.size())); y_ids.push_back(next); } while (int(z_ids.size()) <= y) { - IdString next = ctx->id(stringf("Z%d", int(z_ids.size()))); + IdString next = ctx->idf("Z%d", int(z_ids.size())); z_ids.push_back(next); } } diff --git a/gowin/pack.cc b/gowin/pack.cc index 4b5bc81db6..d978ac408a 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -733,7 +733,7 @@ void pack_sram(Context *ctx) // ci->disconnectPort(id_WRE); for (int i = 0; i < 4; i++) - ci->disconnectPort(ctx->id(stringf("RAD[%d]", i))); + ci->disconnectPort(ctx->idf("RAD[%d]", i)); // Setup placement constraints // Use the 0th bit as an anchor diff --git a/ice40/arch.cc b/ice40/arch.cc index 6746b3028f..905b8d615e 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -111,12 +111,12 @@ Arch::Arch(ArchArgs args) : args(args) log_error("Unsupported package '%s'.\n", args.package.c_str()); for (int i = 0; i < chip_info->width; i++) { - IdString x_id = id(stringf("X%d", i)); + IdString x_id = idf("X%d", i); x_ids.push_back(x_id); id_to_x[x_id] = i; } for (int i = 0; i < chip_info->height; i++) { - IdString y_id = id(stringf("Y%d", i)); + IdString y_id = idf("Y%d", i); y_ids.push_back(y_id); id_to_y[y_id] = i; } diff --git a/machxo2/arch.cc b/machxo2/arch.cc index 5fcdeaf759..b1a4a62a99 100644 --- a/machxo2/arch.cc +++ b/machxo2/arch.cc @@ -93,17 +93,17 @@ Arch::Arch(ArchArgs args) : args(args) BaseArch::init_bel_buckets(); for (int i = 0; i < chip_info->width; i++) - x_ids.push_back(id(stringf("X%d", i))); + x_ids.push_back(idf("X%d", i)); for (int i = 0; i < chip_info->height; i++) - y_ids.push_back(id(stringf("Y%d", i))); + y_ids.push_back(idf("Y%d", i)); for (int i = 0; i < chip_info->width; i++) { - IdString x_id = id(stringf("X%d", i)); + IdString x_id = idf("X%d", i); x_ids.push_back(x_id); id_to_x[x_id] = i; } for (int i = 0; i < chip_info->height; i++) { - IdString y_id = id(stringf("Y%d", i)); + IdString y_id = idf("Y%d", i); y_ids.push_back(y_id); id_to_y[y_id] = i; } diff --git a/mistral/arch.cc b/mistral/arch.cc index 46ed4f62a5..4023f5c317 100644 --- a/mistral/arch.cc +++ b/mistral/arch.cc @@ -88,7 +88,7 @@ Arch::Arch(ArchArgs args) // Setup fast identifier maps for (int i = 0; i < 1024; i++) { - IdString int_id = id(stringf("%d", i)); + IdString int_id = idf("%d", i); int2id.push_back(int_id); id2int[int_id] = i; } diff --git a/mistral/globals.cc b/mistral/globals.cc index 1ba709191e..6af8920d76 100644 --- a/mistral/globals.cc +++ b/mistral/globals.cc @@ -30,7 +30,7 @@ void Arch::create_clkbuf(int x, int y) continue; // TODO: why do other Zs not work? // For now we only consider the input path from general routing, other inputs like dedicated clock pins are // still a TODO - BelId bel = add_bel(x, y, id(stringf("CLKBUF[%d]", z)), id_MISTRAL_CLKENA); + BelId bel = add_bel(x, y, idf("CLKBUF[%d]", z), id_MISTRAL_CLKENA); add_bel_pin(bel, id_A, PORT_IN, get_port(CycloneV::CMUXHG, x, y, -1, CycloneV::CLKIN, z)); add_bel_pin(bel, id_Q, PORT_OUT, get_port(CycloneV::CMUXHG, x, y, z, CycloneV::CLKOUT)); // TODO: enable pin @@ -48,9 +48,9 @@ void Arch::create_hps_mpu_general_purpose(int x, int y) BelId gp_bel = add_bel(x, y, id_cyclonev_hps_interface_mpu_general_purpose, id_cyclonev_hps_interface_mpu_general_purpose); for (int i = 0; i < 32; i++) { - add_bel_pin(gp_bel, id(stringf("gp_in[%d]", i)), PORT_IN, + add_bel_pin(gp_bel, idf("gp_in[%d]", i), PORT_IN, get_port(CycloneV::HPS_MPU_GENERAL_PURPOSE, x, y, -1, CycloneV::GP_IN, i)); - add_bel_pin(gp_bel, id(stringf("gp_out[%d]", i)), PORT_OUT, + add_bel_pin(gp_bel, idf("gp_out[%d]", i), PORT_OUT, get_port(CycloneV::HPS_MPU_GENERAL_PURPOSE, x, y, -1, CycloneV::GP_OUT, i)); } } diff --git a/mistral/io.cc b/mistral/io.cc index c8d0238dcf..6690fc16e2 100644 --- a/mistral/io.cc +++ b/mistral/io.cc @@ -27,8 +27,8 @@ void Arch::create_gpio(int x, int y) { for (int z = 0; z < 4; z++) { // Notional pad wire - WireId pad = add_wire(x, y, id(stringf("PAD[%d]", z))); - BelId bel = add_bel(x, y, id(stringf("IO[%d]", z)), id_MISTRAL_IO); + WireId pad = add_wire(x, y, idf("PAD[%d]", z)); + BelId bel = add_bel(x, y, idf("IO[%d]", z), id_MISTRAL_IO); add_bel_pin(bel, id_PAD, PORT_INOUT, pad); if (has_port(CycloneV::GPIO, x, y, z, CycloneV::DATAOUT, 0)) { // FIXME: is the port index of zero always correct? diff --git a/mistral/lab.cc b/mistral/lab.cc index 4b66ed0c8a..d65ccf537f 100644 --- a/mistral/lab.cc +++ b/mistral/lab.cc @@ -37,10 +37,10 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx) // Create the control set and E/F selection - which is per pair of FF for (int i = 0; i < 2; i++) { // Wires - alm.sel_clk[i] = arch->add_wire(x, y, arch->id(stringf("CLK%c[%d]", i ? 'B' : 'T', z))); - alm.sel_ena[i] = arch->add_wire(x, y, arch->id(stringf("ENA%c[%d]", i ? 'B' : 'T', z))); - alm.sel_aclr[i] = arch->add_wire(x, y, arch->id(stringf("ACLR%c[%d]", i ? 'B' : 'T', z))); - alm.sel_ef[i] = arch->add_wire(x, y, arch->id(stringf("%cEF[%d]", i ? 'B' : 'T', z))); + alm.sel_clk[i] = arch->add_wire(x, y, arch->idf("CLK%c[%d]", i ? 'B' : 'T', z)); + alm.sel_ena[i] = arch->add_wire(x, y, arch->idf("ENA%c[%d]", i ? 'B' : 'T', z)); + alm.sel_aclr[i] = arch->add_wire(x, y, arch->idf("ACLR%c[%d]", i ? 'B' : 'T', z)); + alm.sel_ef[i] = arch->add_wire(x, y, arch->idf("%cEF[%d]", i ? 'B' : 'T', z)); // Muxes - three CLK/ENA per LAB, two ACLR for (int j = 0; j < 3; j++) { arch->add_pip(lab.clk_wires[j], alm.sel_clk[i]); @@ -72,20 +72,20 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx) } } else { // Output from last combinational unit - carry_in = arch->add_wire(x, y, arch->id(stringf("CARRY[%d]", (z * 2 + i) - 1))); - share_in = arch->add_wire(x, y, arch->id(stringf("SHARE[%d]", (z * 2 + i) - 1))); + carry_in = arch->add_wire(x, y, arch->idf("CARRY[%d]", (z * 2 + i) - 1)); + share_in = arch->add_wire(x, y, arch->idf("SHARE[%d]", (z * 2 + i) - 1)); } if (z == 9 && i == 1) { carry_out = arch->add_wire(x, y, id_CO); share_out = arch->add_wire(x, y, id_SHAREOUT); } else { - carry_out = arch->add_wire(x, y, arch->id(stringf("CARRY[%d]", z * 2 + i))); - share_out = arch->add_wire(x, y, arch->id(stringf("SHARE[%d]", z * 2 + i))); + carry_out = arch->add_wire(x, y, arch->idf("CARRY[%d]", z * 2 + i)); + share_out = arch->add_wire(x, y, arch->idf("SHARE[%d]", z * 2 + i)); } - BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_COMB%d", z, i)), - lab.is_mlab ? id_MISTRAL_MCOMB : id_MISTRAL_COMB); + BelId bel = + arch->add_bel(x, y, arch->idf("ALM%d_COMB%d", z, i), lab.is_mlab ? id_MISTRAL_MCOMB : id_MISTRAL_COMB); // LUT/MUX inputs arch->add_bel_pin(bel, id_A, PORT_IN, arch->get_port(block_type, x, y, z, CycloneV::A)); arch->add_bel_pin(bel, id_B, PORT_IN, arch->get_port(block_type, x, y, z, CycloneV::B)); @@ -101,7 +101,7 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx) arch->add_bel_pin(bel, id_CO, PORT_OUT, carry_out); arch->add_bel_pin(bel, id_SHAREOUT, PORT_OUT, share_out); // Combinational output - alm.comb_out[i] = arch->add_wire(x, y, arch->id(stringf("COMBOUT[%d]", z * 2 + i))); + alm.comb_out[i] = arch->add_wire(x, y, arch->idf("COMBOUT[%d]", z * 2 + i)); arch->add_bel_pin(bel, id_COMBOUT, PORT_OUT, alm.comb_out[i]); if (lab.is_mlab) { // Write address - shared between all ALMs in a LAB @@ -128,11 +128,11 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx) for (int i = 0; i < 4; i++) { // FF input, selected by *PKREG* - alm.ff_in[i] = arch->add_wire(x, y, arch->id(stringf("FFIN[%d]", (z * 4) + i))); + alm.ff_in[i] = arch->add_wire(x, y, arch->idf("FFIN[%d]", (z * 4) + i)); arch->add_pip(alm.comb_out[i / 2], alm.ff_in[i]); arch->add_pip(alm.sel_ef[i / 2], alm.ff_in[i]); // FF bel - BelId bel = arch->add_bel(x, y, arch->id(stringf("ALM%d_FF%d", z, i)), id_MISTRAL_FF); + BelId bel = arch->add_bel(x, y, arch->idf("ALM%d_FF%d", z, i), id_MISTRAL_FF); arch->add_bel_pin(bel, id_CLK, PORT_IN, alm.sel_clk[i / 2]); arch->add_bel_pin(bel, id_ENA, PORT_IN, alm.sel_ena[i / 2]); arch->add_bel_pin(bel, id_ACLR, PORT_IN, alm.sel_aclr[i / 2]); @@ -142,7 +142,7 @@ static void create_alm(Arch *arch, int x, int y, int z, uint32_t lab_idx) arch->add_bel_pin(bel, id_SDATA, PORT_IN, alm.sel_ef[i / 2]); // FF output - alm.ff_out[i] = arch->add_wire(x, y, arch->id(stringf("FFOUT[%d]", (z * 4) + i))); + alm.ff_out[i] = arch->add_wire(x, y, arch->idf("FFOUT[%d]", (z * 4) + i)); arch->add_bel_pin(bel, id_Q, PORT_OUT, alm.ff_out[i]); // Output mux (*DFF*) WireId out = arch->get_port(block_type, x, y, z, outputs[i]); @@ -182,7 +182,7 @@ void Arch::create_lab(int x, int y, bool is_mlab) // Clocks - hardcode to CLKA choices, as both CLKA and CLKB coming from general routing causes unexpected // permutations for (int i = 0; i < 3; i++) { - lab.clk_wires[i] = add_wire(x, y, id(stringf("CLK%d", i))); + lab.clk_wires[i] = add_wire(x, y, idf("CLK%d", i)); add_pip(get_port(block_type, x, y, -1, CycloneV::CLKIN, 0), lab.clk_wires[i]); // dedicated routing add_pip(get_port(block_type, x, y, -1, CycloneV::DATAIN, 0), lab.clk_wires[i]); // general routing } @@ -278,7 +278,7 @@ void Arch::assign_comb_info(CellInfo *cell) const cell->combInfo.lut_input_count = 5; cell->combInfo.lut_bits_count = 32; for (int i = 0; i < 5; i++) - cell->combInfo.lut_in[i] = cell->getPort(id(stringf("B1ADDR[%d]", i))); + cell->combInfo.lut_in[i] = cell->getPort(idf("B1ADDR[%d]", i)); auto key = get_mlab_key(cell); cell->combInfo.mlab_group = mlab_groups(key); cell->combInfo.comb_out = cell->getPort(id_B1DATA); @@ -797,8 +797,8 @@ static void assign_mlab_inputs(Context *ctx, CellInfo *cell, int lut) std::array raddr_pins{id_A, id_B, id_C, id_D, id_F0}; for (int i = 0; i < 5; i++) { - cell->pin_data[ctx->id(stringf("A1ADDR[%d]", i))].bel_pins = {ctx->id(stringf("WA%d", i))}; - cell->pin_data[ctx->id(stringf("B1ADDR[%d]", i))].bel_pins = {raddr_pins.at(i)}; + cell->pin_data[ctx->idf("A1ADDR[%d]", i)].bel_pins = {ctx->idf("WA%d", i)}; + cell->pin_data[ctx->idf("B1ADDR[%d]", i)].bel_pins = {raddr_pins.at(i)}; } } @@ -918,7 +918,7 @@ void Arch::reassign_alm_inputs(uint32_t lab, uint8_t alm) CellInfo *ff = ffs[i * 2 + j]; if (!ff || !ff->ffInfo.datain || alm_data.l6_mode) continue; - CellInfo *rt_lut = createCell(id(stringf("%s$ROUTETHRU", nameOf(ff))), id_MISTRAL_BUF); + CellInfo *rt_lut = createCell(idf("%s$ROUTETHRU", nameOf(ff)), id_MISTRAL_BUF); rt_lut->addInput(id_A); rt_lut->addOutput(id_Q); // Disconnect the original data input to the FF, and connect it to the route-thru LUT instead diff --git a/mistral/m10k.cc b/mistral/m10k.cc index 4da1204fd8..df44c66399 100644 --- a/mistral/m10k.cc +++ b/mistral/m10k.cc @@ -27,34 +27,28 @@ void Arch::create_m10k(int x, int y) add_bel_pin(bel, id_ADDRSTALLA, PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLA, 0)); add_bel_pin(bel, id_ADDRSTALLB, PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRSTALLB, 0)); for (int z = 0; z < 2; z++) { - add_bel_pin(bel, id(stringf("BYTEENABLEA[%d]", z)), PORT_IN, + add_bel_pin(bel, idf("BYTEENABLEA[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::BYTEENABLEA, z)); - add_bel_pin(bel, id(stringf("BYTEENABLEB[%d]", z)), PORT_IN, + add_bel_pin(bel, idf("BYTEENABLEB[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::BYTEENABLEB, z)); - add_bel_pin(bel, id(stringf("ACLR[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ACLR, z)); - add_bel_pin(bel, id(stringf("RDEN[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::RDEN, z)); - add_bel_pin(bel, id(stringf("WREN[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::WREN, z)); - add_bel_pin(bel, id(stringf("CLKIN[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z)); - add_bel_pin(bel, id(stringf("CLKIN[%d]", z + 6)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z + 6)); + add_bel_pin(bel, idf("ACLR[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ACLR, z)); + add_bel_pin(bel, idf("RDEN[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::RDEN, z)); + add_bel_pin(bel, idf("WREN[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::WREN, z)); + add_bel_pin(bel, idf("CLKIN[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z)); + add_bel_pin(bel, idf("CLKIN[%d]", z + 6), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::CLKIN, z + 6)); } for (int z = 0; z < 4; z++) { - add_bel_pin(bel, id(stringf("ENABLE[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::ENABLE, z)); + add_bel_pin(bel, idf("ENABLE[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ENABLE, z)); } for (int z = 0; z < 12; z++) { - add_bel_pin(bel, id(stringf("ADDRA[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRA, z)); - add_bel_pin(bel, id(stringf("ADDRB[%d]", z)), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRB, z)); + add_bel_pin(bel, idf("ADDRA[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRA, z)); + add_bel_pin(bel, idf("ADDRB[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::ADDRB, z)); } for (int z = 0; z < 20; z++) { - add_bel_pin(bel, id(stringf("DATAAIN[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::DATAAIN, z)); - add_bel_pin(bel, id(stringf("DATABIN[%d]", z)), PORT_IN, - get_port(CycloneV::M10K, x, y, -1, CycloneV::DATABIN, z)); - add_bel_pin(bel, id(stringf("DATAAOUT[%d]", z)), PORT_OUT, - get_port(CycloneV::M10K, x, y, -1, CycloneV::DATAAOUT, z)); - add_bel_pin(bel, id(stringf("DATABOUT[%d]", z)), PORT_OUT, - get_port(CycloneV::M10K, x, y, -1, CycloneV::DATABOUT, z)); + add_bel_pin(bel, idf("DATAAIN[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::DATAAIN, z)); + add_bel_pin(bel, idf("DATABIN[%d]", z), PORT_IN, get_port(CycloneV::M10K, x, y, -1, CycloneV::DATABIN, z)); + add_bel_pin(bel, idf("DATAAOUT[%d]", z), PORT_OUT, get_port(CycloneV::M10K, x, y, -1, CycloneV::DATAAOUT, z)); + add_bel_pin(bel, idf("DATABOUT[%d]", z), PORT_OUT, get_port(CycloneV::M10K, x, y, -1, CycloneV::DATABOUT, z)); } } diff --git a/mistral/pack.cc b/mistral/pack.cc index 27ad3c92bd..709479a294 100644 --- a/mistral/pack.cc +++ b/mistral/pack.cc @@ -417,10 +417,8 @@ struct MistralPacker bit_offset = 1; } for (int bit = bit_offset; bit < abits; bit++) { - ci->pin_data[ctx->id(stringf("A1ADDR[%d]", bit))].bel_pins = { - ctx->id(stringf("ADDRA[%d]", bit + addr_offset))}; - ci->pin_data[ctx->id(stringf("B1ADDR[%d]", bit))].bel_pins = { - ctx->id(stringf("ADDRB[%d]", bit + addr_offset))}; + ci->pin_data[ctx->idf("A1ADDR[%d]", bit)].bel_pins = {ctx->idf("ADDRA[%d]", bit + addr_offset)}; + ci->pin_data[ctx->idf("B1ADDR[%d]", bit)].bel_pins = {ctx->idf("ADDRB[%d]", bit + addr_offset)}; } // Data lines @@ -451,13 +449,12 @@ struct MistralPacker } for (int bit = 0; bit < dbits; bit++) { for (int offset : offsets) { - ci->pin_data[ctx->id(stringf("A1DATA[%d]", bit))].bel_pins.push_back( - ctx->id(stringf("DATAAIN[%d]", bit + offset))); + ci->pin_data[ctx->idf("A1DATA[%d]", bit)].bel_pins.push_back(ctx->idf("DATAAIN[%d]", bit + offset)); } } for (int bit = 0; bit < dbits; bit++) { - ci->pin_data[ctx->id(stringf("B1DATA[%d]", bit))].bel_pins = {ctx->id(stringf("DATABOUT[%d]", bit))}; + ci->pin_data[ctx->idf("B1DATA[%d]", bit)].bel_pins = {ctx->idf("DATABOUT[%d]", bit)}; } } } diff --git a/nexus/arch.cc b/nexus/arch.cc index 9679c3fbed..b2ae22ce37 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -125,12 +125,12 @@ Arch::Arch(ArchArgs args) : args(args) } for (int i = 0; i < chip_info->width; i++) { - IdString x_id = id(stringf("X%d", i)); + IdString x_id = idf("X%d", i); x_ids.push_back(x_id); id_to_x[x_id] = i; } for (int i = 0; i < chip_info->height; i++) { - IdString y_id = id(stringf("Y%d", i)); + IdString y_id = idf("Y%d", i); y_ids.push_back(y_id); id_to_y[y_id] = i; } @@ -377,7 +377,7 @@ IdStringList Arch::getPipName(PipId pip) const { NPNR_ASSERT(pip != PipId()); std::array ids{x_ids.at(pip.tile % chip_info->width), y_ids.at(pip.tile / chip_info->width), - id(stringf("%d", pip.index)), IdString(loc_data(pip).wires[pip_data(pip).to_wire].name), + idf("%d", pip.index), IdString(loc_data(pip).wires[pip_data(pip).to_wire].name), IdString(loc_data(pip).wires[pip_data(pip).from_wire].name)}; return IdStringList(ids); } @@ -784,7 +784,7 @@ bool Arch::route() CellPinMux Arch::get_cell_pinmux(const CellInfo *cell, IdString pin) const { - IdString param = id(stringf("%sMUX", pin.c_str(this))); + IdString param = idf("%sMUX", pin.c_str(this)); auto fnd_param = cell->params.find(param); if (fnd_param == cell->params.end()) return PINMUX_SIG; @@ -805,7 +805,7 @@ CellPinMux Arch::get_cell_pinmux(const CellInfo *cell, IdString pin) const void Arch::set_cell_pinmux(CellInfo *cell, IdString pin, CellPinMux state) { - IdString param = id(stringf("%sMUX", pin.c_str(this))); + IdString param = idf("%sMUX", pin.c_str(this)); switch (state) { case PINMUX_SIG: cell->params.erase(param); diff --git a/nexus/fasm.cc b/nexus/fasm.cc index de03fb8226..48a3d2591b 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -595,7 +595,7 @@ struct NexusFasmWriter if (wid > 0) { push(stringf("IP_EBR_WID%d", wid)); for (int i = 0; i < 64; i++) { - IdString param = ctx->id(stringf("INITVAL_%02X", i)); + IdString param = ctx->idf("INITVAL_%02X", i); if (!cell->params.count(param)) continue; auto &prop = cell->params.at(param); @@ -906,7 +906,7 @@ struct NexusFasmWriter l.x = 1; push(stringf("IP_LRAM_CORE_R%dC%d", l.y, l.x)); for (int i = 0; i < 128; i++) { - IdString param = ctx->id(stringf("INITVAL_%02X", i)); + IdString param = ctx->idf("INITVAL_%02X", i); if (!cell->params.count(param)) continue; auto &prop = cell->params.at(param); diff --git a/nexus/pack.cc b/nexus/pack.cc index 15752171e4..0aa611440a 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -316,8 +316,8 @@ struct NexusPacker return z; } - NetInfo *new_net = ctx->createNet(ctx->id(stringf("$CONST_%s_NET_", type.c_str(ctx)))); - CellInfo *new_cell = ctx->createCell(ctx->id(stringf("$CONST_%s_DRV_", type.c_str(ctx))), type); + NetInfo *new_net = ctx->createNet(ctx->idf("$CONST_%s_NET_", type.c_str(ctx))); + CellInfo *new_cell = ctx->createCell(ctx->idf("$CONST_%s_DRV_", type.c_str(ctx)), type); new_cell->addOutput(id_Z); new_cell->connectPort(id_Z, new_net); return new_net; @@ -823,10 +823,10 @@ struct NexusPacker Tpred pred) { // Create the buffered net - NetInfo *buffered_net = ctx->createNet(ctx->id(stringf("%s$%s", ctx->nameOf(net), name_postfix.c_str()))); + NetInfo *buffered_net = ctx->createNet(ctx->idf("%s$%s", ctx->nameOf(net), name_postfix.c_str())); // Create the buffer cell - CellInfo *buffer = ctx->createCell( - ctx->id(stringf("%s$drv_%s", ctx->nameOf(buffered_net), ctx->nameOf(buffer_type))), buffer_type); + CellInfo *buffer = ctx->createCell(ctx->idf("%s$drv_%s", ctx->nameOf(buffered_net), ctx->nameOf(buffer_type)), + buffer_type); buffer->addInput(i); buffer->addOutput(o); // Drive the buffered net with the buffer @@ -915,9 +915,9 @@ struct NexusPacker } // Get a bus port name - IdString bus(const std::string &base, int i) { return ctx->id(stringf("%s[%d]", base.c_str(), i)); } + IdString bus(const std::string &base, int i) { return ctx->idf("%s[%d]", base.c_str(), i); } - IdString bus_flat(const std::string &base, int i) { return ctx->id(stringf("%s%d", base.c_str(), i)); } + IdString bus_flat(const std::string &base, int i) { return ctx->idf("%s%d", base.c_str(), i); } // Pack a LUTRAM into COMB and RAMW cells void pack_lutram() @@ -939,11 +939,10 @@ struct NexusPacker for (CellInfo *ci : lutrams) { // Create constituent cells - CellInfo *ramw = ctx->createCell(ctx->id(stringf("%s$lutram_ramw$", ctx->nameOf(ci))), id_RAMW); + CellInfo *ramw = ctx->createCell(ctx->idf("%s$lutram_ramw$", ctx->nameOf(ci)), id_RAMW); std::vector combs; for (int i = 0; i < 4; i++) - combs.push_back( - ctx->createCell(ctx->id(stringf("%s$lutram_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); + combs.push_back(ctx->createCell(ctx->idf("%s$lutram_comb[%d]$", ctx->nameOf(ci), i), id_OXIDE_COMB)); // Rewiring - external WCK and WRE ci->movePortTo(id_WCK, ramw, id_CLK); ci->movePortTo(id_WRE, ramw, id_LSR); @@ -951,8 +950,8 @@ struct NexusPacker // Internal WCK and WRE signals ramw->addOutput(id_WCKO); ramw->addOutput(id_WREO); - NetInfo *int_wck = ctx->createNet(ctx->id(stringf("%s$lutram_wck$", ctx->nameOf(ci)))); - NetInfo *int_wre = ctx->createNet(ctx->id(stringf("%s$lutram_wre$", ctx->nameOf(ci)))); + NetInfo *int_wck = ctx->createNet(ctx->idf("%s$lutram_wck$", ctx->nameOf(ci))); + NetInfo *int_wre = ctx->createNet(ctx->idf("%s$lutram_wre$", ctx->nameOf(ci))); ramw->connectPort(id_WCKO, int_wck); ramw->connectPort(id_WREO, int_wre); @@ -977,7 +976,7 @@ struct NexusPacker ci->disconnectPort(bus("RAD", i)); } // Write address - internal - NetInfo *int_wad = ctx->createNet(ctx->id(stringf("%s$lutram_wad[%d]$", ctx->nameOf(ci), i))); + NetInfo *int_wad = ctx->createNet(ctx->idf("%s$lutram_wad[%d]$", ctx->nameOf(ci), i)); ramw->addOutput(bus_flat("WADO", i)); ramw->connectPort(bus_flat("WADO", i), int_wad); for (int j = 0; j < 4; j++) { @@ -985,7 +984,7 @@ struct NexusPacker combs[j]->connectPort(bus_flat("WAD", i), int_wad); } // Write data - internal - NetInfo *int_wd = ctx->createNet(ctx->id(stringf("%s$lutram_wd[%d]$", ctx->nameOf(ci), i))); + NetInfo *int_wd = ctx->createNet(ctx->idf("%s$lutram_wd[%d]$", ctx->nameOf(ci), i)); ramw->addOutput(bus_flat("WDO", i)); ramw->connectPort(bus_flat("WDO", i), int_wd); combs[i]->addInput(id_WDI); @@ -1256,8 +1255,7 @@ struct NexusPacker for (CellInfo *ci : widefns) { std::vector combs; for (int i = 0; i < 2; i++) - combs.push_back( - ctx->createCell(ctx->id(stringf("%s$widefn_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); + combs.push_back(ctx->createCell(ctx->idf("%s$widefn_comb[%d]$", ctx->nameOf(ci), i), id_OXIDE_COMB)); for (int i = 0; i < 2; i++) { ci->movePortTo(bus_flat("A", i), combs[i], id_A); @@ -1269,7 +1267,7 @@ struct NexusPacker ci->movePortTo(id_SEL, combs[0], id_SEL); ci->movePortTo(id_Z, combs[0], id_OFX); - NetInfo *f1 = ctx->createNet(ctx->id(stringf("%s$widefn_f1$", ctx->nameOf(ci)))); + NetInfo *f1 = ctx->createNet(ctx->idf("%s$widefn_f1$", ctx->nameOf(ci))); combs[0]->addInput(id_F1); combs[1]->addOutput(id_F); combs[1]->connectPort(id_F, f1); @@ -1313,8 +1311,7 @@ struct NexusPacker // Split the carry into two COMB cells std::vector combs; for (int i = 0; i < 2; i++) - combs.push_back( - ctx->createCell(ctx->id(stringf("%s$ccu2_comb[%d]$", ctx->nameOf(ci), i)), id_OXIDE_COMB)); + combs.push_back(ctx->createCell(ctx->idf("%s$ccu2_comb[%d]$", ctx->nameOf(ci), i), id_OXIDE_COMB)); // Rewire LUT ports for (int i = 0; i < 2; i++) { combs[i]->params[id_MODE] = std::string("CCU2"); @@ -1336,7 +1333,7 @@ struct NexusPacker combs[1]->params[id_INIT] = ctx->parse_lattice_param_from_cell(ci, id_INIT1, 16, 0); // Internal carry net between the two split COMB cells - NetInfo *int_cy = ctx->createNet(ctx->id(stringf("%s$widefn_int_cy$", ctx->nameOf(ci)))); + NetInfo *int_cy = ctx->createNet(ctx->idf("%s$widefn_int_cy$", ctx->nameOf(ci))); combs[0]->addOutput(id_FCO); combs[1]->addInput(id_FCI); combs[0]->connectPort(id_FCO, int_cy); @@ -1550,7 +1547,7 @@ struct NexusPacker // Create a DSP cell CellInfo *create_dsp_cell(IdString base_name, IdString type, CellInfo *constr_base, int dx, int dz) { - IdString name = ctx->id(stringf("%s/%s_x%d_z%d", ctx->nameOf(base_name), ctx->nameOf(type), dx, dz)); + IdString name = ctx->idf("%s/%s_x%d_z%d", ctx->nameOf(base_name), ctx->nameOf(type), dx, dz); CellInfo *cell = ctx->createCell(name, type); if (constr_base != nullptr) { // We might be constraining against an already-constrained cell @@ -1739,10 +1736,10 @@ struct NexusPacker if (mt.wide > 0) { // Dot-product mode special case - ci->copyPortBusTo(ctx->id(stringf("B%d", (i * 9) / mt.wide)), (i * 9) % mt.wide, true, preadd9[i], - id_B, 0, false, 9); - ci->copyPortBusTo(ctx->id(stringf("A%d", (i * 9) / mt.wide)), (i * 9) % mt.wide, true, mult9[i], - id_A, 0, false, 9); + ci->copyPortBusTo(ctx->idf("B%d", (i * 9) / mt.wide), (i * 9) % mt.wide, true, preadd9[i], id_B, 0, + false, 9); + ci->copyPortBusTo(ctx->idf("A%d", (i * 9) / mt.wide), (i * 9) % mt.wide, true, mult9[i], id_A, 0, + false, 9); ci->copyPortTo(id_CLK, mult9[i], id_CLK); ci->copyPortTo((i > 1) ? id_CEA2A3 : id_CEA0A1, mult9[i], id_CEA); ci->copyPortTo((i > 1) ? id_RSTA2A3 : id_RSTA0A1, mult9[i], id_RSTA); @@ -1750,8 +1747,8 @@ struct NexusPacker ci->copyPortTo((i > 1) ? id_CEB2B3 : id_CEB0B1, preadd9[i], id_CEB); ci->copyPortTo((i > 1) ? id_RSTB2B3 : id_RSTB0B1, preadd9[i], id_RSTB); // Copy register configuration - copy_param(ci, ctx->id(stringf("REGINPUTAB%d", i)), mult9[i], id_REGBYPSA1); - copy_param(ci, ctx->id(stringf("REGINPUTAB%d", i)), preadd9[i], id_REGBYPSBR0); + copy_param(ci, ctx->idf("REGINPUTAB%d", i), mult9[i], id_REGBYPSA1); + copy_param(ci, ctx->idf("REGINPUTAB%d", i), preadd9[i], id_REGBYPSBR0); } else { // B input split across pre-adders ci->copyPortBusTo(id_B, b_start, true, preadd9[i], id_B, 0, false, 9); From f7354d092ddcb6020f1fc0a23267920884cf8641 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Aug 2022 15:47:22 +0100 Subject: [PATCH 171/712] nexus: Add timing data for LRAM Signed-off-by: gatecat --- nexus/arch.cc | 16 ++++++++++++++++ nexus/constids.inc | 1 + nexus/pack.cc | 13 +++++++++++++ 3 files changed, 30 insertions(+) diff --git a/nexus/arch.cc b/nexus/arch.cc index b2ae22ce37..4a956fae54 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -543,6 +543,14 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; clockInfoCount = 1; return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT; + } else if (cell->type == id_LRAM_CORE) { + if (port.in(id_OPCGLDCK, id_OPCGLOADCLK, id_SCANCLK, id_SCANRST, id_TBISTN, id_INITN, id_STDBYN, id_IGN, + id_DPS)) + return TMG_IGNORE; + if (port == id_CLK) + return TMG_CLOCK_INPUT; + clockInfoCount = 1; + return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT; } else if (cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE) { return (cell->ports.at(port).type == PORT_IN) ? TMG_COMB_INPUT : TMG_COMB_OUTPUT; } else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) { @@ -602,6 +610,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } // Lookup edge based on inversion info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE; + } else if (cell->type == id_LRAM_CORE) { + info.clock_port = id_CLK; + if (cell->ports.at(port).type == PORT_IN) { + lookup_cell_setuphold(cell->tmg_index, lookup_port(port), id_CLK, info.setup, info.hold); + } else { + NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, lookup_port(port), info.clockToQ)); + } + info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE; } else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) { info.clock_port = id_CLK; if (cell->ports.at(port).type == PORT_IN) { diff --git a/nexus/constids.inc b/nexus/constids.inc index bdf8867b50..8731fad7b3 100644 --- a/nexus/constids.inc +++ b/nexus/constids.inc @@ -465,6 +465,7 @@ X(TBISTN) X(WE) X(CEOUTA) X(CEOUTB) +X(OPCGLOADCLK) X(MULTADDSUB9X9WIDE) X(MULTADDSUB18X18WIDE) diff --git a/nexus/pack.cc b/nexus/pack.cc index 0aa611440a..e727364d6b 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -2614,6 +2614,19 @@ void Arch::assignCellInfo(CellInfo *cell) cell->tmg_index = get_cell_timing_idx(id(str_or_default(cell->params, id_MODE, "DP16K") + "_MODE")); NPNR_ASSERT(cell->tmg_index != -1); + } else if (cell->type == id_LRAM_CORE) { + // Strip off bus indices to get the timing ports + // as timing is generally word-wide + for (const auto &port : cell->ports) { + const std::string &name = port.first.str(this); + size_t idx_end = name.find_last_not_of("0123456789"); + std::string base = name.substr(0, idx_end + 1); + // Strip off bus index + cell->tmg_portmap[port.first] = id(base); + } + + cell->tmg_index = get_cell_timing_idx(id_LRAM_CORE); + NPNR_ASSERT(cell->tmg_index != -1); } else if (is_dsp_cell(cell)) { // Strip off bus indices to get the timing ports // as timing is generally word-wide From c60fb94b6c45ca74632e972995555170063b3a03 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Aug 2022 18:58:22 +0100 Subject: [PATCH 172/712] refactor: Use IdString::in instead of || chains Signed-off-by: gatecat --- ecp5/arch.cc | 76 +++++++++++++++++++-------------------------- ecp5/arch.h | 2 +- ecp5/arch_place.cc | 5 ++- ecp5/bitstream.cc | 13 +++----- ecp5/cells.cc | 2 +- ecp5/cells.h | 6 ++-- ecp5/gfx.cc | 45 ++++++++++----------------- ecp5/globals.cc | 22 ++++++------- ecp5/pack.cc | 41 +++++++++++------------- gowin/cells.cc | 5 ++- gowin/gfx.cc | 2 +- gowin/globals.cc | 3 +- ice40/arch.cc | 36 ++++++++++----------- ice40/arch_place.cc | 2 +- ice40/bitstream.cc | 3 +- ice40/cells.cc | 8 ++--- ice40/cells.h | 20 +++++------- ice40/pack.cc | 8 ++--- mistral/globals.cc | 5 +-- nexus/arch.cc | 37 ++++++++++------------ nexus/arch.h | 2 +- nexus/fasm.cc | 7 ++--- nexus/global.cc | 2 +- nexus/pack.cc | 4 +-- 24 files changed, 153 insertions(+), 203 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 0b763612de..f031c9044e 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -557,8 +557,7 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const { - if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin == id_FXA || dst_pin == id_FXB || - (src_pin == id_F && dst_pin == id_DI)) + if ((src_pin == id_FCO && dst_pin == id_FCI) || dst_pin.in(id_FXA, id_FXB) || (src_pin == id_F && dst_pin == id_DI)) return 0; auto driver_loc = getBelLocation(src_bel); auto sink_loc = getBelLocation(dst_bel); @@ -588,7 +587,7 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay if (net_info->driver.port == id_FCO && sink.port == id_FCI) { budget = 0; return true; - } else if (sink.port == id_FXA || sink.port == id_FXB) { + } else if (sink.port.in(id_FXA, id_FXB)) { budget = 0; return true; } else { @@ -819,8 +818,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort IdString tmg_type = has_carry ? (((cell->constr_z >> Arch::lc_idx_shift) % 2) ? id_TRELLIS_COMB_CARRY1 : id_TRELLIS_COMB_CARRY0) : id_TRELLIS_COMB; - if (fromPort == id_A || fromPort == id_B || fromPort == id_C || fromPort == id_D || fromPort == id_M || - fromPort == id_F1 || fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) + if (fromPort.in(id_A, id_B, id_C, id_D, id_M, id_F1, id_FXA, id_FXB, id_FCI)) return get_delay_from_tmg_db(tmg_type, fromPort, toPort, delay); else return false; @@ -842,7 +840,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } return false; } else if (cell->type == id_DCSC) { - if ((fromPort == id_CLK0 || fromPort == id_CLK1) && toPort == id_DCSOUT) { + if ((fromPort.in(id_CLK0, id_CLK1)) && toPort == id_DCSOUT) { delay = DelayQuad(0); return true; } @@ -858,7 +856,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return get_delay_from_tmg_db(cell->multInfo.timing_id, id(std::string("") + fn.front()), id_P, delay); } return false; - } else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) { + } else if (cell->type.in(id_IOLOGIC, id_SIOLOGIC)) { return false; } else { return false; @@ -872,18 +870,16 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in if (cell->type == id_TRELLIS_COMB) { if (port == id_WCK) return TMG_CLOCK_INPUT; - if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_FCI || port == id_FXA || - port == id_FXB || port == id_F1) + if (port.in(id_A, id_B, id_C, id_D, id_FCI, id_FXA, id_FXB, id_F1)) return TMG_COMB_INPUT; if (port == id_F && disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && disconnected(id_FCI)) return TMG_IGNORE; // LUT with no inputs is a constant - if (port == id_F || port == id_FCO || port == id_OFX) + if (port.in(id_F, id_FCO, id_OFX)) return TMG_COMB_OUTPUT; if (port == id_M) return TMG_COMB_INPUT; - if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 || - port == id_WRE) { + if (port.in(id_WD, id_WAD0, id_WAD1, id_WAD2, id_WAD3, id_WRE)) { clockInfoCount = 1; return TMG_REGISTER_INPUT; } @@ -892,7 +888,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED); if (port == id_CLK) return TMG_CLOCK_INPUT; - if (port == id_DI || (using_m && (port == id_M)) || port == id_CE || port == id_LSR) { + if (port == id_DI || (using_m && (port == id_M)) || port.in(id_CE, id_LSR)) { clockInfoCount = 1; return TMG_REGISTER_INPUT; } @@ -902,15 +898,13 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } return TMG_IGNORE; } else if (cell->type == id_TRELLIS_RAMW) { - if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 || - port == id_D0 || port == id_D1) + if (port.in(id_A0, id_A1, id_B0, id_B1, id_C0, id_C1, id_D0, id_D1)) return TMG_COMB_INPUT; - if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3 || port == id_WADO0 || - port == id_WADO1 || port == id_WADO2 || port == id_WADO3) + if (port.in(id_WDO0, id_WDO1, id_WDO2, id_WDO3, id_WADO0, id_WADO1, id_WADO2, id_WADO3)) return TMG_COMB_OUTPUT; return TMG_IGNORE; } else if (cell->type == id_TRELLIS_IO) { - if (port == id_T || port == id_I) + if (port.in(id_T, id_I)) return TMG_ENDPOINT; if (port == id_O) return TMG_STARTPOINT; @@ -922,13 +916,13 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_COMB_OUTPUT; return TMG_IGNORE; } else if (cell->type == id_DCSC) { - if (port == id_CLK0 || port == id_CLK1) + if (port.in(id_CLK0, id_CLK1)) return TMG_COMB_INPUT; if (port == id_DCSOUT) return TMG_COMB_OUTPUT; return TMG_IGNORE; } else if (cell->type == id_DP16KD) { - if (port == id_CLKA || port == id_CLKB) + if (port.in(id_CLKA, id_CLKB)) return TMG_CLOCK_INPUT; std::string port_name = port.str(this); for (auto c : boost::adaptors::reverse(port_name)) { @@ -942,10 +936,9 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } NPNR_ASSERT_FALSE_STR("no timing type for RAM port '" + port.str(this) + "'"); } else if (cell->type == id_MULT18X18D) { - if (port == id_CLK0 || port == id_CLK1 || port == id_CLK2 || port == id_CLK3) + if (port.in(id_CLK0, id_CLK1, id_CLK2, id_CLK3)) return TMG_CLOCK_INPUT; - if (port == id_CE0 || port == id_CE1 || port == id_CE2 || port == id_CE3 || port == id_RST0 || - port == id_RST1 || port == id_RST2 || port == id_RST3 || port == id_SIGNEDA || port == id_SIGNEDB) { + if (port.in(id_CE0, id_CE1, id_CE2, id_CE3, id_RST0, id_RST1, id_RST2, id_RST3, id_SIGNEDA, id_SIGNEDB)) { if (cell->multInfo.is_clocked) { clockInfoCount = 1; return TMG_REGISTER_INPUT; @@ -977,9 +970,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_IGNORE; // FIXME } else if (cell->type == id_EHXPLLL) { return TMG_IGNORE; - } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) { - if (port == id_CH0_FF_TXI_CLK || port == id_CH0_FF_RXI_CLK || port == id_CH1_FF_TXI_CLK || - port == id_CH1_FF_RXI_CLK) + } else if (cell->type.in(id_DCUA, id_EXTREFB, id_PCSCLKDIV)) { + if (port.in(id_CH0_FF_TXI_CLK, id_CH0_FF_RXI_CLK, id_CH1_FF_TXI_CLK, id_CH1_FF_RXI_CLK)) return TMG_CLOCK_INPUT; std::string prefix = port.str(this).substr(0, 9); if (prefix == "CH0_FF_TX" || prefix == "CH0_FF_RX" || prefix == "CH1_FF_TX" || prefix == "CH1_FF_RX") { @@ -987,18 +979,16 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT; } return TMG_IGNORE; - } else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) { - if (port == id_CLK || port == id_ECLK) { + } else if (cell->type.in(id_IOLOGIC, id_SIOLOGIC)) { + if (port.in(id_CLK, id_ECLK)) { return TMG_CLOCK_INPUT; - } else if (port == id_IOLDO || port == id_IOLDOI || port == id_IOLDOD || port == id_IOLTO || port == id_PADDI || - port == id_DQSR90 || port == id_DQSW || port == id_DQSW270) { + } else if (port.in(id_IOLDO, id_IOLDOI, id_IOLDOD, id_IOLTO, id_PADDI, id_DQSR90, id_DQSW, id_DQSW270)) { return TMG_IGNORE; } else { clockInfoCount = 1; return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT; } - } else if (cell->type == id_DTR || cell->type == id_USRMCLK || cell->type == id_SEDGA || cell->type == id_GSR || - cell->type == id_JTAGG) { + } else if (cell->type.in(id_DTR, id_USRMCLK, id_SEDGA, id_GSR, id_JTAGG)) { return (cell->ports.at(port).type == PORT_OUT) ? TMG_STARTPOINT : TMG_ENDPOINT; } else if (cell->type == id_OSCG) { if (port == id_OSC) @@ -1008,22 +998,22 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (cell->type == id_CLKDIVF) { if (port == id_CLKI) return TMG_CLOCK_INPUT; - else if (port == id_RST || port == id_ALIGNWD) + else if (port.in(id_RST, id_ALIGNWD)) return TMG_ENDPOINT; else if (port == id_CDIVX) return TMG_GEN_CLOCK; else NPNR_ASSERT_FALSE("bad clkdiv port"); } else if (cell->type == id_DQSBUFM) { - if (port == id_READ0 || port == id_READ1) { + if (port.in(id_READ0, id_READ1)) { clockInfoCount = 1; return TMG_REGISTER_INPUT; } else if (port == id_DATAVALID) { clockInfoCount = 1; return TMG_REGISTER_OUTPUT; - } else if (port == id_SCLK || port == id_ECLK || port == id_DQSI) { + } else if (port.in(id_SCLK, id_ECLK, id_DQSI)) { return TMG_CLOCK_INPUT; - } else if (port == id_DQSR90 || port == id_DQSW || port == id_DQSW270) { + } else if (port.in(id_DQSR90, id_DQSW, id_DQSW270)) { return TMG_GEN_CLOCK; } return (cell->ports.at(port).type == PORT_OUT) ? TMG_STARTPOINT : TMG_ENDPOINT; @@ -1054,8 +1044,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.hold = DelayPair(0); info.clockToQ = DelayQuad(0); if (cell->type == id_TRELLIS_COMB) { - if (port == id_WD || port == id_WAD0 || port == id_WAD1 || port == id_WAD2 || port == id_WAD3 || - port == id_WRE) { + if (port.in(id_WD, id_WAD0, id_WAD1, id_WAD2, id_WAD3, id_WRE)) { if (port == id_WD) port = id_WD0; info.edge = (cell->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV) ? FALLING_EDGE : RISING_EDGE; @@ -1064,7 +1053,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } } else if (cell->type == id_TRELLIS_FF) { bool using_m = (cell->ffInfo.flags & ArchCellInfo::FF_M_USED); - if (port == id_DI || port == id_CE || port == id_LSR || (using_m && port == id_M)) { + if (port.in(id_DI, id_CE, id_LSR) || (using_m && port == id_M)) { if (port == id_DI) port = id_DI0; if (port == id_M) @@ -1098,9 +1087,8 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port if (cell->ramInfo.is_pdp) { bool is_output = cell->ports.at(port).type == PORT_OUT; // In PDP mode, all read signals are in CLKB domain and write signals in CLKA domain - if (is_output || port == id_OCEB || port == id_CEB || port == id_ADB5 || port == id_ADB6 || - port == id_ADB7 || port == id_ADB8 || port == id_ADB9 || port == id_ADB10 || port == id_ADB11 || - port == id_ADB12 || port == id_ADB13) + if (is_output || port.in(id_OCEB, id_CEB, id_ADB5, id_ADB6, id_ADB7, id_ADB8, id_ADB9, id_ADB10, id_ADB11, + id_ADB12, id_ADB13)) info.clock_port = id_CLKB; else info.clock_port = id_CLKA; @@ -1133,7 +1121,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.setup = DelayPair(getDelayFromNS(1)); info.hold = DelayPair(getDelayFromNS(0)); } - } else if (cell->type == id_IOLOGIC || cell->type == id_SIOLOGIC) { + } else if (cell->type.in(id_IOLOGIC, id_SIOLOGIC)) { info.clock_port = id_CLK; info.edge = RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { @@ -1147,7 +1135,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.edge = RISING_EDGE; if (port == id_DATAVALID) { info.clockToQ = DelayQuad(getDelayFromNS(0.2)); - } else if (port == id_READ0 || port == id_READ1) { + } else if (port.in(id_READ0, id_READ1)) { info.setup = DelayPair(getDelayFromNS(0.5)); info.hold = DelayPair(getDelayFromNS(-0.4)); } else { diff --git a/ecp5/arch.h b/ecp5/arch.h index 3d95fc9b18..12f043b786 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -565,7 +565,7 @@ struct Arch : BaseArch void update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell) { CellInfo *act_cell = (old_cell == nullptr) ? new_cell : old_cell; - if (act_cell->type == id_TRELLIS_FF || act_cell->type == id_TRELLIS_COMB || act_cell->type == id_TRELLIS_RAMW) { + if (act_cell->type.in(id_TRELLIS_FF, id_TRELLIS_COMB, id_TRELLIS_RAMW)) { LogicTileStatus *lts = tile_status.at(tile_index(bel)).lts; NPNR_ASSERT(lts != nullptr); int z = loc_info(bel)->bel_data[bel.index].z; diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index afe9aca0d7..fed9c055d3 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -187,7 +187,7 @@ bool Arch::isBelLocationValid(BelId bel) const CellInfo *cell = getBoundBelCell(bel); if (cell == nullptr) { return true; - } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) { + } else if (cell->type.in(id_DCUA, id_EXTREFB, id_PCSCLKDIV)) { return args.type != ArchArgs::LFE5U_25F && args.type != ArchArgs::LFE5U_45F && args.type != ArchArgs::LFE5U_85F; } else { @@ -203,8 +203,7 @@ void Arch::setup_wire_locations() CellInfo *ci = cell.second.get(); if (ci->bel == BelId()) continue; - if (ci->type == id_MULT18X18D || ci->type == id_DCUA || ci->type == id_DDRDLL || ci->type == id_DQSBUFM || - ci->type == id_EHXPLLL) { + if (ci->type.in(id_MULT18X18D, id_DCUA, id_DDRDLL, id_DQSBUFM, id_EHXPLLL)) { for (auto &port : ci->ports) { if (port.second.net == nullptr) continue; diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 3c01fe71d3..37883bdad6 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -1110,25 +1110,22 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex // Tie signals as appropriate for (auto port : ci->ports) { - if (ci->ramInfo.is_pdp && (port.first == id_WEA || port.first == id_WEB || port.first == id_ADA4)) + if (ci->ramInfo.is_pdp && (port.first.in(id_WEA, id_WEB, id_ADA4))) continue; if (port.second.net == nullptr && port.second.type == PORT_IN) { - if (port.first == id_CLKA || port.first == id_CLKB || port.first == id_WEA || - port.first == id_WEB || port.first == id_RSTA || port.first == id_RSTB) { + if (port.first.in(id_CLKA, id_CLKB, id_WEA, id_WEB, id_RSTA, id_RSTB)) { // CIB clock or LSR. Tie to "1" (also 0 in prjtrellis db?) in CIB // If MUX doesn't exist, set to INV to emulate default 0 tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true); if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX"))) ci->params[ctx->id(port.first.str(ctx) + "MUX")] = std::string("INV"); - } else if (port.first == id_CEA || port.first == id_CEB || port.first == id_OCEA || - port.first == id_OCEB) { + } else if (port.first.in(id_CEA, id_CEB, id_OCEA, id_OCEB)) { // CIB CE. Tie to "1" in CIB // If MUX doesn't exist, set to passthru to emulate default 1 tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true); if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX"))) ci->params[ctx->id(port.first.str(ctx) + "MUX")] = port.first.str(ctx); - } else if (port.first == id_CSA0 || port.first == id_CSA1 || port.first == id_CSA2 || - port.first == id_CSB0 || port.first == id_CSB1 || port.first == id_CSB2) { + } else if (port.first.in(id_CSA0, id_CSA1, id_CSA2, id_CSB0, id_CSB1, id_CSB2)) { // CIB CE. Tie to "1" in CIB. // If MUX doesn't exist, set to INV to emulate default 0 tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true); @@ -1392,7 +1389,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex int_to_bitvector(int_or_default(ci->attrs, id_MFG_ENABLE_FILTEROPAMP, 0), 1)); cc.tilegroups.push_back(tg); - } else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) { + } else if (ci->type.in(id_IOLOGIC, id_SIOLOGIC)) { Loc pio_loc = ctx->getBelLocation(ci->bel); pio_loc.z -= ci->type == id_SIOLOGIC ? 2 : 4; std::string pic_tile = get_pic_tile(ctx, ctx->getBelByLocation(pio_loc)); diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 99f672a605..23d4b855f6 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -133,7 +133,7 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str new_cell->addInput(id_CLKI); new_cell->addOutput(id_CLKO); new_cell->addInput(id_CE); - } else if (type == id_IOLOGIC || type == id_SIOLOGIC) { + } else if (type.in(id_IOLOGIC, id_SIOLOGIC)) { new_cell->params[id_MODE] = std::string("NONE"); new_cell->params[id_GSR] = std::string("DISABLED"); new_cell->params[id_CLKIMUX] = std::string("CLK"); diff --git a/ecp5/cells.h b/ecp5/cells.h index 185b19ced3..c79bf8cd21 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -47,15 +47,13 @@ inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty inline bool is_iologic_input_cell(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_IDDRX1F || cell->type == id_IDDRX2F || cell->type == id_IDDR71B || - cell->type == id_IDDRX2DQA || + return cell->type.in(id_IDDRX1F, id_IDDRX2F, id_IDDR71B, id_IDDRX2DQA) || (cell->type == id_TRELLIS_FF && bool_or_default(cell->attrs, id_syn_useioff) && (str_or_default(cell->attrs, id_ioff_dir, "") != "output")); } inline bool is_iologic_output_cell(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_ODDRX1F || cell->type == id_ODDRX2F || cell->type == id_ODDR71B || - cell->type == id_ODDRX2DQA || cell->type == id_ODDRX2DQSB || cell->type == id_OSHX2A || + return cell->type.in(id_ODDRX1F, id_ODDRX2F, id_ODDR71B, id_ODDRX2DQA, id_ODDRX2DQSB, id_OSHX2A) || (cell->type == id_TRELLIS_FF && bool_or_default(cell->attrs, id_syn_useioff) && (str_or_default(cell->attrs, id_ioff_dir, "") != "input")); } diff --git a/ecp5/gfx.cc b/ecp5/gfx.cc index fe206de02b..b1f3d4dd7a 100644 --- a/ecp5/gfx.cc +++ b/ecp5/gfx.cc @@ -84,8 +84,7 @@ void gfxTileBel(std::vector &g, int x, int y, int z, int w, int 3 * slice_pitch - 0.0007f; el.y2 = el.y1 + wire_distance * 5; g.push_back(el); - } else if (bel_type == id_TRELLIS_IO || bel_type == id_IOLOGIC || bel_type == id_SIOLOGIC || - bel_type == id_DQSBUFM) { + } else if (bel_type.in(id_TRELLIS_IO, id_IOLOGIC, id_SIOLOGIC, id_DQSBUFM)) { bool top_bottom = (y == 0 || y == (h - 1)); if (top_bottom) { el.x1 = x + io_cell_h_x1 + (z + 2) * io_cell_gap; @@ -115,7 +114,7 @@ void gfxTileBel(std::vector &g, int x, int y, int z, int w, int el.x2 = x + switchbox_x1 + (z)*0.025 + 0.020; el.y2 = y + 0.18; g.push_back(el); - } else if (bel_type == id_DP16KD || bel_type == id_MULT18X18D || bel_type == id_ALU54B) { + } else if (bel_type.in(id_DP16KD, id_MULT18X18D, id_ALU54B)) { el.x1 = x + slice_x1; el.x2 = x + slice_x2_wide; el.y1 = y + slice_y1 - 1 * slice_pitch; @@ -133,8 +132,7 @@ void gfxTileBel(std::vector &g, int x, int y, int z, int w, int el.y1 = y + slice_y2; el.y2 = y + 0.25; g.push_back(el); - } else if (bel_type == id_EXTREFB || bel_type == id_PCSCLKDIV || bel_type == id_DTR || bel_type == id_USRMCLK || - bel_type == id_SEDGA || bel_type == id_GSR || bel_type == id_JTAGG || bel_type == id_OSCG) { + } else if (bel_type.in(id_EXTREFB, id_PCSCLKDIV, id_DTR, id_USRMCLK, id_SEDGA, id_GSR, id_JTAGG, id_OSCG)) { el.x1 = x + slice_x1; el.x2 = x + slice_x2_wide; el.y1 = y + slice_y1 + (z)*slice_pitch; @@ -146,8 +144,7 @@ void gfxTileBel(std::vector &g, int x, int y, int z, int w, int el.y1 = y + dll_cell_y1; el.y2 = y + dll_cell_y2; g.push_back(el); - } else if (bel_type == id_DLLDELD || bel_type == id_CLKDIVF || bel_type == id_ECLKSYNCB || - bel_type == id_TRELLIS_ECLKBUF || bel_type == id_ECLKBRIDGECS) { + } else if (bel_type.in(id_DLLDELD, id_CLKDIVF, id_ECLKSYNCB, id_TRELLIS_ECLKBUF, id_ECLKBRIDGECS)) { el.x1 = x + 0.1 + z * 0.05; el.x2 = x + 0.14 + z * 0.05; el.y1 = y + 0.475; @@ -1455,15 +1452,13 @@ void gfxTilePip(std::vector &g, int x, int y, int w, int h, Wire dst_id - TILE_WIRE_JCE0); } - if ((src_type == id_WIRE_TYPE_H02 || src_type == id_WIRE_TYPE_V00 || src_type == id_WIRE_TYPE_V01 || - src_type == id_WIRE_TYPE_V02) && + if ((src_type.in(id_WIRE_TYPE_H02, id_WIRE_TYPE_V00, id_WIRE_TYPE_V01, id_WIRE_TYPE_V02)) && dst_type == id_WIRE_TYPE_NONE && ((dst_id >= TILE_WIRE_FCO && dst_id <= TILE_WIRE_FCI) || (dst_id >= TILE_WIRE_JCE0 && dst_id <= TILE_WIRE_JQ7))) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } - if ((dst_type == id_WIRE_TYPE_H02 || dst_type == id_WIRE_TYPE_V00 || dst_type == id_WIRE_TYPE_V01 || - dst_type == id_WIRE_TYPE_V02) && + if ((dst_type.in(id_WIRE_TYPE_H02, id_WIRE_TYPE_V00, id_WIRE_TYPE_V01, id_WIRE_TYPE_V02)) && src_type == id_WIRE_TYPE_NONE && ((src_id >= TILE_WIRE_FCO && src_id <= TILE_WIRE_FCI) || (src_id >= TILE_WIRE_JCE0 && src_id <= TILE_WIRE_JQ7))) { @@ -1480,33 +1475,27 @@ void gfxTilePip(std::vector &g, int x, int y, int w, int h, Wire } if (src_type == id_WIRE_TYPE_NONE && - (dst_type == id_WIRE_TYPE_PLL || dst_type == id_WIRE_TYPE_GSR || dst_type == id_WIRE_TYPE_JTAG || - dst_type == id_WIRE_TYPE_OSC || dst_type == id_WIRE_TYPE_SED || dst_type == id_WIRE_TYPE_DTR || - dst_type == id_WIRE_TYPE_EXTREF || dst_type == id_WIRE_TYPE_DCU || dst_type == id_WIRE_TYPE_PCSCLKDIV || - dst_type == id_WIRE_TYPE_DDRDLL || dst_type == id_WIRE_TYPE_CCLK || dst_type == id_WIRE_TYPE_DQS || - dst_type == id_WIRE_TYPE_IOLOGIC || dst_type == id_WIRE_TYPE_SIOLOGIC || dst_type == id_WIRE_TYPE_EBR || - dst_type == id_WIRE_TYPE_MULT18 || dst_type == id_WIRE_TYPE_ALU54) && + (dst_type.in(id_WIRE_TYPE_PLL, id_WIRE_TYPE_GSR, id_WIRE_TYPE_JTAG, id_WIRE_TYPE_OSC, id_WIRE_TYPE_SED, + id_WIRE_TYPE_DTR, id_WIRE_TYPE_EXTREF, id_WIRE_TYPE_DCU, id_WIRE_TYPE_PCSCLKDIV, + id_WIRE_TYPE_DDRDLL, id_WIRE_TYPE_CCLK, id_WIRE_TYPE_DQS, id_WIRE_TYPE_IOLOGIC, + id_WIRE_TYPE_SIOLOGIC, id_WIRE_TYPE_EBR, id_WIRE_TYPE_MULT18, id_WIRE_TYPE_ALU54)) && (src_id >= TILE_WIRE_JCE0 && src_id <= TILE_WIRE_JQ7)) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } if (dst_type == id_WIRE_TYPE_NONE && - (src_type == id_WIRE_TYPE_PLL || src_type == id_WIRE_TYPE_GSR || src_type == id_WIRE_TYPE_JTAG || - src_type == id_WIRE_TYPE_OSC || src_type == id_WIRE_TYPE_SED || src_type == id_WIRE_TYPE_DTR || - src_type == id_WIRE_TYPE_EXTREF || src_type == id_WIRE_TYPE_DCU || src_type == id_WIRE_TYPE_PCSCLKDIV || - src_type == id_WIRE_TYPE_DDRDLL || src_type == id_WIRE_TYPE_CCLK || src_type == id_WIRE_TYPE_DQS || - src_type == id_WIRE_TYPE_IOLOGIC || src_type == id_WIRE_TYPE_SIOLOGIC || src_type == id_WIRE_TYPE_EBR || - src_type == id_WIRE_TYPE_MULT18 || src_type == id_WIRE_TYPE_ALU54) && + (src_type.in(id_WIRE_TYPE_PLL, id_WIRE_TYPE_GSR, id_WIRE_TYPE_JTAG, id_WIRE_TYPE_OSC, id_WIRE_TYPE_SED, + id_WIRE_TYPE_DTR, id_WIRE_TYPE_EXTREF, id_WIRE_TYPE_DCU, id_WIRE_TYPE_PCSCLKDIV, + id_WIRE_TYPE_DDRDLL, id_WIRE_TYPE_CCLK, id_WIRE_TYPE_DQS, id_WIRE_TYPE_IOLOGIC, + id_WIRE_TYPE_SIOLOGIC, id_WIRE_TYPE_EBR, id_WIRE_TYPE_MULT18, id_WIRE_TYPE_ALU54)) && (dst_id >= TILE_WIRE_JCE0 && dst_id <= TILE_WIRE_JQ7)) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } - if (src_type == id_WIRE_TYPE_NONE && - (dst_type == id_WIRE_TYPE_IOLOGIC || dst_type == id_WIRE_TYPE_SIOLOGIC || dst_type == id_WIRE_TYPE_PIO) && + if (src_type == id_WIRE_TYPE_NONE && (dst_type.in(id_WIRE_TYPE_IOLOGIC, id_WIRE_TYPE_SIOLOGIC, id_WIRE_TYPE_PIO)) && (src_id >= TILE_WIRE_JDIA && src_id <= TILE_WIRE_ECLKD)) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } - if (dst_type == id_WIRE_TYPE_NONE && - (src_type == id_WIRE_TYPE_IOLOGIC || src_type == id_WIRE_TYPE_SIOLOGIC || src_type == id_WIRE_TYPE_PIO) && + if (dst_type == id_WIRE_TYPE_NONE && (src_type.in(id_WIRE_TYPE_IOLOGIC, id_WIRE_TYPE_SIOLOGIC, id_WIRE_TYPE_PIO)) && (dst_id >= TILE_WIRE_JDIA && dst_id <= TILE_WIRE_ECLKD)) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } @@ -1526,7 +1515,7 @@ void gfxTilePip(std::vector &g, int x, int y, int w, int h, Wire (dst_id >= TILE_WIRE_CLK0 && dst_id <= TILE_WIRE_FCI))) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } - if ((dst_type == id_WIRE_TYPE_H01 || dst_type == id_WIRE_TYPE_V01) && src_type == id_WIRE_TYPE_G_HPBX) { + if ((dst_type.in(id_WIRE_TYPE_H01, id_WIRE_TYPE_V01)) && src_type == id_WIRE_TYPE_G_HPBX) { straightLine(g, el, x, y, w, h, src, src_type, src_id, dst, dst_type, dst_id); } } diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 7123705a63..6c7ffba07d 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -57,10 +57,10 @@ class Ecp5GlobalRouter return true; if (user.cell->type == id_TRELLIS_COMB && user.port == id_WCK) return true; - if (user.cell->type == id_DCUA && (user.port == id_CH0_FF_RXI_CLK || user.port == id_CH1_FF_RXI_CLK || - user.port == id_CH0_FF_TXI_CLK || user.port == id_CH1_FF_TXI_CLK)) + if (user.cell->type == id_DCUA && + (user.port.in(id_CH0_FF_RXI_CLK, id_CH1_FF_RXI_CLK, id_CH0_FF_TXI_CLK, id_CH1_FF_TXI_CLK))) return true; - if ((user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) && (user.port == id_CLK)) + if ((user.cell->type.in(id_IOLOGIC, id_SIOLOGIC)) && (user.port == id_CLK)) return true; return false; } @@ -88,7 +88,7 @@ class Ecp5GlobalRouter clockCount[ni->name]++; if (user.cell->type == id_DCUA) clockCount[ni->name] += 100; - if (user.cell->type == id_IOLOGIC || user.cell->type == id_SIOLOGIC) + if (user.cell->type.in(id_IOLOGIC, id_SIOLOGIC)) clockCount[ni->name] += 10; } } @@ -282,7 +282,7 @@ class Ecp5GlobalRouter bool route_onto_global(NetInfo *net, int network) { WireId glb_src; - NPNR_ASSERT(net->driver.cell->type == id_DCCA || net->driver.cell->type == id_DCSC); + NPNR_ASSERT(net->driver.cell->type.in(id_DCCA, id_DCSC)); glb_src = ctx->getNetinfoSourceWire(net); for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) { WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network); @@ -454,7 +454,7 @@ class Ecp5GlobalRouter { NetInfo *glbptr = nullptr; CellInfo *dccptr = nullptr; - if (net->driver.cell != nullptr && (net->driver.cell->type == id_DCCA || net->driver.cell->type == id_DCSC)) { + if (net->driver.cell != nullptr && (net->driver.cell->type.in(id_DCCA, id_DCSC))) { // Already have a DCC (such as clock gating) glbptr = net; dccptr = net->driver.cell; @@ -502,7 +502,7 @@ class Ecp5GlobalRouter int global_route_priority(const PortRef &load) { - if (load.port == id_WCK || load.port == id_WRE) + if (load.port.in(id_WCK, id_WRE)) return 90; return 99; } @@ -555,7 +555,7 @@ class Ecp5GlobalRouter dict clocks; for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_DCCA || ci->type == id_DCSC) { + if (ci->type.in(id_DCCA, id_DCSC)) { NetInfo *clock = ci->ports.at((ci->type == id_DCSC) ? id_DCSOUT : id_CLKO).net; NPNR_ASSERT(clock != nullptr); bool drives_fabric = false; @@ -591,7 +591,7 @@ class Ecp5GlobalRouter return global_route_priority(*a.first) < global_route_priority(*b.first); }); for (const auto &user : toroute) { - if (user.first->cell->type == id_DCSC && (user.first->port == id_CLK0 || user.first->port == id_CLK1)) { + if (user.first->cell->type == id_DCSC && (user.first->port.in(id_CLK0, id_CLK1))) { // Special case, skips most of the typical global network NetInfo *net = clocks.at(user.second); simple_router(net, ctx->getNetinfoSourceWire(net), ctx->getNetinfoSinkWire(net, *(user.first), 0)); @@ -606,9 +606,9 @@ class Ecp5GlobalRouter // Try and use dedicated paths if possible for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_ECLKSYNCB || ci->type == id_TRELLIS_ECLKBUF || ci->type == id_ECLKBRIDGECS) { + if (ci->type.in(id_ECLKSYNCB, id_TRELLIS_ECLKBUF, id_ECLKBRIDGECS)) { std::vector pins; - if (ci->type == id_ECLKSYNCB || ci->type == id_TRELLIS_ECLKBUF) { + if (ci->type.in(id_ECLKSYNCB, id_TRELLIS_ECLKBUF)) { pins.push_back(id_ECLKI); } else { pins.push_back(id_CLK0); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 4cd33deee3..4c8ceee83f 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -266,11 +266,10 @@ class Ecp5Packer if (port.cell == nullptr) return false; if (port.cell->type == id_DCUA) { - return port.port == id_CH0_HDINP || port.port == id_CH0_HDINN || port.port == id_CH0_HDOUTP || - port.port == id_CH0_HDOUTN || port.port == id_CH1_HDINP || port.port == id_CH1_HDINN || - port.port == id_CH1_HDOUTP || port.port == id_CH1_HDOUTN; + return port.port.in(id_CH0_HDINP, id_CH0_HDINN, id_CH0_HDOUTP, id_CH0_HDOUTN, id_CH1_HDINP, id_CH1_HDINN, + id_CH1_HDOUTP, id_CH1_HDOUTN); } else if (port.cell->type == id_EXTREFB) { - return port.port == id_REFCLKP || port.port == id_REFCLKN; + return port.port.in(id_REFCLKP, id_REFCLKN); } else { return false; } @@ -905,13 +904,11 @@ class Ecp5Packer uc->params[id_CEMUX] = std::string(constval ? "1" : "0"); uc->ports[user.port].net = nullptr; } else if (is_carry(ctx, uc)) { - if (constval && - (user.port == id_A0 || user.port == id_A1 || user.port == id_B0 || user.port == id_B1 || - user.port == id_C0 || user.port == id_C1 || user.port == id_D0 || user.port == id_D1)) { + if (constval && (user.port.in(id_A0, id_A1, id_B0, id_B1, id_C0, id_C1, id_D0, id_D1))) { // Input tied high, nothing special to do (bitstream gen will auto-enable tie-high) uc->ports[user.port].net = nullptr; } else if (!constval) { - if (user.port == id_A0 || user.port == id_A1 || user.port == id_B0 || user.port == id_B1) { + if (user.port.in(id_A0, id_A1, id_B0, id_B1)) { // These inputs can be switched to tie-high without consequence set_ccu2c_input_constant(uc, user.port, constval); } else if (user.port == id_C0 && is_ccu2c_port_high(uc, id_D0)) { @@ -940,10 +937,8 @@ class Ecp5Packer (constval && str_or_default(uc->params, id_LSRMUX, "LSR") == "INV"))) { uc->ports[user.port].net = nullptr; } else if (uc->type == id_DP16KD) { - if (user.port == id_CLKA || user.port == id_CLKB || user.port == id_RSTA || user.port == id_RSTB || - user.port == id_WEA || user.port == id_WEB || user.port == id_CEA || user.port == id_CEB || - user.port == id_OCEA || user.port == id_OCEB || user.port == id_CSA0 || user.port == id_CSA1 || - user.port == id_CSA2 || user.port == id_CSB0 || user.port == id_CSB1 || user.port == id_CSB2) { + if (user.port.in(id_CLKA, id_CLKB, id_RSTA, id_RSTB, id_WEA, id_WEB, id_CEA, id_CEB, id_OCEA, + id_OCEB, id_CSA0, id_CSA1, id_CSA2, id_CSB0, id_CSB1, id_CSB2)) { // Connect to CIB CLK, LSR or CE. Default state is 1 uc->params[ctx->id(user.port.str(ctx) + "MUX")] = constval ? user.port.str(ctx) : "INV"; } else { @@ -951,7 +946,7 @@ class Ecp5Packer uc->params[ctx->id(user.port.str(ctx) + "MUX")] = std::string(constval ? "1" : "0"); } uc->ports[user.port].net = nullptr; - } else if (uc->type == id_ALU54B || uc->type == id_MULT18X18D) { + } else if (uc->type.in(id_ALU54B, id_MULT18X18D)) { if (user.port.str(ctx).substr(0, 3) == "CLK" || user.port.str(ctx).substr(0, 2) == "CE" || user.port.str(ctx).substr(0, 3) == "RST" || user.port.str(ctx).substr(0, 3) == "SRO" || user.port.str(ctx).substr(0, 3) == "SRI" || user.port.str(ctx).substr(0, 2) == "RO" || @@ -1350,7 +1345,7 @@ class Ecp5Packer if (net == nullptr || net->driver.cell == nullptr) continue; IdString ct = net->driver.cell->type; - if (ct == id_GND || ct == id_VCC) { + if (ct.in(id_GND, id_VCC)) { ci->disconnectPort(ndport); ci->ports.erase(ndport); } @@ -1442,7 +1437,7 @@ class Ecp5Packer ci->renamePort(id_USRMCLKI, id_PADDO); ci->renamePort(id_USRMCLKTS, id_PADDT); ci->renamePort(id_USRMCLKO, id_PADDI); - } else if (ci->type == id_GSR || ci->type == id_SGSR) { + } else if (ci->type.in(id_GSR, id_SGSR)) { ci->params[id_MODE] = std::string("ACTIVE_LOW"); ci->params[id_SYNCMODE] = ci->type == id_SGSR ? std::string("SYNC") : std::string("ASYNC"); ci->type = id_GSR; @@ -1894,7 +1889,7 @@ class Ecp5Packer for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_DELAYF || ci->type == id_DELAYG) { + if (ci->type.in(id_DELAYF, id_DELAYG)) { CellInfo *i_pio = net_driven_by(ctx, ci->ports.at(id_A).net, is_trellis_io, id_O); CellInfo *o_pio = net_only_drives(ctx, ci->ports.at(id_Z).net, is_trellis_io, id_I, true); CellInfo *iol = nullptr; @@ -2021,7 +2016,7 @@ class Ecp5Packer ci->movePortTo(id_D1, iol, id_TXDATA1); iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); packed_cells.insert(cell.first); - } else if (ci->type == id_ODDRX2F || ci->type == id_ODDR71B) { + } else if (ci->type.in(id_ODDRX2F, id_ODDR71B)) { CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio == nullptr) log_error("%s '%s' Q output must be connected only to a top level output\n", ci->type.c_str(ctx), @@ -2061,7 +2056,7 @@ class Ecp5Packer iol->params[id_GSR] = str_or_default(ci->params, id_GSR, "DISABLED"); pio->params[id_DATAMUX_ODDR] = std::string("IOLDO"); packed_cells.insert(cell.first); - } else if (ci->type == id_IDDRX2F || ci->type == id_IDDR71B) { + } else if (ci->type.in(id_IDDRX2F, id_IDDR71B)) { CellInfo *pio = net_driven_by(ctx, ci->ports.at(id_D).net, is_trellis_io, id_O); if (pio == nullptr || ci->ports.at(id_D).net->users.entries() > 1) log_error("%s '%s' D input must be connected only to a top level input\n", ci->type.c_str(ctx), @@ -2121,7 +2116,7 @@ class Ecp5Packer iol->params[ctx->id("MODDRX.MODE")] = std::string("MOSHX2"); pio->params[id_DATAMUX_MDDR] = std::string("IOLDO"); packed_cells.insert(cell.first); - } else if (ci->type == id_ODDRX2DQA || ci->type == id_ODDRX2DQSB) { + } else if (ci->type.in(id_ODDRX2DQA, id_ODDRX2DQSB)) { CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_I, true); if (pio == nullptr) log_error("%s '%s' Q output must be connected only to a top level output\n", ci->type.c_str(ctx), @@ -2183,7 +2178,7 @@ class Ecp5Packer process_dqs_port(ci, pio, iol, id_WRPNTR1); process_dqs_port(ci, pio, iol, id_WRPNTR0); packed_cells.insert(cell.first); - } else if (ci->type == id_TSHX2DQA || ci->type == id_TSHX2DQSA) { + } else if (ci->type.in(id_TSHX2DQA, id_TSHX2DQSA)) { CellInfo *pio = net_only_drives(ctx, ci->ports.at(id_Q).net, is_trellis_io, id_T, true); if (pio == nullptr) log_error("%s '%s' Q output must be connected only to a top level tristate\n", ci->type.c_str(ctx), @@ -2388,7 +2383,7 @@ class Ecp5Packer // Promote/route edge clocks for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_IOLOGIC || ci->type == id_DQSBUFM) { + if (ci->type.in(id_IOLOGIC, id_DQSBUFM)) { if (!ci->ports.count(id_ECLK) || ci->ports.at(id_ECLK).net == nullptr) continue; BelId bel = ctx->getBelByNameStr(str_or_default(ci->attrs, id_BEL)); @@ -2669,7 +2664,7 @@ class Ecp5Packer pool changed_cells; for (auto net : changed_nets) { for (auto &user : ctx->nets.at(net)->users) - if (user.port == id_CLKI || user.port == id_ECLKI || user.port == id_CLK0 || user.port == id_CLK1) + if (user.port.in(id_CLKI, id_ECLKI, id_CLK0, id_CLK1)) changed_cells.insert(user.cell->name); auto &drv = ctx->nets.at(net)->driver; if (iter == 1 && drv.cell != nullptr && drv.port == id_OSC) @@ -2688,7 +2683,7 @@ class Ecp5Packer else log_error("Unsupported divider ratio '%s' on CLKDIVF '%s'\n", div.c_str(), ci->name.c_str(ctx)); copy_constraint(ci, id_CLKI, id_CDIVX, ratio); - } else if (ci->type == id_ECLKSYNCB || ci->type == id_TRELLIS_ECLKBUF) { + } else if (ci->type.in(id_ECLKSYNCB, id_TRELLIS_ECLKBUF)) { copy_constraint(ci, id_ECLKI, id_ECLKO, 1); } else if (ci->type == id_ECLKBRIDGECS) { copy_constraint(ci, id_CLK0, id_ECSOUT, 1); diff --git a/gowin/cells.cc b/gowin/cells.cc index 0c027d38a4..fc5e7388c1 100644 --- a/gowin/cells.cc +++ b/gowin/cells.cc @@ -56,13 +56,12 @@ std::unique_ptr create_generic_cell(Context *ctx, IdString type, std:: new_cell->addInput(id_CLK); new_cell->addInput(id_CE); new_cell->addInput(id_LSR); - } else if (type == id_MUX2_LUT5 || type == id_MUX2_LUT6 || type == id_MUX2_LUT7 || type == id_MUX2_LUT7 || - type == id_MUX2_LUT8) { + } else if (type.in(id_MUX2_LUT5, id_MUX2_LUT6, id_MUX2_LUT7, id_MUX2_LUT7, id_MUX2_LUT8)) { new_cell->addInput(id_I0); new_cell->addInput(id_I1); new_cell->addInput(id_SEL); new_cell->addOutput(id_OF); - } else if (type == id_IOB || type == id_IOBS) { + } else if (type.in(id_IOB, id_IOBS)) { new_cell->params[id_INPUT_USED] = 0; new_cell->params[id_OUTPUT_USED] = 0; new_cell->params[id_ENABLE_USED] = 0; diff --git a/gowin/gfx.cc b/gowin/gfx.cc index 9c9bc3fc02..048ae62efa 100644 --- a/gowin/gfx.cc +++ b/gowin/gfx.cc @@ -4969,7 +4969,7 @@ void gfxSetPipDefaultDecal(Arch *arch, PipInfo &pip) // create if absent if (arch->decal_graphics.count(active_id) == 0) { // clock? - if (dst_loc_id == id_GT00 || dst_loc_id == id_GT10) { + if (dst_loc_id.in(id_GT00, id_GT10)) { WireInfo &wi = arch->wire_info(pip.srcWire); if (wi.type.str(arch).substr(0, 3) != "UNK") { // create pip diff --git a/gowin/globals.cc b/gowin/globals.cc index 1794dd4c39..21ee722ab8 100644 --- a/gowin/globals.cc +++ b/gowin/globals.cc @@ -32,8 +32,7 @@ NEXTPNR_NAMESPACE_BEGIN bool GowinGlobalRouter::is_clock_port(PortRef const &user) { - if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && - user.port == id_CLK) { + if ((user.cell->type.in(id_SLICE, id_ODDR, id_ODDRC)) && user.port == id_CLK) { return true; } return false; diff --git a/ice40/arch.cc b/ice40/arch.cc index 905b8d615e..c9c99d3aa4 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -643,7 +643,7 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay { const auto &driver = net_info->driver; if (driver.port == id_COUT) { - NPNR_ASSERT(sink.port == id_CIN || sink.port == id_I3); + NPNR_ASSERT(sink.port.in(id_CIN, id_I3)); NPNR_ASSERT(driver.cell->constr_abs_z); bool cin = sink.port == id_CIN; bool same_y = driver.cell->constr_z < 7; @@ -904,7 +904,7 @@ std::vector Arch::getDecalGraphics(DecalId decal) const ret.push_back(el); } - if (bel_type == id_ICESTORM_PLL || bel_type == id_SB_WARMBOOT) { + if (bel_type.in(id_ICESTORM_PLL, id_SB_WARMBOOT)) { GraphicElement el; el.type = GraphicElement::TYPE_BOX; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; @@ -937,7 +937,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort if (fromPort == id_I3 && ((cell->lcInfo.lutInputMask & 0x8U) == 0)) return false; } - } else if (cell->type == id_ICESTORM_RAM || cell->type == id_ICESTORM_SPRAM) { + } else if (cell->type.in(id_ICESTORM_RAM, id_ICESTORM_SPRAM)) { return false; } return get_cell_delay_internal(cell, fromPort, toPort, delay); @@ -971,7 +971,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; if (port == id_CIN) return TMG_COMB_INPUT; - if (port == id_COUT || port == id_LO) + if (port.in(id_COUT, id_LO)) return TMG_COMB_OUTPUT; if (port == id_O) { // LCs with no inputs are constant drivers @@ -991,7 +991,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } } else if (cell->type == id_ICESTORM_RAM) { - if (port == id_RCLK || port == id_WCLK) + if (port.in(id_RCLK, id_WCLK)) return TMG_CLOCK_INPUT; clockInfoCount = 1; @@ -1000,8 +1000,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_REGISTER_OUTPUT; else return TMG_REGISTER_INPUT; - } else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) { - if (port == id_CLK || port == id_CLOCK) + } else if (cell->type.in(id_ICESTORM_DSP, id_ICESTORM_SPRAM)) { + if (port.in(id_CLK, id_CLOCK)) return TMG_CLOCK_INPUT; else { clockInfoCount = 1; @@ -1011,7 +1011,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_REGISTER_INPUT; } } else if (cell->type == id_SB_IO) { - if (port == id_INPUT_CLK || port == id_OUTPUT_CLK) + if (port.in(id_INPUT_CLK, id_OUTPUT_CLK)) return TMG_CLOCK_INPUT; if (port == id_CLOCK_ENABLE) { clockInfoCount = 2; @@ -1023,7 +1023,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (port == id_D_IN_0) { return TMG_STARTPOINT; } - if (port == id_D_OUT_0 || port == id_D_OUT_1) { + if (port.in(id_D_OUT_0, id_D_OUT_1)) { if ((cell->ioInfo.pintype & 0xC) == 0x8) { return TMG_ENDPOINT; } else { @@ -1041,7 +1041,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_IGNORE; } else if (cell->type == id_ICESTORM_PLL) { - if (port == id_PLLOUT_A || port == id_PLLOUT_B || port == id_PLLOUT_A_GLOBAL || port == id_PLLOUT_B_GLOBAL) + if (port.in(id_PLLOUT_A, id_PLLOUT_B, id_PLLOUT_A_GLOBAL, id_PLLOUT_B_GLOBAL)) return TMG_GEN_CLOCK; return TMG_IGNORE; } else if (cell->type == id_ICESTORM_LFOSC) { @@ -1063,18 +1063,18 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_IGNORE; return TMG_ENDPOINT; } else if (cell->type == id_SB_RGB_DRV) { - if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2 || port == id_RGBPU) + if (port.in(id_RGB0, id_RGB1, id_RGB2, id_RGBPU)) return TMG_IGNORE; return TMG_ENDPOINT; } else if (cell->type == id_SB_RGBA_DRV) { - if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2) + if (port.in(id_RGB0, id_RGB1, id_RGB2)) return TMG_IGNORE; return TMG_ENDPOINT; } else if (cell->type == id_SB_LEDDA_IP) { - if (port == id_CLK || port == id_CLOCK) + if (port.in(id_CLK, id_CLOCK)) return TMG_CLOCK_INPUT; return TMG_IGNORE; - } else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) { + } else if (cell->type.in(id_SB_I2C, id_SB_SPI)) { if (port == id_SBCLKI) return TMG_CLOCK_INPUT; @@ -1098,7 +1098,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port bool has_clktoq = get_cell_delay_internal(cell, id_CLK, id_O, info.clockToQ); NPNR_ASSERT(has_clktoq); } else { - if (port == id_I0 || port == id_I1 || port == id_I2 || port == id_I3) { + if (port.in(id_I0, id_I1, id_I2, id_I3)) { DelayQuad dlut; bool has_ld = get_cell_delay_internal(cell, port, id_O, dlut); NPNR_ASSERT(has_ld); @@ -1146,7 +1146,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.edge = cell->ioInfo.negtrig ? FALLING_EDGE : RISING_EDGE; info.setup = DelayPair(io_setup); info.hold = DelayPair(0); - } else if (port == id_D_OUT_0 || port == id_OUTPUT_ENABLE) { + } else if (port.in(id_D_OUT_0, id_OUTPUT_ENABLE)) { info.clock_port = id_OUTPUT_CLK; info.edge = cell->ioInfo.negtrig ? FALLING_EDGE : RISING_EDGE; info.setup = DelayPair(io_setup); @@ -1167,7 +1167,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } else { NPNR_ASSERT_FALSE("no clock data for IO cell port"); } - } else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) { + } else if (cell->type.in(id_ICESTORM_DSP, id_ICESTORM_SPRAM)) { info.clock_port = cell->type == id_ICESTORM_SPRAM ? id_CLOCK : id_CLK; info.edge = RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { @@ -1178,7 +1178,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port info.setup = DelayPair(100); info.hold = DelayPair(0); } - } else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) { + } else if (cell->type.in(id_SB_I2C, id_SB_SPI)) { info.clock_port = id_SBCLKI; info.edge = RISING_EDGE; if (cell->ports.at(port).type == PORT_OUT) { diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc index 67ddf77763..5c3fa3c5c1 100644 --- a/ice40/arch_place.cc +++ b/ice40/arch_place.cc @@ -105,7 +105,7 @@ bool Arch::isBelLocationValid(BelId bel) const // that are a PLL clock output. auto wire = getBelPinWire(bel, id_D_IN_0); for (auto pin : getWireBelPins(wire)) { - if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) { + if (pin.pin.in(id_PLLOUT_A, id_PLLOUT_B)) { // Is there a PLL there ? const CellInfo *pll_cell = getBoundBelCell(pin.bel); if (pll_cell == nullptr) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 48fbc1320b..3e50c06585 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -640,8 +640,7 @@ void write_asc(const Context *ctx, std::ostream &out) {"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}}; configure_extra_cell(config, ctx, cell.second.get(), rgba_params, true, std::string("IpConfig.")); set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGBA_DRV_EN", true, "IpConfig."); - } else if (cell.second->type == id_SB_WARMBOOT || cell.second->type == id_ICESTORM_LFOSC || - cell.second->type == id_SB_LEDDA_IP) { + } else if (cell.second->type.in(id_SB_WARMBOOT, id_ICESTORM_LFOSC, id_SB_LEDDA_IP)) { // No config needed } else if (cell.second->type == id_SB_I2C) { bool sda_in_dly = !cell.second->attrs.count(id_SDA_INPUT_DELAYED) || diff --git a/ice40/cells.cc b/ice40/cells.cc index 9166b41b8d..fc24caaf4f 100644 --- a/ice40/cells.cc +++ b/ice40/cells.cc @@ -502,13 +502,13 @@ bool is_clock_port(const BaseCtx *ctx, const PortRef &port) if (port.cell->type == id_ICESTORM_LC) return port.port == id_CLK; if (is_ram(ctx, port.cell) || port.cell->type == id_ICESTORM_RAM) - return port.port == id_RCLK || port.port == id_WCLK || port.port == id_RCLKN || port.port == id_WCLKN; + return port.port.in(id_RCLK, id_WCLK, id_RCLKN, id_WCLKN); if (is_sb_mac16(ctx, port.cell) || port.cell->type == id_ICESTORM_DSP) return port.port == id_CLK; if (is_sb_spram(ctx, port.cell) || port.cell->type == id_ICESTORM_SPRAM) return port.port == id_CLOCK; if (is_sb_io(ctx, port.cell)) - return port.port == id_INPUT_CLK || port.port == id_OUTPUT_CLK; + return port.port.in(id_INPUT_CLK, id_OUTPUT_CLK); return false; } @@ -517,11 +517,11 @@ bool is_reset_port(const BaseCtx *ctx, const PortRef &port) if (port.cell == nullptr) return false; if (is_ff(ctx, port.cell)) - return port.port == id_R || port.port == id_S; + return port.port.in(id_R, id_S); if (port.cell->type == id_ICESTORM_LC) return port.port == id_SR; if (is_sb_mac16(ctx, port.cell) || port.cell->type == id_ICESTORM_DSP) - return port.port == id_IRSTTOP || port.port == id_IRSTBOT || port.port == id_ORSTTOP || port.port == id_ORSTBOT; + return port.port.in(id_IRSTTOP, id_IRSTBOT, id_ORSTTOP, id_ORSTBOT); return false; } diff --git a/ice40/cells.h b/ice40/cells.h index 724500d66c..8aed649de3 100644 --- a/ice40/cells.h +++ b/ice40/cells.h @@ -35,13 +35,9 @@ inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type // Return true if a cell is a flipflop inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_SB_DFF || cell->type == id_SB_DFFE || cell->type == id_SB_DFFSR || - cell->type == id_SB_DFFR || cell->type == id_SB_DFFSS || cell->type == id_SB_DFFS || - cell->type == id_SB_DFFESR || cell->type == id_SB_DFFER || cell->type == id_SB_DFFESS || - cell->type == id_SB_DFFES || cell->type == id_SB_DFFN || cell->type == id_SB_DFFNE || - cell->type == id_SB_DFFNSR || cell->type == id_SB_DFFNR || cell->type == id_SB_DFFNSS || - cell->type == id_SB_DFFNS || cell->type == id_SB_DFFNESR || cell->type == id_SB_DFFNER || - cell->type == id_SB_DFFNESS || cell->type == id_SB_DFFNES; + return cell->type.in(id_SB_DFF, id_SB_DFFE, id_SB_DFFSR, id_SB_DFFR, id_SB_DFFSS, id_SB_DFFS, id_SB_DFFESR, + id_SB_DFFER, id_SB_DFFESS, id_SB_DFFES, id_SB_DFFN, id_SB_DFFNE, id_SB_DFFNSR, id_SB_DFFNR, + id_SB_DFFNSS, id_SB_DFFNS, id_SB_DFFNESR, id_SB_DFFNER, id_SB_DFFNESS, id_SB_DFFNES); } inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_CARRY; } @@ -60,8 +56,7 @@ inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->typ // Return true if a cell is a RAM inline bool is_ram(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_SB_RAM40_4K || cell->type == id_SB_RAM40_4KNR || cell->type == id_SB_RAM40_4KNW || - cell->type == id_SB_RAM40_4KNRNW; + return cell->type.in(id_SB_RAM40_4K, id_SB_RAM40_4KNR, id_SB_RAM40_4KNW, id_SB_RAM40_4KNRNW); } inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SB_LFOSC; } @@ -86,13 +81,12 @@ inline bool is_sb_spi(const BaseCtx *ctx, const CellInfo *cell) { return cell->t inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_SB_PLL40_PAD || cell->type == id_SB_PLL40_2_PAD || cell->type == id_SB_PLL40_2F_PAD || - cell->type == id_SB_PLL40_CORE || cell->type == id_SB_PLL40_2F_CORE; + return cell->type.in(id_SB_PLL40_PAD, id_SB_PLL40_2_PAD, id_SB_PLL40_2F_PAD, id_SB_PLL40_CORE, id_SB_PLL40_2F_CORE); } inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_SB_PLL40_PAD || cell->type == id_SB_PLL40_2_PAD || cell->type == id_SB_PLL40_2F_PAD || + return cell->type.in(id_SB_PLL40_PAD, id_SB_PLL40_2_PAD, id_SB_PLL40_2F_PAD) || (cell->type == id_ICESTORM_PLL && (cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_PAD" || cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2_PAD" || cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2F_PAD")); @@ -100,7 +94,7 @@ inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell) inline bool is_sb_pll40_dual(const BaseCtx *ctx, const CellInfo *cell) { - return cell->type == id_SB_PLL40_2_PAD || cell->type == id_SB_PLL40_2F_PAD || cell->type == id_SB_PLL40_2F_CORE || + return cell->type.in(id_SB_PLL40_2_PAD, id_SB_PLL40_2F_PAD, id_SB_PLL40_2F_CORE) || (cell->type == id_ICESTORM_PLL && (cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2_PAD" || cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2F_PAD" || cell->attrs.at(id_TYPE).as_string() == "SB_PLL40_2F_CORE")); diff --git a/ice40/pack.cc b/ice40/pack.cc index 9eff053e65..c981172175 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -345,8 +345,8 @@ static void pack_ram(Context *ctx) packed->attrs[attr.first] = attr.second; for (auto param : ci->params) packed->params[param.first] = param.second; - packed->params[id_NEG_CLK_W] = Property(ci->type == id_SB_RAM40_4KNW || ci->type == id_SB_RAM40_4KNRNW, 1); - packed->params[id_NEG_CLK_R] = Property(ci->type == id_SB_RAM40_4KNR || ci->type == id_SB_RAM40_4KNRNW, 1); + packed->params[id_NEG_CLK_W] = Property(ci->type.in(id_SB_RAM40_4KNW, id_SB_RAM40_4KNRNW), 1); + packed->params[id_NEG_CLK_R] = Property(ci->type.in(id_SB_RAM40_4KNR, id_SB_RAM40_4KNRNW), 1); packed->type = id_ICESTORM_RAM; for (auto port : ci->ports) { PortInfo &pi = port.second; @@ -1468,11 +1468,11 @@ void pack_plls(Context *ctx) newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2); } - if (pi.name == id_PLLOUTCOREA || pi.name == id_PLLOUTCORE) + if (pi.name.in(id_PLLOUTCOREA, id_PLLOUTCORE)) newname = "PLLOUT_A"; if (pi.name == id_PLLOUTCOREB) newname = "PLLOUT_B"; - if (pi.name == id_PLLOUTGLOBALA || pi.name == id_PLLOUTGLOBAL) + if (pi.name.in(id_PLLOUTGLOBALA, id_PLLOUTGLOBAL)) newname = "PLLOUT_A_GLOBAL"; if (pi.name == id_PLLOUTGLOBALB) newname = "PLLOUT_B_GLOBAL"; diff --git a/mistral/globals.cc b/mistral/globals.cc index 6af8920d76..bdd835a7e0 100644 --- a/mistral/globals.cc +++ b/mistral/globals.cc @@ -38,10 +38,7 @@ void Arch::create_clkbuf(int x, int y) } } -bool Arch::is_clkbuf_cell(IdString cell_type) const -{ - return cell_type == id_MISTRAL_CLKENA || cell_type == id_MISTRAL_CLKBUF; -} +bool Arch::is_clkbuf_cell(IdString cell_type) const { return cell_type.in(id_MISTRAL_CLKENA, id_MISTRAL_CLKBUF); } void Arch::create_hps_mpu_general_purpose(int x, int y) { diff --git a/nexus/arch.cc b/nexus/arch.cc index 4a956fae54..c2d3b6e41c 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -479,7 +479,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } return result; } else { - if (toPort == id_F || toPort == id_OFX) + if (toPort.in(id_F, id_OFX)) return lookup_cell_delay(cell->tmg_index, fromPort, toPort, delay); } } else if (is_dsp_cell(cell)) { @@ -487,7 +487,7 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; // don't include delays that are actually clock-to-out here return lookup_cell_delay(cell->tmg_index, lookup_port(fromPort), lookup_port(toPort), delay); } else if (cell->type == id_DCS) { - if (fromPort == id_SELFORCE || fromPort == id_SEL) { + if (fromPort.in(id_SELFORCE, id_SEL)) { return false; } int index = get_cell_timing_idx(id_DCS, id_DCS); @@ -505,10 +505,9 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in }; clockInfoCount = 0; if (cell->type == id_OXIDE_COMB) { - if (port == id_A || port == id_B || port == id_C || port == id_D || port == id_SEL || port == id_F1 || - port == id_FCI || port == id_WDI) + if (port.in(id_A, id_B, id_C, id_D, id_SEL, id_F1, id_FCI, id_WDI)) return TMG_COMB_INPUT; - if (port == id_F || port == id_OFX || port == id_FCO) { + if (port.in(id_F, id_OFX, id_FCO)) { if (disconnected(id_A) && disconnected(id_B) && disconnected(id_C) && disconnected(id_D) && disconnected(id_FCI) && disconnected(id_SEL) && disconnected(id_WDI)) return TMG_IGNORE; @@ -528,18 +527,17 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in } else if (cell->type == id_RAMW) { if (port == id_CLK) return TMG_CLOCK_INPUT; - else if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3) { + else if (port.in(id_WDO0, id_WDO1, id_WDO2, id_WDO3)) { clockInfoCount = 1; return TMG_REGISTER_OUTPUT; - } else if (port == id_A0 || port == id_A1 || port == id_B0 || port == id_B1 || port == id_C0 || port == id_C1 || - port == id_D0 || port == id_D1) { + } else if (port.in(id_A0, id_A1, id_B0, id_B1, id_C0, id_C1, id_D0, id_D1)) { clockInfoCount = 1; return TMG_REGISTER_INPUT; } } else if (cell->type == id_OXIDE_EBR) { - if (port == id_DWS0 || port == id_DWS1 || port == id_DWS2 || port == id_DWS3 || port == id_DWS4) + if (port.in(id_DWS0, id_DWS1, id_DWS2, id_DWS3, id_DWS4)) return TMG_IGNORE; - if (port == id_CLKA || port == id_CLKB) + if (port.in(id_CLKA, id_CLKB)) return TMG_CLOCK_INPUT; clockInfoCount = 1; return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT; @@ -551,9 +549,9 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return TMG_CLOCK_INPUT; clockInfoCount = 1; return (cell->ports.at(port).type == PORT_IN) ? TMG_REGISTER_INPUT : TMG_REGISTER_OUTPUT; - } else if (cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE) { + } else if (cell->type.in(id_MULT18_CORE, id_MULT18X36_CORE, id_MULT36_CORE)) { return (cell->ports.at(port).type == PORT_IN) ? TMG_COMB_INPUT : TMG_COMB_OUTPUT; - } else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) { + } else if (cell->type.in(id_PREADD9_CORE, id_REG18_CORE, id_MULT9_CORE)) { if (port == id_CLK) return TMG_CLOCK_INPUT; auto type = lookup_port_type(cell->tmg_index, lookup_port(port), cell->ports.at(port).type, id_CLK); @@ -571,7 +569,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in // FIXME: Making inputs TMG_CLOCK_INPUT and the output TMG_CLOCK_GEN // yielded in error in the timing analyzer. For now keep those as // regular combinational ports. - if (port == id_CLK0 || port == id_CLK1) + if (port.in(id_CLK0, id_CLK1)) return TMG_COMB_INPUT; else if (port == id_DCSOUT) { return TMG_COMB_OUTPUT; @@ -598,7 +596,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port } else if (cell->type == id_RAMW) { info.edge = (cell->ffInfo.ctrlset.clkmux == ID_INV) ? FALLING_EDGE : RISING_EDGE; info.clock_port = id_CLK; - if (port == id_WDO0 || port == id_WDO1 || port == id_WDO2 || port == id_WDO3) + if (port.in(id_WDO0, id_WDO1, id_WDO2, id_WDO3)) NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, port, info.clockToQ)); else lookup_cell_setuphold(cell->tmg_index, port, id_CLK, info.setup, info.hold); @@ -618,7 +616,7 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port NPNR_ASSERT(lookup_cell_delay(cell->tmg_index, id_CLK, lookup_port(port), info.clockToQ)); } info.edge = (get_cell_pinmux(cell, info.clock_port) == PINMUX_INV) ? FALLING_EDGE : RISING_EDGE; - } else if (cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE) { + } else if (cell->type.in(id_PREADD9_CORE, id_REG18_CORE, id_MULT9_CORE)) { info.clock_port = id_CLK; if (cell->ports.at(port).type == PORT_IN) { lookup_cell_setuphold(cell->tmg_index, lookup_port(port), id_CLK, info.setup, info.hold); @@ -737,9 +735,8 @@ void Arch::pre_routing() { for (auto &cell : cells) { CellInfo *ci = cell.second.get(); - if (ci->type == id_MULT9_CORE || ci->type == id_PREADD9_CORE || ci->type == id_MULT18_CORE || - ci->type == id_MULT18X36_CORE || ci->type == id_MULT36_CORE || ci->type == id_REG18_CORE || - ci->type == id_ACC54_CORE) { + if (ci->type.in(id_MULT9_CORE, id_PREADD9_CORE, id_MULT18_CORE, id_MULT18X36_CORE, id_MULT36_CORE, + id_REG18_CORE, id_ACC54_CORE)) { for (auto &port : ci->ports) { WireId wire = getBelPinWire(ci->bel, port.first); if (wire != WireId()) @@ -953,8 +950,8 @@ int db_binary_search(const Tres *list, int count, Tgetter key_getter, Tkey key) bool Arch::is_dsp_cell(const CellInfo *cell) const { - return cell->type == id_MULT18_CORE || cell->type == id_MULT18X36_CORE || cell->type == id_MULT36_CORE || - cell->type == id_PREADD9_CORE || cell->type == id_REG18_CORE || cell->type == id_MULT9_CORE; + return cell->type.in(id_MULT18_CORE, id_MULT18X36_CORE, id_MULT36_CORE, id_PREADD9_CORE, id_REG18_CORE, + id_MULT9_CORE); } int Arch::get_cell_timing_idx(IdString cell_type, IdString cell_variant) const diff --git a/nexus/arch.h b/nexus/arch.h index d713afcfe4..4a28dfb704 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -1072,7 +1072,7 @@ struct Arch : BaseArch bool getBelGlobalBuf(BelId bel) const override { IdString type = getBelType(bel); - return type == id_DCC || type == id_VCC_DRV; + return type.in(id_DCC, id_VCC_DRV); } IdString getBelType(BelId bel) const override diff --git a/nexus/fasm.cc b/nexus/fasm.cc index 48a3d2591b..81208c0f20 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -1079,9 +1079,8 @@ struct NexusFasmWriter write_osc(ci); else if (ci->type == id_OXIDE_EBR) write_bram(ci); - else if (ci->type == id_MULT9_CORE || ci->type == id_PREADD9_CORE || ci->type == id_MULT18_CORE || - ci->type == id_MULT18X36_CORE || ci->type == id_MULT36_CORE || ci->type == id_REG18_CORE || - ci->type == id_ACC54_CORE) + else if (ci->type.in(id_MULT9_CORE, id_PREADD9_CORE, id_MULT18_CORE, id_MULT18X36_CORE, id_MULT36_CORE, + id_REG18_CORE, id_ACC54_CORE)) write_dsp(ci); else if (ci->type == id_PLL_CORE) write_pll(ci); @@ -1089,7 +1088,7 @@ struct NexusFasmWriter write_lram(ci); else if (ci->type == id_DPHY_CORE) write_dphy(ci); - else if (ci->type == id_IOLOGIC || ci->type == id_SIOLOGIC) + else if (ci->type.in(id_IOLOGIC, id_SIOLOGIC)) write_iol(ci); else if (ci->type == id_DCC) write_dcc(ci); diff --git a/nexus/global.cc b/nexus/global.cc index 37629f6417..0792715dbd 100644 --- a/nexus/global.cc +++ b/nexus/global.cc @@ -193,7 +193,7 @@ struct NexusGlobalRouter CellInfo *drv = ni->driver.cell; if (drv == nullptr) continue; - if (drv->type == id_DCC || drv->type == id_DCS) { + if (drv->type.in(id_DCC, id_DCS)) { route_clk_net(ni); continue; } diff --git a/nexus/pack.cc b/nexus/pack.cc index e727364d6b..3d293f6903 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -1981,11 +1981,11 @@ struct NexusPacker pool changed_cells; for (auto net : changed_nets) { for (auto &user : ctx->nets.at(net)->users) - if (user.port == id_CLKI || user.port == id_REFCK) + if (user.port.in(id_CLKI, id_REFCK)) changed_cells.insert(user.cell->name); auto &drv = ctx->nets.at(net)->driver; if (iter == 1 && drv.cell != nullptr) { - if (drv.cell->type == id_OSC_CORE && (drv.port == id_HFCLKOUT || drv.port == id_LFCLKOUT)) + if (drv.cell->type == id_OSC_CORE && (drv.port.in(id_HFCLKOUT, id_LFCLKOUT))) changed_cells.insert(drv.cell->name); if (drv.cell->type == id_DCC && drv.port == id_CLKO) changed_cells.insert(drv.cell->name); From 47da562600315f99b4de269a056f00b9d14ed62f Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 15 Aug 2022 12:15:00 +0200 Subject: [PATCH 173/712] viaduct: Allow passing command line options to uarch with -o Signed-off-by: gatecat --- generic/main.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/generic/main.cc b/generic/main.cc index d08ae3819a..3ee1c790fa 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -49,6 +49,8 @@ po::options_description GenericCommandHandler::getArchOptions() po::options_description specific("Architecture specific options"); specific.add_options()("uarch", po::value(), uarch_help.c_str()); specific.add_options()("no-iobs", "disable automatic IO buffer insertion"); + specific.add_options()("vopt,o", po::value>(), "options to pass to the viaduct uarch"); + return specific; } @@ -68,6 +70,16 @@ std::unique_ptr GenericCommandHandler::createContext(dict(); dict args; // TODO + if (vm.count("vopt")) { + std::vector options = vm["vopt"].as>(); + for (const auto &opt : options) { + size_t epos = opt.find('='); + if (epos == std::string::npos) + args[opt] = ""; + else + args[opt.substr(0, epos)] = opt.substr(epos + 1); + } + } auto uarch = ViaductArch::create(uarch_name, args); if (!uarch) { std::string all_uarches = ViaductArch::list(); @@ -75,6 +87,8 @@ std::unique_ptr GenericCommandHandler::createContext(dictuarch = std::move(uarch); ctx->uarch->init(ctx.get()); + } else if (vm.count("vopt")) { + log_error("Viaduct options passed in non-viaduct mode!\n"); } return ctx; } From ae8966040bc7f148958d4ac0b989e04059b6ba06 Mon Sep 17 00:00:00 2001 From: LAK132 Date: Tue, 16 Aug 2022 20:14:43 +0930 Subject: [PATCH 174/712] Replace deprecated method of finding Python 3 --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3812f7dcc9..269e90eb5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,10 +173,10 @@ if (BUILD_GUI) set(CMAKE_ENABLE_EXPORTS ON) endif() -find_package(PythonInterp 3.5 REQUIRED) +find_package(Python3 3.5 REQUIRED COMPONENTS Interpreter) if (BUILD_PYTHON) # TODO: sensible minimum Python version - find_package(PythonLibs 3.5 REQUIRED) + find_package(Python3 3.5 REQUIRED COMPONENTS Development) else() add_definitions("-DNO_PYTHON") endif() @@ -221,7 +221,7 @@ if (NOT DEFINED PYBIND11_INCLUDE_DIR) set(PYBIND11_INCLUDE_DIR "3rdparty/pybind11/include") endif() -include_directories(common/kernel/ common/place/ common/route/ json/ frontend/ 3rdparty/json11/ ${PYBIND11_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) +include_directories(common/kernel/ common/place/ common/route/ json/ frontend/ 3rdparty/json11/ ${PYBIND11_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}) if(BUILD_HEAP) find_package (Eigen3 REQUIRED NO_MODULE) @@ -339,7 +339,7 @@ foreach (family ${ARCH}) target_link_libraries(${target} LINK_PUBLIC gui_${family} ${GUI_LIBRARY_FILES_${ufamily}}) endif() if (BUILD_PYTHON) - target_link_libraries(${target} LINK_PUBLIC ${PYTHON_LIBRARIES}) + target_link_libraries(${target} LINK_PUBLIC ${Python3_LIBRARIES}) if (STATIC_BUILD) target_link_libraries(${target} LINK_PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES} ${EXPAT_LIBRARIES}) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") From 05167fcb8bf9cd85ea605331ca36cac6f25ce67f Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 18 Aug 2022 15:09:41 +0200 Subject: [PATCH 175/712] pybindings: Mark CellInfo::bel as readonly bel bindings should be updated with bindBel/unbindBel during placement, or setting the BEL attribute for constraints before placement. Fixes #522 Signed-off-by: gatecat --- common/kernel/pybindings.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/kernel/pybindings.cc b/common/kernel/pybindings.cc index 9a783eb41d..f738f081a2 100644 --- a/common/kernel/pybindings.cc +++ b/common/kernel/pybindings.cc @@ -191,8 +191,7 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m) ci_cls, "params"); readonly_wrapper>::def_wrap( ci_cls, "ports"); - readwrite_wrapper, - conv_from_str>::def_wrap(ci_cls, "bel"); + readonly_wrapper>::def_wrap(ci_cls, "bel"); readwrite_wrapper, pass_through>::def_wrap(ci_cls, "belStrength"); From 19160f10ae8916f9bb22166ce38186550c56b6d5 Mon Sep 17 00:00:00 2001 From: Adam Sampson Date: Sun, 21 Aug 2022 17:47:35 +0100 Subject: [PATCH 176/712] Use CMake's Python3 rather than PythonInterp in subdirs --- README.md | 2 +- ecp5/CMakeLists.txt | 4 ++-- fpga_interchange/examples/chipdb.cmake | 8 ++++---- fpga_interchange/examples/chipdb_xilinx.cmake | 2 +- fpga_interchange/examples/tests.cmake | 8 ++++---- ice40/CMakeLists.txt | 4 ++-- machxo2/CMakeLists.txt | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 36ff9a8b6b..957be8899a 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ sudo make install On Windows, you may specify paths explicitly: ``` -cmake . -DARCH=ice40 -DICESTORM_INSTALL_PREFIX=C:/ProgramData/icestorm -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows -G "Visual Studio 15 2017 Win64" -DPYTHON_EXECUTABLE=C:/Python364/python.exe -DPYTHON_LIBRARY=C:/vcpkg/packages/python3_x64-windows/lib/python36.lib -DPYTHON_INCLUDE_DIR=C:/vcpkg/packages/python3_x64-windows/include/python3.6 . +cmake . -DARCH=ice40 -DICESTORM_INSTALL_PREFIX=C:/ProgramData/icestorm -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows -G "Visual Studio 15 2017 Win64" -DPython3_EXECUTABLE=C:/Python364/python.exe -DPython3_LIBRARY=C:/vcpkg/packages/python3_x64-windows/lib/python36.lib -DPython3_INCLUDE_DIR=C:/vcpkg/packages/python3_x64-windows/include/python3.6 . cmake --build . --config Release ``` diff --git a/ecp5/CMakeLists.txt b/ecp5/CMakeLists.txt index bc34d51b90..115e0ce896 100644 --- a/ecp5/CMakeLists.txt +++ b/ecp5/CMakeLists.txt @@ -9,7 +9,7 @@ message(STATUS "Enabled ECP5 devices: ${ECP5_DEVICES}") if(DEFINED ECP5_CHIPDB) add_custom_target(chipdb-ecp5-bbas ALL) else() - find_package(PythonInterp 3.5 REQUIRED) + find_package(Python3 3.5 REQUIRED COMPONENTS Interpreter) # shared among all families set(SERIALIZE_CHIPDBS TRUE CACHE BOOL @@ -81,7 +81,7 @@ else() set(device_bba chipdb/chipdb-${device}.bba) add_custom_command( OUTPUT ${device_bba} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/trellis_import.py + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/trellis_import.py -L ${TRELLIS_LIBDIR} -L ${TRELLIS_DATADIR}/util/common -L ${TRELLIS_DATADIR}/timing/util diff --git a/fpga_interchange/examples/chipdb.cmake b/fpga_interchange/examples/chipdb.cmake index a060576de6..e820124baa 100644 --- a/fpga_interchange/examples/chipdb.cmake +++ b/fpga_interchange/examples/chipdb.cmake @@ -57,7 +57,7 @@ function(create_patched_device_db) add_custom_command( OUTPUT ${output_device_file} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.patch + ${Python3_EXECUTABLE} -mfpga_interchange.patch --schema_dir ${INTERCHANGE_SCHEMA_PATH} --schema device --patch_path ${patch_path} @@ -76,7 +76,7 @@ function(create_patched_device_db) add_custom_target(${patch_name}-${device}-device-yaml COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.convert + ${Python3_EXECUTABLE} -mfpga_interchange.convert --schema_dir ${INTERCHANGE_SCHEMA_PATH} --schema device --input_format capnp @@ -145,7 +145,7 @@ function(patch_device_with_prim_lib) add_custom_command( OUTPUT ${output_device_file} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.add_prim_lib + ${Python3_EXECUTABLE} -mfpga_interchange.add_prim_lib --schema_dir ${INTERCHANGE_SCHEMA_PATH} ${input_device_loc} ${output_json_file} @@ -227,7 +227,7 @@ function(generate_chipdb) add_custom_command( OUTPUT ${chipdb_bba} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.nextpnr_emit + ${Python3_EXECUTABLE} -mfpga_interchange.nextpnr_emit --schema_dir ${INTERCHANGE_SCHEMA_PATH} --output_dir ${CMAKE_CURRENT_BINARY_DIR} --device_config ${device_config} diff --git a/fpga_interchange/examples/chipdb_xilinx.cmake b/fpga_interchange/examples/chipdb_xilinx.cmake index 23b4d7f127..62e6b9eda6 100644 --- a/fpga_interchange/examples/chipdb_xilinx.cmake +++ b/fpga_interchange/examples/chipdb_xilinx.cmake @@ -53,7 +53,7 @@ function(create_rapidwright_device_db) add_custom_target(rapidwright-${device}-device-yaml COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.convert + ${Python3_EXECUTABLE} -mfpga_interchange.convert --schema_dir ${INTERCHANGE_SCHEMA_PATH} --schema device --input_format capnp diff --git a/fpga_interchange/examples/tests.cmake b/fpga_interchange/examples/tests.cmake index 48b1cee3b7..b568c5df7b 100644 --- a/fpga_interchange/examples/tests.cmake +++ b/fpga_interchange/examples/tests.cmake @@ -98,7 +98,7 @@ function(add_interchange_test) add_custom_command( OUTPUT ${netlist} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.yosys_json + ${Python3_EXECUTABLE} -mfpga_interchange.yosys_json --schema_dir ${INTERCHANGE_SCHEMA_PATH} --device ${device_loc} --top ${top} @@ -117,7 +117,7 @@ function(add_interchange_test) add_custom_command( OUTPUT ${netlist_yaml} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.convert + ${Python3_EXECUTABLE} -mfpga_interchange.convert --schema_dir ${INTERCHANGE_SCHEMA_PATH} --schema logical --input_format capnp @@ -248,7 +248,7 @@ function(add_interchange_test) add_custom_command( OUTPUT ${phys_yaml} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.convert + ${Python3_EXECUTABLE} -mfpga_interchange.convert --schema_dir ${INTERCHANGE_SCHEMA_PATH} --schema physical --input_format capnp @@ -296,7 +296,7 @@ function(add_interchange_test) add_custom_command( OUTPUT ${fasm} COMMAND - ${PYTHON_EXECUTABLE} -mfpga_interchange.fasm_generator + ${Python3_EXECUTABLE} -mfpga_interchange.fasm_generator --schema_dir ${INTERCHANGE_SCHEMA_PATH} --family ${device_family} ${device_loc} diff --git a/ice40/CMakeLists.txt b/ice40/CMakeLists.txt index 7a878bf45a..3bdfa26de2 100644 --- a/ice40/CMakeLists.txt +++ b/ice40/CMakeLists.txt @@ -9,7 +9,7 @@ message(STATUS "Enabled iCE40 devices: ${ICE40_DEVICES}") if(DEFINED ICE40_CHIPDB) add_custom_target(chipdb-ice40-bbas ALL) else() - find_package(PythonInterp 3.5 REQUIRED) + find_package(Python3 3.5 REQUIRED COMPONENTS Interpreter) # shared among all families set(SERIALIZE_CHIPDBS TRUE CACHE BOOL @@ -57,7 +57,7 @@ else() set(device_bba chipdb/chipdb-${device}.bba) add_custom_command( OUTPUT ${device_bba} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/chipdb.py + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/chipdb.py -p ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc -g ${CMAKE_CURRENT_SOURCE_DIR}/gfx.h ${timing_opts} diff --git a/machxo2/CMakeLists.txt b/machxo2/CMakeLists.txt index ab4eded6c2..f772f80124 100644 --- a/machxo2/CMakeLists.txt +++ b/machxo2/CMakeLists.txt @@ -10,7 +10,7 @@ message(STATUS "Enabled MachXO2 devices: ${MACHXO2_DEVICES}") if(DEFINED MACHXO2_CHIPDB) add_custom_target(chipdb-machxo2-bbas ALL) else() - find_package(PythonInterp 3.5 REQUIRED) + find_package(Python3 3.5 REQUIRED COMPONENTS Interpreter) # shared among all families set(SERIALIZE_CHIPDBS TRUE CACHE BOOL @@ -58,7 +58,7 @@ else() set(device_bba chipdb/chipdb-${device}.bba) add_custom_command( OUTPUT ${device_bba} - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/facade_import.py + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/facade_import.py -L ${TRELLIS_LIBDIR} -L ${TRELLIS_DATADIR}/util/common -L ${TRELLIS_DATADIR}/timing/util From 1aa797b82068a6ac0691c2e6a5e2ebe91a9a7b72 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 22 Aug 2022 12:32:50 +0200 Subject: [PATCH 177/712] Fix parameter order --- common/route/router2.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/route/router2.cc b/common/route/router2.cc index 1153f054bc..d054c9efbd 100644 --- a/common/route/router2.cc +++ b/common/route/router2.cc @@ -373,8 +373,7 @@ struct Router2 return base_cost * hist_cost * present_cost / (1 + (source_uses * crit_weight)) + bias_cost; } - float get_togo_cost(NetInfo *net, store_index user, int wire, WireId src_sink, float crit_weight, - bool bwd = false) + float get_togo_cost(NetInfo *net, store_index user, int wire, WireId src_sink, bool bwd, float crit_weight) { auto &nd = nets.at(net->udata); auto &wd = flat_wires[wire]; From a00b997cf19c0123f46f6509b2e42142e72f1d8c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 22 Aug 2022 12:35:24 +0200 Subject: [PATCH 178/712] add missing overrides --- common/kernel/nextpnr_types.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/kernel/nextpnr_types.h b/common/kernel/nextpnr_types.h index e4042aecc3..2a9062fd2a 100644 --- a/common/kernel/nextpnr_types.h +++ b/common/kernel/nextpnr_types.h @@ -205,9 +205,9 @@ struct RegionPlug : PseudoCell WireId getPortWire(IdString port) const override { return port_wires.at(port); } // TODO: partial reconfiguration region timing - bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const { return false; } - TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const { return TMG_IGNORE; } - virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const { return TimingClockingInfo{}; } + bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const override { return false; } + TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const override { return TMG_IGNORE; } + TimingClockingInfo getPortClockingInfo(IdString port, int index) const override { return TimingClockingInfo{}; } dict port_wires; Loc loc; From e0539f0ed77b0dd9400f3947fcd2dbf1b5b78ae0 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 25 Aug 2022 11:52:29 +1000 Subject: [PATCH 179/712] gowin: BUGFIX. Really memorize the chip When it really needed to distinguish between the chips, this unforgivable error was discovered :) Signed-off-by: YRabbit --- gowin/arch.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index b37d444cee..e33a904ac1 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -1168,6 +1168,8 @@ Arch::Arch(ArchArgs args) : args(args) log_info("Series:%s Device:%s Package:%s Speed:%s\n", family.c_str(), device_id.c_str(this), package_name.c_str(this), speed_id.c_str(this)); + device = device_id.str(this); + // setup db // add global VCC and GND bels addBel(id_GND, id_GND, Loc(0, 0, BelZ::gnd_0_z), true); From 8b6be098094c6de151199a33690d6e8ecacdd446 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 30 Aug 2022 17:30:13 +0200 Subject: [PATCH 180/712] Fixed port timing classes of DCC ports in the Nexus architecture Signed-off-by: Maciej Kurc --- nexus/arch.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/nexus/arch.cc b/nexus/arch.cc index c2d3b6e41c..70e3d38de3 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -492,6 +492,15 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } int index = get_cell_timing_idx(id_DCS, id_DCS); return lookup_cell_delay(index, fromPort, toPort, delay); + } else if (cell->type == id_DCC) { + if (fromPort == id_CLKI && toPort == id_CLKO) { + // TODO: Use actual DCC delays + delay.rise.min_delay = 1000; + delay.rise.max_delay = 1000; + delay.fall.min_delay = 1000; + delay.fall.max_delay = 1000; + return true; + } } return false; } @@ -560,11 +569,9 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in return type; } else if (cell->type == id_DCC) { if (port == id_CLKI) - return TMG_CLOCK_INPUT; - else if (port == id_CLKO) - return TMG_GEN_CLOCK; - else if (port == id_CE) return TMG_COMB_INPUT; + else if (port == id_CLKO) + return TMG_COMB_OUTPUT; } else if (cell->type == id_DCS) { // FIXME: Making inputs TMG_CLOCK_INPUT and the output TMG_CLOCK_GEN // yielded in error in the timing analyzer. For now keep those as From 9a61ad923444bbf01f777bd76b4b2e74e4e5c503 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Tue, 30 Aug 2022 17:30:58 +0200 Subject: [PATCH 181/712] Augmented TimingAnalyser class with detection of clock to clock relations Signed-off-by: Maciej Kurc --- common/kernel/timing.cc | 226 ++++++++++++++++++++++++++++++++++++++-- common/kernel/timing.h | 6 ++ 2 files changed, 225 insertions(+), 7 deletions(-) diff --git a/common/kernel/timing.cc b/common/kernel/timing.cc index 834785fbe3..370b43c65b 100644 --- a/common/kernel/timing.cc +++ b/common/kernel/timing.cc @@ -29,12 +29,17 @@ NEXTPNR_NAMESPACE_BEGIN +namespace { +const char *edge_name(ClockEdge edge) { return (edge == FALLING_EDGE) ? "negedge" : "posedge"; } +} // namespace + void TimingAnalyser::setup() { init_ports(); get_cell_delays(); topo_sort(); setup_port_domains(); + identify_related_domains(); run(); } @@ -249,6 +254,7 @@ void TimingAnalyser::setup_port_domains() copy_domains(port, CellPortKey(pi.net->driver), true); } } + // Iterate over ports and find domain paris for (auto port : topological_order) { auto &pd = ports.at(port); @@ -280,6 +286,174 @@ void TimingAnalyser::setup_port_domains() } } +void TimingAnalyser::identify_related_domains() { + + // Identify clock nets + pool clock_nets; + for (const auto& domain : domains) { + clock_nets.insert(domain.key.clock); + } + + // For each clock net identify all nets that can possibly drive it. Compute + // cumulative delays to each of them. + std::function&, delay_t, int32_t)> find_net_drivers = + [&] (const NetInfo* ni, dict& drivers, delay_t delay_acc, int32_t level) + { + // Get driving cell and port + const CellInfo* cell = ni->driver.cell; + const IdString port = ni->driver.port; + + bool didGoUpstream = false; + + std::string indent (level, ' '); + + log("%sout %s.%s\n", indent.c_str(), cell->name.str(ctx).c_str(), port.str(ctx).c_str()); + + // The cell has only one port + if (cell->ports.size() == 1) { + drivers[ni->name] = delay_acc; + return; + } + + // Count cell port types + size_t num_inp = 0; + size_t num_out = 0; + for (const auto& it : cell->ports) { + const auto& pi = it.second; + if (pi.type != PORT_IN) { + num_inp++; + } else { + num_out++; + } + } + + // Get the driver timing class + int info_count = 0; + auto timing_class = ctx->getPortTimingClass(cell, port, info_count); + + // The driver must be a combinational output + if (timing_class != TMG_COMB_OUTPUT) { + drivers[ni->name] = delay_acc; + return; + } + + // Recurse upstream through all input ports that have combinational + // paths to this driver + for (const auto& it : cell->ports) { + const auto& pi = it.second; + + // Only connected inputs + if (pi.type != PORT_IN) { + continue; + } + if (pi.net == nullptr) { + continue; + } + + // The input must be a combinational input + timing_class = ctx->getPortTimingClass(cell, pi.name, info_count); + if (timing_class != TMG_COMB_INPUT) { + continue; + } + // There must be a combinational arc + DelayQuad delay; + if (!ctx->getCellDelay(cell, pi.name, port, delay)) { + continue; + } + + log("%sinp %s.%s\n", indent.c_str(), cell->name.str(ctx).c_str(), pi.name.str(ctx).c_str()); + + // Recurse + find_net_drivers(pi.net, drivers, delay_acc + delay.maxDelay(), level+1); + didGoUpstream = true; + } + + // Did not propagate upstream through the cell, mark the net as driver + if (!didGoUpstream) { + log("%send %s\n", indent.c_str(), ni->name.str(ctx).c_str()); + drivers[ni->name] = delay_acc; + } + }; + + // Identify possible drivers for each clock domain + dict> clock_drivers; + for (const auto& domain : domains) { + + const NetInfo* ni = ctx->nets.at(domain.key.clock).get(); + dict drivers; + find_net_drivers(ni, drivers, 0, 0); + + clock_drivers[domain.key.clock] = drivers; + + log("Clock '%s' can be driven by:\n", domain.key.clock.str(ctx).c_str()); + for (const auto& it : drivers) { + log(" net '%s' delay %.3fns\n", it.first.str(ctx).c_str(), ctx->getDelayNS(it.second)); + } + } + + // Identify related clocks + for (const auto& c1 : clock_drivers) { + for (const auto& c2 : clock_drivers) { + + // Evident? + if (c1 == c2) { + continue; + } + + // Make an intersection of the two drivers sets + pool common_drivers; + for (const auto& it : c1.second) { + common_drivers.insert(it.first); + } + for (const auto& it : c2.second) { + common_drivers.insert(it.first); + } + + for (auto it=common_drivers.begin(); it!=common_drivers.end();) { + if (!c1.second.count(*it) || !c2.second.count(*it)) { + it = common_drivers.erase(it); + } else { + ++it; + } + } + + // DEBUG // + log("Clocks '%s' and '%s'\n", c1.first.str(ctx).c_str(), c2.first.str(ctx).c_str()); + for (const auto& it : common_drivers) { + + // TEST // + const NetInfo* ni = ctx->nets.at(it).get(); + const CellInfo* cell = ni->driver.cell; + const IdString port = ni->driver.port; + + int info_count; + auto timing_class = ctx->getPortTimingClass(cell, port, info_count); + // TEST // + + log(" net '%s', cell %s (%s), port %s, class%d\n", + it.str(ctx).c_str(), + cell->name.str(ctx).c_str(), + cell->type.str(ctx).c_str(), + port.str(ctx).c_str(), + int(timing_class) + ); + } + // DEBUG // + + // If there is no single driver then consider the two clocks + // unrelated. + if (common_drivers.size() != 1) { + continue; + } + + // Compute delay from c1 to c2 and store it + auto driver = *common_drivers.begin(); + auto delay = c2.second.at(driver) - c1.second.at(driver); + clock_delays[std::make_pair(c1.first, c2.first)] = delay; + } + } +} + void TimingAnalyser::reset_times() { for (auto &port : ports) { @@ -466,11 +640,23 @@ void TimingAnalyser::compute_slack() auto &pd = ports.at(p); for (auto &pdp : pd.domain_pairs) { auto &dp = domain_pairs.at(pdp.first); + + // Get clock names + const auto& launch_clock = domains.at(dp.key.launch).key.clock; + const auto& capture_clock = domains.at(dp.key.capture).key.clock; + + // Get clock-to-clock delay if any + delay_t clock_to_clock = 0; + auto clocks = std::make_pair(launch_clock, capture_clock); + if (clock_delays.count(clocks)) { + clock_to_clock = clock_delays.at(clocks); + } + auto &arr = pd.arrival.at(dp.key.launch); auto &req = pd.required.at(dp.key.capture); - pdp.second.setup_slack = 0 - (arr.value.maxDelay() - req.value.minDelay()); + pdp.second.setup_slack = 0 - (arr.value.maxDelay() - req.value.minDelay() + clock_to_clock); if (!setup_only) - pdp.second.hold_slack = arr.value.minDelay() - req.value.maxDelay(); + pdp.second.hold_slack = arr.value.minDelay() - req.value.maxDelay() + clock_to_clock; pdp.second.max_path_length = arr.path_length + req.path_length; if (dp.key.launch == dp.key.capture) pd.worst_setup_slack = std::min(pd.worst_setup_slack, dp.period.minDelay() + pdp.second.setup_slack); @@ -541,10 +727,6 @@ void TimingAnalyser::print_critical_path(CellPortKey endpoint, domain_id_t domai } } -namespace { -const char *edge_name(ClockEdge edge) { return (edge == FALLING_EDGE) ? "negedge" : "posedge"; } -} // namespace - void TimingAnalyser::print_report() { for (int i = 0; i < int(domain_pairs.size()); i++) { @@ -558,6 +740,16 @@ void TimingAnalyser::print_report() print_critical_path(ep, i); log_break(); } + + print_fmax(); + + for (const auto& it : clock_delays) { + log_info("Clock-to-clock %s -> %s: %0.02f ns\n", + it.first.first.str(ctx).c_str(), + it.first.second.str(ctx).c_str(), + ctx->getDelayNS(it.second) + ); + } } domain_id_t TimingAnalyser::domain_id(IdString cell, IdString clock_port, ClockEdge edge) @@ -1216,11 +1408,15 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p (update_results && ctx->detailed_timing_report) ? &detailed_net_timings : nullptr); timing.walk_paths(); + TimingAnalyser timingAnalyser (ctx); + timingAnalyser.setup(); + bool report_critical_paths = print_path || print_fmax || update_results; dict clock_reports; std::vector xclock_reports; dict clock_fmax; + dict, ClockFmax> xclock_fmax; std::set empty_clocks; // set of clocks with no interior paths if (report_critical_paths) { @@ -1256,12 +1452,24 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p if (a.clock == b.clock && a.clock != ctx->id("$async$")) continue; + double Fmax; + if (a.edge == b.edge) + Fmax = 1000 / ctx->getDelayNS(path.second.path_delay); + else + Fmax = 500 / ctx->getDelayNS(path.second.path_delay); + + auto key = std::make_pair(a.clock, b.clock); + if (!xclock_fmax.count(key) || Fmax < xclock_fmax.at(key).achieved) { + xclock_fmax[key].achieved = Fmax; + xclock_fmax[key].constraint = 0.0f; // Will be filled later + } + auto &crit_path = crit_paths.at(path.first).ports; xclock_reports.push_back(build_critical_path_report(ctx, path.first, crit_path)); xclock_reports.back().period = path.second.path_period; } - if (clock_reports.empty()) { + if (clock_reports.empty() && xclock_reports.empty()) { log_info("No Fmax available; no interior timing paths found in design.\n"); } @@ -1418,6 +1626,10 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_info("Critical path report for cross-domain path '%s' -> '%s':\n", start.c_str(), end.c_str()); print_path_report(report); } + + log("== NEW CODE ==\n"); + timingAnalyser.print_report(); + log("== NEW CODE ==\n"); } if (print_fmax) { diff --git a/common/kernel/timing.h b/common/kernel/timing.h index fe1bcaa84c..9562ba9347 100644 --- a/common/kernel/timing.h +++ b/common/kernel/timing.h @@ -92,6 +92,10 @@ struct TimingAnalyser return slack; } + auto get_clock_delays () const { + return clock_delays; + } + bool setup_only = false; bool verbose_mode = false; bool have_loops = false; @@ -103,6 +107,7 @@ struct TimingAnalyser void get_route_delays(); void topo_sort(); void setup_port_domains(); + void identify_related_domains(); void reset_times(); @@ -217,6 +222,7 @@ struct TimingAnalyser dict pair_to_id; std::vector domains; std::vector domain_pairs; + dict, delay_t> clock_delays; std::vector topological_order; From 60a6e8b070081492f1df0b829123c3bf08de8566 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 31 Aug 2022 14:15:33 +0200 Subject: [PATCH 182/712] Added timing check for cross-domain paths for related clocks Signed-off-by: Maciej Kurc --- common/kernel/timing.cc | 108 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/common/kernel/timing.cc b/common/kernel/timing.cc index 370b43c65b..0141da9311 100644 --- a/common/kernel/timing.cc +++ b/common/kernel/timing.cc @@ -1408,6 +1408,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p (update_results && ctx->detailed_timing_report) ? &detailed_net_timings : nullptr); timing.walk_paths(); + // Use TimingAnalyser to determine clock-to-clock relations TimingAnalyser timingAnalyser (ctx); timingAnalyser.setup(); @@ -1626,10 +1627,6 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_info("Critical path report for cross-domain path '%s' -> '%s':\n", start.c_str(), end.c_str()); print_path_report(report); } - - log("== NEW CODE ==\n"); - timingAnalyser.print_report(); - log("== NEW CODE ==\n"); } if (print_fmax) { @@ -1657,6 +1654,109 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_nonfatal_error("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "", clock_name.c_str(), fmax, passed ? "PASS" : "FAIL", target); } + log_break(); + + // All clock to clock delays + const auto& clock_delays = timingAnalyser.get_clock_delays(); + + // Clock to clock delays for xpaths + dict xclock_delays; + for (auto &report : xclock_reports) { + const auto& clock1_name = report.clock_pair.start.clock; + const auto& clock2_name = report.clock_pair.end.clock; + + const auto key = std::make_pair(clock1_name, clock2_name); + if (clock_delays.count(key)) { + xclock_delays[report.clock_pair] = clock_delays.at(key); + } + } + + unsigned max_width_xca = 0; + unsigned max_width_xcb = 0; + for (auto &report : xclock_reports) { + max_width_xca = std::max((unsigned)format_event(report.clock_pair.start).length(), max_width_xca); + max_width_xcb = std::max((unsigned)format_event(report.clock_pair.end).length(), max_width_xcb); + } + + // Check and report xpath delays for related clocks + if (!xclock_reports.empty()) { + for (auto &report : xclock_reports) { + const auto& clock_a = report.clock_pair.start.clock; + const auto& clock_b = report.clock_pair.end.clock; + + const auto key = std::make_pair(clock_a, clock_b); + if (!clock_delays.count(key)) { + continue; + } + + delay_t path_delay = 0; + for (const auto &segment : report.segments) { + path_delay += segment.delay; + } + + // Compensate path delay for clock-to-clock delay. If the + // result is negative then only the latter matters. Otherwise + // the compensated path delay is taken. + auto clock_delay = clock_delays.at(key); + path_delay -= clock_delay; + + float fmax = std::numeric_limits::infinity(); + if (path_delay < 0) { + fmax = 1e3f / ctx->getDelayNS(clock_delay); + } else if (path_delay > 0) { + fmax = 1e3f / ctx->getDelayNS(path_delay); + } + + // Both clocks are related so they should have the same + // frequency. However, they may get different constraints from + // user input. In case of only one constraint preset take it, + // otherwise get the worst case (min.) + float target; + if (clock_fmax.count(clock_a) && !clock_fmax.count(clock_b)) { + target = clock_fmax.at(clock_a).constraint; + } + else if (!clock_fmax.count(clock_a) && clock_fmax.count(clock_b)) { + target = clock_fmax.at(clock_b).constraint; + } + else { + target = std::min(clock_fmax.at(clock_a).constraint, clock_fmax.at(clock_b).constraint); + } + + bool passed = target < fmax; + + auto ev_a = format_event(report.clock_pair.start, max_width_xca); + auto ev_b = format_event(report.clock_pair.end, max_width_xcb); + + if (!warn_on_failure || passed) + log_info("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n", + ev_a.c_str(), ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target); + else if (bool_or_default(ctx->settings, ctx->id("timing/allowFail"), false)) + log_warning("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n", + ev_a.c_str(), ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target); + else + log_nonfatal_error("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n", + ev_a.c_str(), ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target); + } + log_break(); + } + + // Report clock delays for xpaths + if (!clock_delays.empty()) { + for (auto &pair : xclock_delays) { + auto ev_a = format_event(pair.first.start, max_width_xca); + auto ev_b = format_event(pair.first.end, max_width_xcb); + + delay_t delay = pair.second; + if (pair.first.start.edge != pair.first.end.edge) { + delay /= 2; + } + + log_info("Clock to clock delay %s -> %s: %0.02f ns\n", ev_a.c_str(), ev_b.c_str(), ctx->getDelayNS(delay)); + } + + log_break(); + } + for (auto &eclock : empty_clocks) { if (eclock != ctx->id("$async$")) log_info("Clock '%s' has no interior paths\n", eclock.c_str(ctx)); From 1f1bae3e23bd3acb932d9287350ab2c38489a2f4 Mon Sep 17 00:00:00 2001 From: Maciej Kurc Date: Wed, 31 Aug 2022 14:31:24 +0200 Subject: [PATCH 183/712] Code cleanup Signed-off-by: Maciej Kurc --- common/kernel/timing.cc | 99 +++++++++++++++-------------------------- nexus/arch.cc | 8 ++-- 2 files changed, 39 insertions(+), 68 deletions(-) diff --git a/common/kernel/timing.cc b/common/kernel/timing.cc index 0141da9311..b7b979fc08 100644 --- a/common/kernel/timing.cc +++ b/common/kernel/timing.cc @@ -254,7 +254,6 @@ void TimingAnalyser::setup_port_domains() copy_domains(port, CellPortKey(pi.net->driver), true); } } - // Iterate over ports and find domain paris for (auto port : topological_order) { auto &pd = ports.at(port); @@ -296,8 +295,8 @@ void TimingAnalyser::identify_related_domains() { // For each clock net identify all nets that can possibly drive it. Compute // cumulative delays to each of them. - std::function&, delay_t, int32_t)> find_net_drivers = - [&] (const NetInfo* ni, dict& drivers, delay_t delay_acc, int32_t level) + std::function&, delay_t)> find_net_drivers = + [&] (const NetInfo* ni, dict& drivers, delay_t delay_acc) { // Get driving cell and port const CellInfo* cell = ni->driver.cell; @@ -305,30 +304,14 @@ void TimingAnalyser::identify_related_domains() { bool didGoUpstream = false; - std::string indent (level, ' '); - - log("%sout %s.%s\n", indent.c_str(), cell->name.str(ctx).c_str(), port.str(ctx).c_str()); - // The cell has only one port if (cell->ports.size() == 1) { drivers[ni->name] = delay_acc; return; } - // Count cell port types - size_t num_inp = 0; - size_t num_out = 0; - for (const auto& it : cell->ports) { - const auto& pi = it.second; - if (pi.type != PORT_IN) { - num_inp++; - } else { - num_out++; - } - } - // Get the driver timing class - int info_count = 0; + int info_count = 0; auto timing_class = ctx->getPortTimingClass(cell, port, info_count); // The driver must be a combinational output @@ -361,16 +344,13 @@ void TimingAnalyser::identify_related_domains() { continue; } - log("%sinp %s.%s\n", indent.c_str(), cell->name.str(ctx).c_str(), pi.name.str(ctx).c_str()); - // Recurse - find_net_drivers(pi.net, drivers, delay_acc + delay.maxDelay(), level+1); + find_net_drivers(pi.net, drivers, delay_acc + delay.maxDelay()); didGoUpstream = true; } // Did not propagate upstream through the cell, mark the net as driver if (!didGoUpstream) { - log("%send %s\n", indent.c_str(), ni->name.str(ctx).c_str()); drivers[ni->name] = delay_acc; } }; @@ -381,21 +361,28 @@ void TimingAnalyser::identify_related_domains() { const NetInfo* ni = ctx->nets.at(domain.key.clock).get(); dict drivers; - find_net_drivers(ni, drivers, 0, 0); + find_net_drivers(ni, drivers, 0); clock_drivers[domain.key.clock] = drivers; - log("Clock '%s' can be driven by:\n", domain.key.clock.str(ctx).c_str()); - for (const auto& it : drivers) { - log(" net '%s' delay %.3fns\n", it.first.str(ctx).c_str(), ctx->getDelayNS(it.second)); + if (ctx->debug) { + log("Clock '%s' can be driven by:\n", domain.key.clock.str(ctx).c_str()); + for (const auto& it : drivers) { + const NetInfo* net = ctx->nets.at(it.first).get(); + log(" %s.%s delay %.3fns\n", + net->driver.cell->name.str(ctx).c_str(), + net->driver.port.str(ctx).c_str(), + ctx->getDelayNS(it.second) + ); + } } } - // Identify related clocks + // Identify related clocks. For simplicity do it both for A->B and B->A + // cases. for (const auto& c1 : clock_drivers) { for (const auto& c2 : clock_drivers) { - // Evident? if (c1 == c2) { continue; } @@ -417,28 +404,25 @@ void TimingAnalyser::identify_related_domains() { } } - // DEBUG // - log("Clocks '%s' and '%s'\n", c1.first.str(ctx).c_str(), c2.first.str(ctx).c_str()); - for (const auto& it : common_drivers) { - - // TEST // - const NetInfo* ni = ctx->nets.at(it).get(); - const CellInfo* cell = ni->driver.cell; - const IdString port = ni->driver.port; - - int info_count; - auto timing_class = ctx->getPortTimingClass(cell, port, info_count); - // TEST // - - log(" net '%s', cell %s (%s), port %s, class%d\n", - it.str(ctx).c_str(), - cell->name.str(ctx).c_str(), - cell->type.str(ctx).c_str(), - port.str(ctx).c_str(), - int(timing_class) - ); + if (ctx->debug) { + + log("Possible common driver(s) for clocks '%s' and '%s'\n", + c1.first.str(ctx).c_str(), c2.first.str(ctx).c_str()); + + for (const auto& it : common_drivers) { + + const NetInfo* ni = ctx->nets.at(it).get(); + const CellInfo* cell = ni->driver.cell; + const IdString port = ni->driver.port; + + log(" net '%s', cell %s (%s), port %s\n", + it.str(ctx).c_str(), + cell->name.str(ctx).c_str(), + cell->type.str(ctx).c_str(), + port.str(ctx).c_str() + ); + } } - // DEBUG // // If there is no single driver then consider the two clocks // unrelated. @@ -1417,7 +1401,6 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p dict clock_reports; std::vector xclock_reports; dict clock_fmax; - dict, ClockFmax> xclock_fmax; std::set empty_clocks; // set of clocks with no interior paths if (report_critical_paths) { @@ -1453,18 +1436,6 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p if (a.clock == b.clock && a.clock != ctx->id("$async$")) continue; - double Fmax; - if (a.edge == b.edge) - Fmax = 1000 / ctx->getDelayNS(path.second.path_delay); - else - Fmax = 500 / ctx->getDelayNS(path.second.path_delay); - - auto key = std::make_pair(a.clock, b.clock); - if (!xclock_fmax.count(key) || Fmax < xclock_fmax.at(key).achieved) { - xclock_fmax[key].achieved = Fmax; - xclock_fmax[key].constraint = 0.0f; // Will be filled later - } - auto &crit_path = crit_paths.at(path.first).ports; xclock_reports.push_back(build_critical_path_report(ctx, path.first, crit_path)); xclock_reports.back().period = path.second.path_period; diff --git a/nexus/arch.cc b/nexus/arch.cc index 70e3d38de3..bc00a1e98a 100644 --- a/nexus/arch.cc +++ b/nexus/arch.cc @@ -495,10 +495,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort } else if (cell->type == id_DCC) { if (fromPort == id_CLKI && toPort == id_CLKO) { // TODO: Use actual DCC delays - delay.rise.min_delay = 1000; - delay.rise.max_delay = 1000; - delay.fall.min_delay = 1000; - delay.fall.max_delay = 1000; + delay.rise.min_delay = 1; + delay.rise.max_delay = 1; + delay.fall.min_delay = 1; + delay.fall.max_delay = 1; return true; } } From f4230553901de30f07be5906471b219ed255cb6e Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 4 Aug 2022 09:04:02 +0200 Subject: [PATCH 184/712] fabulous: Add a viaduct uarch Signed-off-by: gatecat --- generic/family.cmake | 2 +- generic/viaduct/fabulous/constids.inc | 88 +++++ generic/viaduct/fabulous/fab_cfg.h | 102 ++++++ generic/viaduct/fabulous/fab_defs.h | 39 ++ generic/viaduct/fabulous/fabric_parsing.h | 175 +++++++++ generic/viaduct/fabulous/fabulous.cc | 396 +++++++++++++++++++++ generic/viaduct/fabulous/fasm.cc | 191 ++++++++++ generic/viaduct/fabulous/fasm.h | 32 ++ generic/viaduct/fabulous/pack.cc | 253 +++++++++++++ generic/viaduct/fabulous/pack.h | 32 ++ generic/viaduct/fabulous/validity_check.cc | 201 +++++++++++ generic/viaduct/fabulous/validity_check.h | 127 +++++++ 12 files changed, 1637 insertions(+), 1 deletion(-) create mode 100644 generic/viaduct/fabulous/constids.inc create mode 100644 generic/viaduct/fabulous/fab_cfg.h create mode 100644 generic/viaduct/fabulous/fab_defs.h create mode 100644 generic/viaduct/fabulous/fabric_parsing.h create mode 100644 generic/viaduct/fabulous/fabulous.cc create mode 100644 generic/viaduct/fabulous/fasm.cc create mode 100644 generic/viaduct/fabulous/fasm.h create mode 100644 generic/viaduct/fabulous/pack.cc create mode 100644 generic/viaduct/fabulous/pack.h create mode 100644 generic/viaduct/fabulous/validity_check.cc create mode 100644 generic/viaduct/fabulous/validity_check.h diff --git a/generic/family.cmake b/generic/family.cmake index de4ce1afa8..b6a674f7f2 100644 --- a/generic/family.cmake +++ b/generic/family.cmake @@ -1,4 +1,4 @@ -set(VIADUCT_UARCHES "example" "okami") +set(VIADUCT_UARCHES "example" "okami" "fabulous") foreach(uarch ${VIADUCT_UARCHES}) aux_source_directory(${family}/viaduct/${uarch} UARCH_FILES) foreach(target ${family_targets}) diff --git a/generic/viaduct/fabulous/constids.inc b/generic/viaduct/fabulous/constids.inc new file mode 100644 index 0000000000..c46d5e6d39 --- /dev/null +++ b/generic/viaduct/fabulous/constids.inc @@ -0,0 +1,88 @@ +X(FABULOUS_LC) +X(FABULOUS_COMB) +X(FABULOUS_FF) + +X(SET_NORESET) +X(ASYNC_SR) +X(NEG_CLK) +X(FF) +X(LATCH_NOFF) + +X(IO_1_bidirectional_frame_config_pass) +X(InPass4_frame_config) +X(OutPass4_frame_config) +X(RegFile_32x4) +X(MULADD) +X(MUX8LUT_frame_config) + +X(CLK) +X(I) +X(T) +X(O) +X(Q) +X(Ci) +X(Co) + +X(X0Y0) + +X(REG_CLK) +X(LUT_CLK) +X(global_clock) + +X(Global_Clock) +X(O2Q) + +X(WRITE_DATA) +X(WRITE_ADDRESS) +X(READ_DATA) +X(READ_ADDRESS) +X(DSP_DATA_OUT) +X(DSP_DATA_IN) +X(DSP_CLR) + +X(carry_in) +X(carry_out) + +X(LUTFF) +X(LUTFF_E) +X(LUTFF_SR) +X(LUTFF_SS) +X(LUTFF_ESR) +X(LUTFF_ESS) +X(LUTFF_R) +X(LUTFF_S) +X(LUTFF_ER) +X(LUTFF_ES) + +X(LUTFF_N) +X(LUTFF_NE) +X(LUTFF_NSR) +X(LUTFF_NSS) +X(LUTFF_NESR) +X(LUTFF_NESS) +X(LUTFF_NR) +X(LUTFF_NS) +X(LUTFF_NER) +X(LUTFF_NES) + +X(clr) + +X(__disconnected) +X(INIT) + +X(E) +X(C) +X(D) +X(S) +X(R) +X(SR) +X(EN) + +X(BEL) +X(PAD) +X(LUT1) + +X(BelBegin) +X(BelEnd) +X(GlobalClk) +X(CFG) diff --git a/generic/viaduct/fabulous/fab_cfg.h b/generic/viaduct/fabulous/fab_cfg.h new file mode 100644 index 0000000000..dc487fbddd --- /dev/null +++ b/generic/viaduct/fabulous/fab_cfg.h @@ -0,0 +1,102 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 FAB_CFG_H +#define FAB_CFG_H + +#include "fab_defs.h" +#include "hashlib.h" +#include "idstring.h" +#include "nextpnr_assertions.h" +#include "nextpnr_namespaces.h" + +#include +#include + +NEXTPNR_NAMESPACE_BEGIN + +/* +This set of structures is designed to enumerate the different configurable options for a fabulous architecture, +affecting the packer etc... +*/ + +struct ControlSetConfig +{ + /* + CLB signal routing masks for fast validity checking + for each unique CLK/CE/SR input to a CLB, add an entry to this vector, and set the bits to 1 for each ff that + signal can drive for a CLB with 8 FFs and 2 clocks split at halfway, the first entry would be 0x0F and the second + 0xF0 + */ + std::vector routing = {0b11111111}; // default 1 shared between all + bool have_signal = true; + bool can_invert = false; +}; + +struct LogicConfig +{ + // ** Core CLB config + unsigned lc_per_clb = 8; // number of logic cells per clb + bool split_lc = false; // whether to represent SLICE as a single bel or separate lut+ff (latter important if ff and + // lut can be used separately) + + // ** LUT config + unsigned lut_k = 4; // base number of inputs for lookup table + enum LutType + { + SINGLE_LUT, + // ... + } lut_type = LutType::SINGLE_LUT; // different types of fracturable LUT structure + + enum LutCascade + { + NO_CASCADE, + // ... + } lut_casc = LutCascade::NO_CASCADE; // different types of cascading between LUTs + + // TODO: other features we might want to represent... + // TODO: fracLUT/FF/mux/carry output sharing matrices + + // ** Carry config + enum CarryType + { + NO_CARRY, // no carry chain + HA_PRE_LUT, // half addder before LUT (classic fabulous LC) + PG_POST_LUT, // prop/gen logic after a fractured LUT + FA_POST_LUT, // full adder after a fractured LUT + } carry_type = CarryType::HA_PRE_LUT; + int carry_lut_frac = -1; // how the LUT is fractured for PG_POST_LUT/FA_POST_LUT, if the LUT fracturing is different + // (or only supported) for carry modes and not in general + + // ** FF config + unsigned ff_per_lc = 1; // number of flipflops per logic cell + uint32_t dedi_ff_input = 0; // mask of flipflops in a LC that have dedicated inputs + uint32_t dedi_ff_output = 0; // mask of flipflops in a LC that have dedicated outputs + + ControlSetConfig clk, sr, en; // flipflop control set routing +}; + +struct FabricConfig +{ + LogicConfig clb; + // DSP cascading, BRAM, IP rules, IO, clocking ... +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct/fabulous/fab_defs.h b/generic/viaduct/fabulous/fab_defs.h new file mode 100644 index 0000000000..bd026b18c0 --- /dev/null +++ b/generic/viaduct/fabulous/fab_defs.h @@ -0,0 +1,39 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2022 gatecat + * + * 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 FAB_DEFS_H +#define FAB_DEFS_H + +#include +#include "nextpnr_namespaces.h" + +NEXTPNR_NAMESPACE_BEGIN + +/* +This defines some compile-time maximums for the fabulous arch +*/ + +static constexpr unsigned MAX_LUTK = 6; // max number of LUT inputs + +typedef uint64_t route_mask_t; // the width of this type defines the max number of FFs in a CLB (for defining control + // set route patterns) + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct/fabulous/fabric_parsing.h b/generic/viaduct/fabulous/fabric_parsing.h new file mode 100644 index 0000000000..3fa263ca5d --- /dev/null +++ b/generic/viaduct/fabulous/fabric_parsing.h @@ -0,0 +1,175 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2022 gatecat + * + * 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 FABULOUS_PARSING_H +#define FABULOUS_PARSING_H + +#include +#include +#include +#include "idstring.h" +#include "nextpnr_assertions.h" +#include "nextpnr_namespaces.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct BaseCtx; + +// Lightweight NIH string_view +struct parser_view +{ + char *m_ptr; + size_t m_length; + parser_view() : m_ptr(nullptr), m_length(0){}; + explicit parser_view(std::string &str) : m_ptr(&(str[0])), m_length(str.size()){}; + parser_view(char *ptr, size_t length) : m_ptr(ptr), m_length(length){}; + + static constexpr size_t npos = std::numeric_limits::max(); + char operator[](size_t idx) + { + NPNR_ASSERT(idx < m_length); + return m_ptr[idx]; + } + + size_t size() const { return m_length; } + + bool empty() const { return m_length == 0; } + + parser_view substr(size_t start, size_t length = npos) + { + NPNR_ASSERT(start <= m_length); + if (length == npos) + length = m_length - start; + NPNR_ASSERT(length <= m_length); + return parser_view(m_ptr + start, length); + } + + size_t find(char tok) const + { + for (size_t i = 0; i < m_length; i++) + if (m_ptr[i] == tok) + return i; + return npos; + } + + IdString to_id(const BaseCtx *ctx) + { + // This isn't really ideal, let's hope one day we can move to C++20 and have proper string_views instead :3 + char tmp = m_ptr[m_length]; + m_ptr[m_length] = '\0'; + IdString id = IdString(ctx, m_ptr); + m_ptr[m_length] = tmp; + return id; + } + + long to_int() + { + // This isn't really ideal, let's hope one day we can move to C++20 and have proper string_views instead :3 + char tmp = m_ptr[m_length]; + m_ptr[m_length] = '\0'; + long l = strtol(m_ptr, nullptr, 0); + m_ptr[m_length] = tmp; + return l; + } + + parser_view strip(const std::string &ws = " \r\n\t") + { + char *ptr = m_ptr; + size_t length = m_length; + while (length > 0) { // strip front + if (ws.find(*ptr) == std::string::npos) // not whitespace + break; + ptr++; + length--; + } + while (length > 0) { // strip back + if (ws.find(ptr[length - 1]) == std::string::npos) // not whitespace + break; + length--; + } + return parser_view(ptr, length); + } + + char back() + { + NPNR_ASSERT(m_length > 0); + return m_ptr[m_length - 1]; + } + + parser_view back(size_t count) + { + NPNR_ASSERT(count <= m_length); + return parser_view(m_ptr + (m_length - count), count); + } + + bool starts_with(const std::string &st) + { + if (m_length < st.size()) + return false; + for (size_t i = 0; i < st.length(); i++) + if (m_ptr[i] != st[i]) + return false; + return true; + } + std::pair split(char delim) const + { + size_t pos = find(delim); + NPNR_ASSERT(pos != npos); + return std::make_pair(parser_view(m_ptr, pos), parser_view(m_ptr + pos + 1, m_length - (pos + 1))); + } +}; + +struct CsvParser +{ + explicit CsvParser(std::istream &in) : in(in){}; + std::istream ∈ + std::string buf; + parser_view view; + bool fetch_next_line() + { + while (!in.eof()) { + std::getline(in, buf); + view = parser_view(buf).strip(); + size_t end_pos = view.find('#'); + if (end_pos != parser_view::npos) + view = view.substr(0, end_pos); + view = view.strip(); + if (!view.empty()) + return true; + } + return false; + } + parser_view next_field() + { + size_t next_delim = view.find(','); + if (next_delim == parser_view::npos) { + parser_view result = view.substr(0, next_delim); + view = parser_view(); + return result; + } else { + parser_view result = view.substr(0, next_delim); + view = view.substr(next_delim + 1); + return result; + } + } +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct/fabulous/fabulous.cc b/generic/viaduct/fabulous/fabulous.cc new file mode 100644 index 0000000000..e2adb8947c --- /dev/null +++ b/generic/viaduct/fabulous/fabulous.cc @@ -0,0 +1,396 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 "fabric_parsing.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" +#include "viaduct_api.h" +#include "viaduct_helpers.h" + +#include + +#define GEN_INIT_CONSTIDS +#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc" +#include "viaduct_constids.h" + +#include "fab_cfg.h" +#include "fab_defs.h" +#include "fasm.h" +#include "pack.h" +#include "validity_check.h" + +#include + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct FabulousImpl : ViaductAPI +{ + FabulousImpl(const dict &args) + { + for (auto a : args) { + if (a.first == "fasm") + fasm_file = a.second; + else + log_error("unrecognised fabulous option '%s'\n", a.first.c_str()); + } + } + ~FabulousImpl(){}; + void init(Context *ctx) override + { + init_uarch_constids(ctx); + ViaductAPI::init(ctx); + h.init(ctx); + fab_root = get_env_var("FAB_ROOT", ", set it to the fabulous build output or project path"); + if (boost::filesystem::exists(fab_root + "/.FABulous")) + is_new_fab = true; + else + is_new_fab = false; + log_info("Detected FABulous %s format project.\n", is_new_fab ? "2.0" : "1.0"); + // To consider: a faster serialised form of the device data (like bba that other arches use) so we don't have to + // go through the whole csv parsing malarkey each time + blk_trk = std::make_unique(ctx, cfg); + is_new_fab ? init_bels_v2() : init_bels_v1(); + init_pips(); + } + + void pack() override { fabulous_pack(ctx, cfg); } + + void postRoute() override + { + if (!fasm_file.empty()) + fabulous_write_fasm(ctx, cfg, fasm_file); + } + + void prePlace() override { assign_cell_info(); } + bool isBelLocationValid(BelId bel) const override { return blk_trk->check_validity(bel, cfg, cell_tags); } + + private: + FabricConfig cfg; // TODO: non-default config + ViaductHelpers h; + + WireId global_clk_wire; + + std::string fasm_file; + + std::unique_ptr blk_trk; + + std::string get_env_var(const std::string &name, const std::string &prompt = "") + { + const char *var = getenv(name.c_str()); + if (var == nullptr) + log_error("environment variable '%s' is not set%s\n", name.c_str(), prompt.c_str()); + return std::string(var); + } + + std::ifstream open_data_rel(const std::string &postfix) + { + const std::string filename(fab_root + postfix); + std::ifstream in(filename); + if (!in) + log_error("failed to open data file '%s' (is FAB_ROOT set correctly?)\n", filename.c_str()); + return in; + } + + std::string fab_root; + bool is_new_fab; + + pool warned_beltypes; + + void add_pseudo_pip(WireId src, WireId dst, IdString pip_type) + { + const auto &src_data = ctx->wire_info(src); + IdStringList pip_name = IdStringList::concat(ctx->getWireName(src), ctx->getWireName(dst)); + ctx->addPip(pip_name, pip_type, src, dst, ctx->getDelayFromNS(0.05), Loc(src_data.x, src_data.y, 0)); + } + + void handle_bel_ports(BelId bel, IdString tile, IdString bel_type, const std::vector &ports) + { + // TODO: improve the scalability here as we support more bel types + IdString idx = ctx->getBelName(bel)[1]; + Loc loc = ctx->getBelLocation(bel); + if (bel_type == id_IO_1_bidirectional_frame_config_pass) { + for (parser_view p : ports) { + IdString port_id = p.to_id(ctx); + WireId port_wire = get_wire(tile, port_id, ctx->idf("W_IO_%s", port_id.c_str(ctx))); + IdString pin = p.back(1).to_id(ctx); + ctx->addBelPin(bel, pin, port_wire, pin.in(id_I, id_T) ? PORT_IN : PORT_OUT); + } + } else if (bel_type.in(id_InPass4_frame_config, id_OutPass4_frame_config)) { + for (parser_view p : ports) { + IdString port_id = p.to_id(ctx); + WireId port_wire = get_wire(tile, port_id, port_id); + IdString pin = p.back(2).to_id(ctx); + ctx->addBelPin(bel, pin, port_wire, bel_type == id_OutPass4_frame_config ? PORT_IN : PORT_OUT); + } + } else if (bel_type == id_RegFile_32x4) { + WireId clk_wire = get_wire(tile, id_CLK, id_REG_CLK); + ctx->addBelInput(bel, id_CLK, clk_wire); + add_pseudo_pip(global_clk_wire, clk_wire, id_global_clock); + for (parser_view p : ports) { + IdString port_id = p.to_id(ctx); + // TODO: nicer way of determining port type? + if (p[0] == 'D') { + ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_WRITE_DATA)); + } else if (p[0] == 'W') { + ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_WRITE_ADDRESS)); + } else if (p[1] == 'D') { + ctx->addBelOutput(bel, port_id, get_wire(tile, port_id, id_READ_DATA)); + } else { + ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_READ_ADDRESS)); + } + } + } else if (bel_type == id_MULADD) { + // TODO: do DSPs need a clock too like regfiles? + for (parser_view p : ports) { + IdString port_id = p.to_id(ctx); + if (p[0] == 'Q') { + ctx->addBelOutput(bel, port_id, get_wire(tile, port_id, id_DSP_DATA_OUT)); + } else if (port_id == id_clr) { + ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_DSP_CLR)); + } else { + ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_DSP_DATA_IN)); + } + } + } else if (bel_type == id_MUX8LUT_frame_config) { + for (parser_view p : ports) { + IdString port_id = p.to_id(ctx); + ctx->addBelPin(bel, port_id, get_wire(tile, port_id, ctx->idf("LUTMUX_%s", port_id.c_str(ctx))), + p[0] == 'M' ? PORT_OUT : PORT_IN); + } + } else if (bel_type == id_FABULOUS_LC) { + // TODO: split LC mode, LUT permutation pseudo-switchbox, LUT thru pseudo-pips + WireId clk_wire = get_wire(tile, ctx->idf("L%s_CLK", idx.c_str(ctx)), id_LUT_CLK); + ctx->addBelInput(bel, id_CLK, clk_wire); + add_pseudo_pip(global_clk_wire, clk_wire, id_global_clock); + blk_trk->set_bel_type(bel, BelFlags::BLOCK_CLB, BelFlags::FUNC_LC_COMB, loc.z); + for (parser_view p : ports) { + IdString port_id = p.to_id(ctx); + WireId port_wire = get_wire(tile, port_id, ctx->idf("LUT_%s", port_id.c_str(ctx))); + // TODO: more robust port name handling + if (p[3] == 'S' || p[3] == 'E' || p[3] == 'I') { // set/reset, enable, LUT input + ctx->addBelInput(bel, p.substr(3).to_id(ctx), port_wire); + } else if (p[3] == 'O') { // LUT otuput + ctx->addBelOutput(bel, p.substr(3, 1).to_id(ctx), port_wire); + } else if (p[3] == 'C') { // carry chain + if (p[4] == 'i') { + ctx->addBelInput(bel, id_Ci, port_wire); + } else { + NPNR_ASSERT(p[4] == 'o'); + ctx->addBelOutput(bel, id_Co, port_wire); + } + } else { + log_error("don't know what to do with LC port '%s'\n", port_id.c_str(ctx)); + } + } + } else { + // ... + if (!warned_beltypes.count(bel_type) && !ports.empty()) { + log_warning("don't know how to handle ports for bel type '%s'\n", bel_type.c_str(ctx)); + warned_beltypes.insert(bel_type); + } + } + } + + void init_global_clock() + { + // TODO: how do we extend this to more complex clocking topologies? + BelId global_clk_bel = + ctx->addBel(IdStringList::concat(ctx->id("X0Y0"), id_CLK), id_Global_Clock, Loc(0, 0, 0), true, false); + global_clk_wire = ctx->addWire(IdStringList::concat(ctx->id("X0Y0"), id_CLK), id_CLK, 0, 0); + ctx->addBelOutput(global_clk_bel, id_CLK, global_clk_wire); + } + + // TODO: this is for legacy fabulous only, the new code path can be a lot simpler + void init_bels_v1() + { + std::ifstream in = open_data_rel("/npnroutput/bel.txt"); + CsvParser csv(in); + init_global_clock(); + while (csv.fetch_next_line()) { + IdString tile = csv.next_field().to_id(ctx); + int bel_x = csv.next_field().substr(1).to_int(); + int bel_y = csv.next_field().substr(1).to_int(); + auto bel_idx = csv.next_field(); + IdString bel_type = csv.next_field().to_id(ctx); + NPNR_ASSERT(bel_idx.size() == 1); + int bel_z = bel_idx[0] - 'A'; + NPNR_ASSERT(bel_z >= 0 && bel_z < 26); + /* + In the future we will need to handle optionally splitting SLICEs into separate LUT/COMB and FF bels + This is the preferred approach in nextpnr for arches where the LUT and FF can be used separately of + each other (e.g. there is a way of routing the LUT and FF outputs individually, and some extra + optional FF input). + While this isn't yet the standard fabulous SLICE, it should be considered as a future option in fabulous. + */ + Loc loc(bel_x, bel_y, bel_z); + BelId bel = ctx->addBel(IdStringList::concat(tile, bel_idx.to_id(ctx)), bel_type, loc, false, false); + std::vector ports; + parser_view port; + while (!(port = csv.next_field()).empty()) { + ports.push_back(port); + } + handle_bel_ports(bel, tile, bel_type, ports); + } + postprocess_bels(); + } + + void init_bels_v2() + { + std::ifstream in = open_data_rel("/.FABulous/bel.v2.txt"); + CsvParser csv(in); + init_global_clock(); + BelId curr_bel; + while (csv.fetch_next_line()) { + IdString cmd = csv.next_field().to_id(ctx); + if (cmd == id_BelBegin) { + IdString tile = csv.next_field().to_id(ctx); + auto bel_idx = csv.next_field(); + IdString bel_type = csv.next_field().to_id(ctx); + NPNR_ASSERT(bel_idx.size() == 1); + int bel_z = bel_idx[0] - 'A'; + NPNR_ASSERT(bel_z >= 0 && bel_z < 26); + Loc loc = tile_loc(tile); + curr_bel = ctx->addBel(IdStringList::concat(tile, bel_idx.to_id(ctx)), bel_type, + Loc(loc.x, loc.y, bel_z), false, false); + } else if (cmd.in(id_I, id_O)) { + IdString port = csv.next_field().to_id(ctx); + auto wire_name = csv.next_field().split('.'); + WireId wire = + get_wire(wire_name.first.to_id(ctx), wire_name.second.to_id(ctx), wire_name.second.to_id(ctx)); + ctx->addBelPin(curr_bel, port, wire, cmd == id_O ? PORT_OUT : PORT_IN); + } else if (cmd == id_GlobalClk) { + IdStringList bel_name = ctx->getBelName(curr_bel); + WireId clk_wire = get_wire(bel_name[0], ctx->idf("%s_CLK", bel_name[1].c_str(ctx)), id_REG_CLK); + ctx->addBelInput(curr_bel, id_CLK, clk_wire); + add_pseudo_pip(global_clk_wire, clk_wire, id_global_clock); + } else if (cmd == id_CFG) { + // TODO... + } else if (cmd == id_BelEnd) { + curr_bel = BelId(); + } else if (cmd != IdString()) { + log_error("unsupported command %s in definition of bel %s\n", cmd.c_str(ctx), + curr_bel == BelId() ? "" : ctx->nameOfBel(curr_bel)); + } + } + postprocess_bels(); + } + + void postprocess_bels() + { + // This does some post-processing on bels to make them useful for nextpnr place-and-route regardless of the code + // path that creates them. In the future, splitting muxes and creating split LCs would be done here, too + for (auto bel : ctx->getBels()) { + auto &data = ctx->bel_info(bel); + if (data.type == id_FABULOUS_LC) { + if (!data.pins.count(id_Q)) { + // Add a Q pseudo-pin and pseudo-pip from Q to O + WireId o_wire = ctx->getBelPinWire(bel, id_O); + IdString q_name = ctx->idf("%s_Q", data.name[1].c_str(ctx)); + WireId q_wire = get_wire(data.name[0], q_name, q_name); + ctx->addBelOutput(bel, id_Q, q_wire); + // Pseudo-pip for FF mode + add_pseudo_pip(q_wire, o_wire, id_O2Q); + } + } else if (data.type == id_IO_1_bidirectional_frame_config_pass) { + if (!data.pins.count(id_PAD)) { + // Add a PAD pseudo-pin for the top level + ctx->addBelInout(bel, id_PAD, + get_wire(data.name[0], ctx->idf("PAD_%s", data.name[1].c_str(ctx)), id_PAD)); + } + } + } + } + + void init_pips() + { + std::ifstream in = open_data_rel(is_new_fab ? "/.FABulous/pips.txt" : "/npnroutput/pips.txt"); + CsvParser csv(in); + while (csv.fetch_next_line()) { + IdString src_tile = csv.next_field().to_id(ctx); + IdString src_port = csv.next_field().to_id(ctx); + IdString dst_tile = csv.next_field().to_id(ctx); + IdString dst_port = csv.next_field().to_id(ctx); + int delay = csv.next_field().to_int(); + IdString pip_name = csv.next_field().to_id(ctx); + WireId src_wire = get_wire(src_tile, src_port, src_port); + WireId dst_wire = get_wire(dst_tile, dst_port, dst_port); + ctx->addPip(IdStringList::concat(src_tile, pip_name), pip_name, src_wire, dst_wire, + ctx->getDelayFromNS(0.01 * delay), tile_loc(src_tile)); + } + } + + // Fast lookup of tile names to XY pairs + dict tile2loc; + Loc tile_loc(IdString tile) + { + if (!tile2loc.count(tile)) { + std::string tile_name = tile.str(ctx); + parser_view view(tile_name); + NPNR_ASSERT(view[0] == 'X'); + size_t ypos = view.find('Y'); + NPNR_ASSERT(ypos != parser_view::npos); + int x = view.substr(1, ypos - 1).to_int(); + int y = view.substr(ypos + 1).to_int(); + tile2loc[tile] = Loc(x, y, 0); + } + return tile2loc.at(tile); + } + + // Create a wire if it doesn't exist, otherwise just return it + WireId get_wire(IdString tile, IdString wire, IdString type) + { + // Create a wire name by using the built-in IdStringList mechanism to store a (tile, wire) pair + // this way we don't store a full string in memory of every concatenated wire name, reducing the memory + // footprint and start time significantly beyond the ~1k LUT scale + auto wire_name = IdStringList::concat(tile, wire); + auto found = ctx->wire_by_name.find(wire_name); + if (found != ctx->wire_by_name.end()) + return found->second; + // doesn't exist + Loc loc = tile_loc(tile); + return ctx->addWire(wire_name, type, loc.x, loc.y); + } + + CellTagger cell_tags; + void assign_cell_info() + { + for (auto &cell : ctx->cells) { + cell_tags.assign_for(ctx, cfg, cell.second.get()); + } + } + void notifyBelChange(BelId bel, CellInfo *cell) + { + CellInfo *old = ctx->getBoundBelCell(bel); + blk_trk->update_bel(bel, old, cell); + } +}; + +struct FabulousArch : ViaductArch +{ + FabulousArch() : ViaductArch("fabulous"){}; + std::unique_ptr create(const dict &args) + { + return std::make_unique(args); + } +} fabulousArch; +} // namespace + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct/fabulous/fasm.cc b/generic/viaduct/fabulous/fasm.cc new file mode 100644 index 0000000000..1405766e49 --- /dev/null +++ b/generic/viaduct/fabulous/fasm.cc @@ -0,0 +1,191 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 "fasm.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include +#include +#include + +#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc" +#include "viaduct_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct FabFasmWriter +{ + FabFasmWriter(const Context *ctx, const FabricConfig &cfg, const std::string &filename) + : ctx(ctx), cfg(cfg), out(filename) + { + if (!out) + log_error("failed to open fasm file '%s' for writing\n", filename.c_str()); + } + std::string format_name(IdStringList name) + { + std::string result; + for (IdString entry : name) { + if (!result.empty()) + result += "."; + result += entry.str(ctx); + } + return result; + } + void write_pip(PipId pip) + { + auto &data = ctx->pip_info(pip); + if (data.type.in(id_global_clock, id_O2Q)) + return; // pseudo-pips with no underlying bitstream bits + // write pip name but with '.' instead of '/' for separator + out << format_name(data.name) << std::endl; + } + void write_routing(const NetInfo *net) + { + std::vector sorted_pips; + for (auto &w : net->wires) + if (w.second.pip != PipId()) + sorted_pips.push_back(w.second.pip); + std::sort(sorted_pips.begin(), sorted_pips.end()); + out << stringf("# routing for net '%s'\n", ctx->nameOf(net)) << std::endl; + for (auto pip : sorted_pips) + write_pip(pip); + out << std::endl; + } + + std::string prefix; + + // Write a FASM bitvector; optionally inverting the values in the process + void write_vector(const std::string &name, const std::vector &value, bool invert = false) + { + out << prefix << name << " = " << int(value.size()) << "'b"; + for (auto bit : boost::adaptors::reverse(value)) + out << ((bit ^ invert) ? '1' : '0'); + out << std::endl; + } + // Write a FASM bitvector given an integer value + void write_int_vector(const std::string &name, uint64_t value, int width, bool invert = false) + { + std::vector bits(width, false); + for (int i = 0; i < width; i++) + bits[i] = (value & (1ULL << i)) != 0; + write_vector(name, bits, invert); + } + // Write an int vector param + void write_int_vector_param(const CellInfo *cell, const std::string &name, uint64_t defval, int width, + bool invert = false) + { + uint64_t value = int_or_default(cell->params, ctx->id(name), defval); + std::vector bits(width, false); + for (int i = 0; i < width; i++) + bits[i] = (value & (1ULL << i)) != 0; + write_vector(stringf("%s[%d:0]", name.c_str(), width - 1), bits, invert); + } + + void write_bool(const CellInfo *cell, const std::string &name) + { + if (bool_or_default(cell->params, ctx->id(name))) { + out << prefix << name << std::endl; + } + } + + void write_logic(const CellInfo *lc) + { + prefix = format_name(ctx->getBelName(lc->bel)) + "."; + if (lc->type.in(id_FABULOUS_LC, id_FABULOUS_COMB)) { + write_int_vector_param(lc, "INIT", 0U, 1U << cfg.clb.lut_k); // todo lut depermute and thru + } + if (lc->type == id_FABULOUS_LC) { + write_bool(lc, "FF"); + } + if (lc->type.in(id_FABULOUS_LC, id_FABULOUS_FF)) { + write_bool(lc, "SET_NORESET"); + write_bool(lc, "NEG_CLK"); + write_bool(lc, "NEG_EN"); + write_bool(lc, "NEG_SR"); + write_bool(lc, "ASYNC_SR"); + } + } + + void write_io(const CellInfo *io) + { + write_bool(io, "INPUT_USED"); + write_bool(io, "OUTPUT_USED"); + write_bool(io, "ENABLE_USED"); + } + + void write_generic_cell(const CellInfo *ci) + { + prefix = format_name(ctx->getBelName(ci->bel)) + "."; + for (auto ¶m : ci->params) { + // TODO: better parameter type auto-detection + if (param.second.is_string) { + // enum type parameter + out << prefix << param.first.c_str(ctx) << "." << param.second.str << std::endl; + } else if (param.second.str.size() == 1) { + // boolean type parameter + if (param.second.intval != 0) + out << prefix << param.first.c_str(ctx) << std::endl; + } else { + // vector type parameter + int msb = int(param.second.str.size()) - 1; + out << prefix << param.first.c_str(ctx) << "[" << msb << ":0] = "; + for (auto bit : boost::adaptors::reverse(param.second.str)) + out << bit; + out << std::endl; + } + } + } + + void write_cell(const CellInfo *ci) + { + out << stringf("# config for cell '%s'\n", ctx->nameOf(ci)) << std::endl; + if (ci->type.in(id_FABULOUS_COMB, id_FABULOUS_FF, id_FABULOUS_LC)) + write_logic(ci); + else if (ci->type == id_IO_1_bidirectional_frame_config_pass) + write_io(ci); + else + write_generic_cell(ci); + // TODO: other cell types + out << std::endl; + } + + void write_fasm() + { + for (const auto &net : ctx->nets) + write_routing(net.second.get()); + for (const auto &cell : ctx->cells) + write_cell(cell.second.get()); + } + + const Context *ctx; + const FabricConfig &cfg; + std::ofstream out; +}; +} // namespace + +void fabulous_write_fasm(const Context *ctx, const FabricConfig &cfg, const std::string &filename) +{ + FabFasmWriter wr(ctx, cfg, filename); + wr.write_fasm(); +} + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct/fabulous/fasm.h b/generic/viaduct/fabulous/fasm.h new file mode 100644 index 0000000000..0f152c64cc --- /dev/null +++ b/generic/viaduct/fabulous/fasm.h @@ -0,0 +1,32 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 FABULOUS_FASM_H +#define FABULOUS_FASM_H + +#include "fab_cfg.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +void fabulous_write_fasm(const Context *ctx, const FabricConfig &cfg, const std::string &filename); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct/fabulous/pack.cc b/generic/viaduct/fabulous/pack.cc new file mode 100644 index 0000000000..0273a28a59 --- /dev/null +++ b/generic/viaduct/fabulous/pack.cc @@ -0,0 +1,253 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 "pack.h" +#include "log.h" +#include "util.h" + +#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc" +#include "viaduct_constids.h" +#include "viaduct_helpers.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct FabulousPacker +{ + Context *ctx; + const FabricConfig &cfg; + ViaductHelpers h; + + dict lut_types; + std::vector lut_inputs; + + FabulousPacker(Context *ctx, const FabricConfig &cfg) : ctx(ctx), cfg(cfg) + { + // Set up some structures for faster lookups + for (unsigned i = 0; i < cfg.clb.lut_k; i++) { + lut_types[ctx->idf("LUT%d", i + 1)] = i + 1; + lut_inputs.push_back(ctx->idf("I%d", i)); + } + h.init(ctx); + } + + void pack_luts() + { + // Pack LUTs into FABULOUS_COMB (split-LUTFF mode) or FABULOUS_LC (packed-LUTFF mode) + // TODO: fracturable LUT handling + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + auto fnd_lut = lut_types.find(ci->type); + if (fnd_lut == lut_types.end()) + continue; + unsigned lut_n = fnd_lut->second; + // convert to the necessary type + ci->type = cfg.clb.split_lc ? id_FABULOUS_COMB : id_FABULOUS_LC; + // add disconnected unused inputs + for (unsigned i = 0; i < cfg.clb.lut_k; i++) + if (!ci->ports.count(lut_inputs.at(i))) + ci->addInput(lut_inputs.at(i)); + // replicate the INIT value so the unused MSBs become don't-cares + auto inst_init = get_or_default(ci->params, id_INIT, Property(0)); + unsigned orig_init_len = 1U << lut_n, prim_len = 1U << cfg.clb.lut_k; + Property new_init(0, prim_len); + for (unsigned i = 0; i < prim_len; i += orig_init_len) { + auto chunk = inst_init.extract(0, orig_init_len); + for (unsigned j = 0; j < orig_init_len; j++) + new_init.str.at(i + j) = chunk.str.at(j); + } + new_init.update_intval(); + ci->params[id_INIT] = new_init; + } + } + + // Two-stage flipflop packing. First convert all the random primitives into a much easier-to-handle FABULOUS_FF + // Then for split-LC mode, cluster it to connected LUTs; separate-LC mode, pack it into a connected or new LC + + void prepare_ffs() + { + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + const std::string &type_str = ci->type.str(ctx); + if (type_str.size() < 5 || type_str.substr(0, 5) != "LUTFF") + continue; + ci->type = id_FABULOUS_FF; + // parse config string and unify + size_t idx = 5; + if (idx < type_str.size() && type_str.at(idx) == '_') + ++idx; + // clock inversion + if (idx < type_str.size() && type_str.at(idx) == 'N') { + ci->params[id_NEG_CLK] = 1; + ++idx; + } else { + ci->params[id_NEG_CLK] = 0; + } + // clock enable + if (idx < type_str.size() && type_str.at(idx) == 'E') + ++idx; + if (ci->ports.count(id_E)) + ci->renamePort(id_E, id_EN); + else + ci->addInput(id_EN); // autocreate emtpy enable port if enable missing or unused + // sr presence and type + std::string srt = type_str.substr(idx); + if (srt == "S") { + ci->params[id_SET_NORESET] = 1; + ci->params[id_ASYNC_SR] = 1; + } else if (srt == "R") { + ci->params[id_SET_NORESET] = 0; + ci->params[id_ASYNC_SR] = 1; + } else if (srt == "SS") { + ci->params[id_SET_NORESET] = 1; + ci->params[id_ASYNC_SR] = 0; + } else if (srt == "SR" || srt == "") { + ci->params[id_SET_NORESET] = 0; + ci->params[id_ASYNC_SR] = 0; + } else { + NPNR_ASSERT_FALSE("unhandled FF type"); + } + if (ci->ports.count(id_S)) + ci->renamePort(id_S, id_SR); + else if (ci->ports.count(id_R)) + ci->renamePort(id_R, id_SR); + if (!ci->ports.count(id_SR)) + ci->addInput(id_SR); // autocreate emtpy enable port if enable missing or unused + } + } + + void pack_ffs() + { + pool to_delete; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_FABULOUS_FF) + continue; + NetInfo *d = ci->getPort(id_D); + if (!d || !d->driver.cell) + continue; + CellInfo *drv = d->driver.cell; + if (drv->type != (cfg.clb.split_lc ? id_FABULOUS_COMB : id_FABULOUS_LC) || d->driver.port != id_O) + continue; + if (!cfg.clb.split_lc && d->users.entries() > 1) + continue; // TODO: could also resolve by duplicating LUT + if (drv->cluster != ClusterId()) { + // TODO: actually we can pack these often, we just have to be more careful to check control sets + continue; + } + // we can pack them together + if (cfg.clb.split_lc) { + // create/modify cluster and add constraints. copy from an arch where we do this already... + NPNR_ASSERT_FALSE("unimplemented"); + } else { + to_delete.insert(ci->name); + // this connection is packed inside the LC + ci->disconnectPort(id_D); + drv->disconnectPort(id_O); + // move other ports/params + ci->movePortTo(id_CLK, drv, id_CLK); + ci->movePortTo(id_SR, drv, id_SR); + ci->movePortTo(id_EN, drv, id_EN); + ci->movePortTo(id_O, drv, id_Q); + drv->params[id_NEG_CLK] = ci->params[id_NEG_CLK]; + drv->params[id_ASYNC_SR] = ci->params[id_ASYNC_SR]; + drv->params[id_SET_NORESET] = ci->params[id_SET_NORESET]; + drv->params[id_FF] = 1; + for (auto &attr : ci->attrs) + drv->attrs[attr.first] = attr.second; + } + } + for (auto del : to_delete) + ctx->cells.erase(del); + if (!cfg.clb.split_lc) { + // convert remaining ffs to their own lc + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_FABULOUS_FF) + continue; + ci->type = id_FABULOUS_LC; + ci->renamePort(id_D, lut_inputs.at(0)); + ci->renamePort(id_O, id_Q); + // configure LUT as a thru + Property init(1U << cfg.clb.lut_k); + for (unsigned i = 0; i < (1U << cfg.clb.lut_k); i += 2) { + init.str[i] = Property::S0; + init.str[i + 1] = Property::S1; + } + init.update_intval(); + ci->params[id_INIT] = init; + ci->params[id_FF] = 1; + } + } + } + + void update_bel_attrs() + { + // This new arch uses the new IdStringList system with a / separator + // old fabulous arches used a dot separator in bel names + // update old attributes for maximum cross-compat + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (!ci->attrs.count(id_BEL)) + continue; + std::string &bel = ci->attrs.at(id_BEL).str; + if (bel.find('/') != std::string::npos) // new style + continue; + size_t dot_pos = bel.find('.'); + if (dot_pos != std::string::npos) + bel[dot_pos] = '/'; + } + } + + void handle_constants() + { + const dict vcc_params = {{id_INIT, Property(0x3, 2)}}; + const dict gnd_params = {{id_INIT, Property(0x0, 2)}}; + h.replace_constants(CellTypePort(id_LUT1, id_O), CellTypePort(id_LUT1, id_O), vcc_params, gnd_params); + } + + void handle_io() + { + // As per the preferred approach for new nextpnr flows, we require IO to be inserted by Yosys + // pre-place-and-route, or just manually instantiated + const pool top_ports{ + CellTypePort(id_IO_1_bidirectional_frame_config_pass, id_PAD), + }; + h.remove_nextpnr_iobs(top_ports); + } + + void run() + { + update_bel_attrs(); + handle_constants(); + handle_io(); + pack_luts(); + prepare_ffs(); + pack_ffs(); + } +}; +} // namespace + +void fabulous_pack(Context *ctx, const FabricConfig &cfg) +{ + FabulousPacker packer(ctx, cfg); + packer.run(); +} + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct/fabulous/pack.h b/generic/viaduct/fabulous/pack.h new file mode 100644 index 0000000000..0ee35e0c4f --- /dev/null +++ b/generic/viaduct/fabulous/pack.h @@ -0,0 +1,32 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 FABULOUS_PACK_H +#define FABULOUS_PACK_H + +#include "fab_cfg.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +void fabulous_pack(Context *ctx, const FabricConfig &cfg); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/generic/viaduct/fabulous/validity_check.cc b/generic/viaduct/fabulous/validity_check.cc new file mode 100644 index 0000000000..91dba689c6 --- /dev/null +++ b/generic/viaduct/fabulous/validity_check.cc @@ -0,0 +1,201 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 "validity_check.h" +#include "log.h" +#include "util.h" + +#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc" +#include "viaduct_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +CLBState::CLBState(const LogicConfig &cfg) +{ + // TODO: more than one per LC if in split-SLICE mode with fracturable LUTs + lc_comb = std::make_unique(cfg.lc_per_clb); + if (cfg.split_lc) { + ff = std::make_unique(cfg.lc_per_clb * cfg.ff_per_lc); + } + // TODO: mux +} + +void CellTagger::assign_for(const Context *ctx, const FabricConfig &cfg, const CellInfo *ci) +{ + if (int(data.size()) <= ci->flat_index) + data.resize(ci->flat_index + 1); + auto &t = data.at(ci->flat_index); + // Use the same logic to handle both packed and split LC modes + if (ci->type.in(id_FABULOUS_COMB, id_FABULOUS_LC)) { + unsigned lut_input_count = 0; + for (unsigned i = 0; i < cfg.clb.lut_k; i++) + if (ci->getPort(ctx->idf("I%d", i))) + lut_input_count = i + 1; + t.comb.lut_inputs = SSOArray(lut_input_count, IdString()); + for (unsigned i = 0; i < lut_input_count; i++) { + const NetInfo *sig = ci->getPort(ctx->idf("I%d", i)); + t.comb.lut_inputs[i] = sig ? sig->name : IdString(); + } + t.comb.carry_used = false; // TODO + t.comb.lut_out = ci->getPort(id_O); + } + if (ci->type.in(id_FABULOUS_FF, id_FABULOUS_LC)) { + if (ci->type == id_FABULOUS_FF || bool_or_default(ci->params, id_FF)) { + t.ff.ff_used = true; + auto get_ctrlsig = [&](IdString name) { + const NetInfo *sig = ci->getPort(name); + bool invert = sig && bool_or_default(ci->params, ctx->idf("NEG_%s", name.c_str(ctx))); + return ControlSig(sig ? sig->name : id___disconnected, invert); + }; + t.ff.clk = get_ctrlsig(id_CLK); + t.ff.sr = get_ctrlsig(id_SR); + t.ff.en = get_ctrlsig(id_EN); + t.ff.async = bool_or_default(ci->params, id_ASYNC_SR); + t.ff.latch = bool_or_default(ci->params, id_LATCH_NOFF); + t.ff.d = ci->getPort(id_D); + t.ff.q = ci->getPort(id_Q); + } else { + t.ff.ff_used = false; + } + } +} + +void BlockTracker::set_bel_type(BelId bel, BelFlags::BlockType block, BelFlags::FuncType func, uint8_t index) +{ + Loc loc = ctx->getBelLocation(bel); + if (int(tiles.size()) <= loc.y) + tiles.resize(loc.y + 1); + auto &row = tiles.at(loc.y); + if (int(row.size()) <= loc.x) + row.resize(loc.x + 1); + auto &tile = row.at(loc.x); + if (block == BelFlags::BLOCK_CLB) { + if (!tile.clb) + tile.clb = std::make_unique(cfg.clb); + } + if (int(bel_data.size()) <= bel.index) + bel_data.resize(bel.index + 1); + auto &flags = bel_data.at(bel.index); + flags.block = block; + flags.func = func; + flags.index = index; +} + +void BlockTracker::update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell) +{ + if (bel.index >= int(bel_data.size())) + return; // some kind of bel not being tracked + auto flags = bel_data.at(bel.index); + if (flags.block == BelFlags::BLOCK_OTHER) + return; // no structures to update + Loc loc = ctx->getBelLocation(bel); + if (loc.y >= int(tiles.size())) + return; // some kind of bel not being tracked + const auto &row = tiles.at(loc.y); + if (loc.x >= int(row.size())) + return; // some kind of bel not being tracked + const auto &entry = row.at(loc.x); + if (flags.block == BelFlags::BLOCK_CLB) { + NPNR_ASSERT(entry.clb); + // TODO: incremental validity check updates might care about this in the future, hence keeping it in the + // interface for now + NPNR_UNUSED(old_cell); + if (flags.func == BelFlags::FUNC_LC_COMB) + entry.clb->lc_comb[flags.index] = new_cell; + else if (flags.func == BelFlags::FUNC_FF) + entry.clb->ff[flags.index] = new_cell; + else if (flags.func == BelFlags::FUNC_MUX) + entry.clb->mux[flags.index] = new_cell; + } +} + +bool CLBState::check_validity(const LogicConfig &cfg, const CellTagger &cell_data) +{ + SSOArray used_clk(cfg.clk.routing.size()), used_sr(cfg.sr.routing.size()), + used_en(cfg.en.routing.size()); + auto check_ctrlsig = [&](unsigned idx, ControlSig actual, const ControlSetConfig &ctrl, + SSOArray &used) { + // see if we have an already-matching signal available + for (unsigned i = 0; i < ctrl.routing.size(); i++) { + // doesn't route to this pin + if (((ctrl.routing.at(i) >> unsigned(idx)) & 0x1U) == 0) + continue; + if (used[i] == actual) + return true; + } + // see if we have a free slot available + for (unsigned i = 0; i < ctrl.routing.size(); i++) { + // doesn't route to this pin + if (((ctrl.routing.at(i) >> unsigned(idx)) & 0x1U) == 0) + continue; + if (used[i] != ControlSig()) + continue; + used[i] = actual; + return true; + } + // no option available + return false; + }; + for (unsigned z = 0; z < cfg.lc_per_clb; z++) { + // flipflop control set checking + if (cfg.split_lc) { + NPNR_ASSERT_FALSE("unimplemented!"); // TODO + } else { + NPNR_ASSERT(cfg.ff_per_lc == 1); // split-slice mode must be used for more + const CellInfo *lc = lc_comb[z]; + if (!lc) + continue; + auto &lct = cell_data.get(lc); + if (lct.ff.ff_used) { + // check shared control signals + if (!check_ctrlsig(z, lct.ff.clk, cfg.clk, used_clk)) + return false; + if (cfg.en.have_signal && !check_ctrlsig(z, lct.ff.en, cfg.en, used_en)) + return false; + if (cfg.sr.have_signal && !check_ctrlsig(z, lct.ff.sr, cfg.sr, used_sr)) + return false; + } + } + } + // TODO: other checks... + return true; +} + +bool BlockTracker::check_validity(BelId bel, const FabricConfig &cfg, const CellTagger &cell_data) +{ + if (bel.index >= int(bel_data.size())) + return true; // some kind of bel not being tracked + auto flags = bel_data.at(bel.index); + if (flags.block == BelFlags::BLOCK_OTHER) + return true; // no structures to update + Loc loc = ctx->getBelLocation(bel); + if (loc.y >= int(tiles.size())) + return true; // some kind of bel not being tracked + const auto &row = tiles.at(loc.y); + if (loc.x >= int(row.size())) + return true; // some kind of bel not being tracked + const auto &entry = row.at(loc.x); + if (flags.block == BelFlags::BLOCK_CLB) { + return entry.clb->check_validity(cfg.clb, cell_data); + } else { + return true; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/generic/viaduct/fabulous/validity_check.h b/generic/viaduct/fabulous/validity_check.h new file mode 100644 index 0000000000..43291f6eec --- /dev/null +++ b/generic/viaduct/fabulous/validity_check.h @@ -0,0 +1,127 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 gatecat + * + * 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 VALIDITY_CHECKING_H +#define VALIDITY_CHECKING_H + +#include "fab_cfg.h" +#include "fab_defs.h" +#include "sso_array.h" + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +// The validity checking engine for the fabulous configurable CLB + +// data that we tag onto cells for fast lookup, so we aren't doing slow hash map accesses in the inner-loop-critical +// validity checking code +struct ControlSig +{ + ControlSig() : net(), invert(false){}; + ControlSig(IdString net, bool invert) : net(net), invert(invert){}; + IdString net; + bool invert; + bool operator==(const ControlSig &other) const { return net == other.net && invert == other.invert; } + bool operator!=(const ControlSig &other) const { return net != other.net || invert != other.invert; } +}; + +struct CellTags +{ + struct + { + SSOArray lut_inputs; // for checking fracturable LUTs + bool carry_used = false; + const NetInfo *lut_out = nullptr; + // ... + } comb; // data for LUTs, or the LUT part of combined LUT+FF cells + struct + { + ControlSig clk, sr, en; + bool ff_used = false; + bool async = false; + bool latch = false; + const NetInfo *d = nullptr, *q = nullptr; + } ff; // data for FFs, or the FF part of combined LUT+FF cells +}; + +// map between cell and tags, using the flat_index that viaduct defines for this purpose +struct CellTagger +{ + std::vector data; + const CellTags &get(const CellInfo *ci) const { return data.at(ci->flat_index); } + void assign_for(const Context *ctx, const FabricConfig &cfg, const CellInfo *ci); +}; + +// we need to add some extra data to CLB bels to track what they do, so we can update CLBState accordingly +struct BelFlags +{ + enum BlockType : uint8_t + { + BLOCK_OTHER, + BLOCK_CLB, + // ... + } block = BlockType::BLOCK_OTHER; + enum FuncType : uint8_t + { + FUNC_LC_COMB, + FUNC_FF, + FUNC_MUX, + FUNC_OTHER, + } func; + uint8_t index; + // ... +}; + +// state of a CLB, for fast bel->cell lookup +// TODO: add valid/dirty tracking for incremental validity re-checking, important once we have bigger/more complex CLBs +// (cf. xilinx/intel arches in nextpnr) +struct CLBState +{ + explicit CLBState(const LogicConfig &cfg); + // In combined-LC mode (LC bel contains LUT and FF), this indexes the entire LC bel to cell. In separate mode, this + // indexes the combinational part (LUT or LUT+carry only). + std::unique_ptr lc_comb; + // In split-LC mode only, this maps FF bel (in CLB) index to cell + std::unique_ptr ff; + // If there is (a) separate mux bel(s), map them to cells + std::unique_ptr mux; + bool check_validity(const LogicConfig &cfg, const CellTagger &cell_data); +}; + +struct BlockTracker +{ + Context *ctx; + const FabricConfig &cfg; + std::vector bel_data; + + BlockTracker(Context *ctx, const FabricConfig &cfg) : ctx(ctx), cfg(cfg){}; + void set_bel_type(BelId bel, BelFlags::BlockType block, BelFlags::FuncType func, uint8_t index); + void update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell); + struct TileData + { + std::unique_ptr clb; + // ... + }; + std::vector> tiles; + bool check_validity(BelId bel, const FabricConfig &cfg, const CellTagger &cell_data); +}; + +NEXTPNR_NAMESPACE_END + +#endif From 0a8c411692629b4748673b11f8dca8c3db7552fb Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 14 Sep 2022 09:24:49 +0200 Subject: [PATCH 185/712] ice40: Fix UltraPlus BRAM clock polarity Signed-off-by: gatecat --- ice40/bitstream.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc index 3e50c06585..2e1a6d4e21 100644 --- a/ice40/bitstream.cc +++ b/ice40/bitstream.cc @@ -620,9 +620,13 @@ void write_asc(const Context *ctx, std::ostream &out) bool negclk_w = get_param_or_def(ctx, cell.second.get(), id_NEG_CLK_W); int write_mode = get_param_or_def(ctx, cell.second.get(), id_WRITE_MODE); int read_mode = get_param_or_def(ctx, cell.second.get(), id_READ_MODE); - set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w); - set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r); - + if (ctx->args.type == ArchArgs::UP5K || ctx->args.type == ArchArgs::UP3K) { + set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_r); + set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_w); + } else { + set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w); + set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r); + } set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1); set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2); set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1); From a72f898ff4c4237424c468044a6db9d6953b541e Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 14 Sep 2022 09:28:47 +0200 Subject: [PATCH 186/712] 3rdparty: Bump vendored pybind11 version for py3.11 support Signed-off-by: gatecat --- 3rdparty/pybind11/.appveyor.yml | 8 +- 3rdparty/pybind11/.clang-format | 38 + 3rdparty/pybind11/.clang-tidy | 82 +- 3rdparty/pybind11/.codespell-ignore-lines | 24 + 3rdparty/pybind11/.gitattributes | 1 + 3rdparty/pybind11/.github/CODEOWNERS | 9 + 3rdparty/pybind11/.github/CONTRIBUTING.md | 103 +- .../.github/ISSUE_TEMPLATE/bug-report.yml | 45 + .../.github/ISSUE_TEMPLATE/config.yml | 3 + 3rdparty/pybind11/.github/dependabot.yml | 4 - .../pybind11/.github/pull_request_template.md | 7 +- 3rdparty/pybind11/.github/workflows/ci.yml | 688 +++-- .../pybind11/.github/workflows/configure.yml | 16 +- .../pybind11/.github/workflows/format.yml | 23 +- 3rdparty/pybind11/.github/workflows/pip.yml | 39 +- .../pybind11/.github/workflows/upstream.yml | 112 + 3rdparty/pybind11/.gitignore | 2 + 3rdparty/pybind11/.pre-commit-config.yaml | 131 +- 3rdparty/pybind11/CMakeLists.txt | 78 +- 3rdparty/pybind11/MANIFEST.in | 1 - 3rdparty/pybind11/README.rst | 63 +- 3rdparty/pybind11/docs/Doxyfile | 4 +- 3rdparty/pybind11/docs/_static/css/custom.css | 3 + .../pybind11/docs/advanced/cast/custom.rst | 8 +- .../pybind11/docs/advanced/cast/eigen.rst | 6 +- .../pybind11/docs/advanced/cast/overview.rst | 181 +- 3rdparty/pybind11/docs/advanced/cast/stl.rst | 19 +- .../pybind11/docs/advanced/cast/strings.rst | 51 +- 3rdparty/pybind11/docs/advanced/classes.rst | 144 +- 3rdparty/pybind11/docs/advanced/embedding.rst | 27 +- .../pybind11/docs/advanced/exceptions.rst | 132 +- 3rdparty/pybind11/docs/advanced/functions.rst | 87 +- 3rdparty/pybind11/docs/advanced/misc.rst | 2 +- .../pybind11/docs/advanced/pycpp/numpy.rst | 67 +- .../pybind11/docs/advanced/pycpp/object.rst | 37 +- .../docs/advanced/pycpp/utilities.rst | 23 +- .../pybind11/docs/advanced/smart_ptrs.rst | 3 +- 3rdparty/pybind11/docs/basics.rst | 20 +- 3rdparty/pybind11/docs/benchmark.py | 33 +- 3rdparty/pybind11/docs/changelog.rst | 934 ++++++- 3rdparty/pybind11/docs/classes.rst | 59 +- 3rdparty/pybind11/docs/compiling.rst | 96 +- 3rdparty/pybind11/docs/conf.py | 40 +- 3rdparty/pybind11/docs/faq.rst | 52 +- 3rdparty/pybind11/docs/installing.rst | 4 +- 3rdparty/pybind11/docs/limitations.rst | 20 +- 3rdparty/pybind11/docs/pybind11-logo.png | Bin 58510 -> 61034 bytes 3rdparty/pybind11/docs/reference.rst | 15 +- 3rdparty/pybind11/docs/release.rst | 80 +- 3rdparty/pybind11/docs/requirements.txt | 13 +- 3rdparty/pybind11/docs/upgrade.rst | 46 +- 3rdparty/pybind11/include/pybind11/attr.h | 355 ++- .../pybind11/include/pybind11/buffer_info.h | 131 +- 3rdparty/pybind11/include/pybind11/cast.h | 2202 ++++++--------- 3rdparty/pybind11/include/pybind11/chrono.h | 172 +- 3rdparty/pybind11/include/pybind11/complex.h | 31 +- .../pybind11/include/pybind11/detail/class.h | 333 +-- .../pybind11/include/pybind11/detail/common.h | 1040 ++++--- .../pybind11/include/pybind11/detail/descr.h | 108 +- .../pybind11/include/pybind11/detail/init.h | 290 +- .../include/pybind11/detail/internals.h | 447 +++- .../pybind11/detail/type_caster_base.h | 1010 +++++++ .../pybind11/include/pybind11/detail/typeid.h | 30 +- 3rdparty/pybind11/include/pybind11/eigen.h | 546 ++-- 3rdparty/pybind11/include/pybind11/embed.h | 180 +- 3rdparty/pybind11/include/pybind11/eval.h | 112 +- .../pybind11/include/pybind11/functional.h | 72 +- 3rdparty/pybind11/include/pybind11/gil.h | 202 ++ 3rdparty/pybind11/include/pybind11/iostream.h | 143 +- 3rdparty/pybind11/include/pybind11/numpy.h | 1392 ++++++---- .../pybind11/include/pybind11/operators.h | 246 +- 3rdparty/pybind11/include/pybind11/options.h | 41 +- 3rdparty/pybind11/include/pybind11/pybind11.h | 2384 ++++++++++------- 3rdparty/pybind11/include/pybind11/pytypes.h | 1765 ++++++++---- 3rdparty/pybind11/include/pybind11/stl.h | 277 +- .../include/pybind11/stl/filesystem.h | 116 + 3rdparty/pybind11/include/pybind11/stl_bind.h | 674 +++-- 3rdparty/pybind11/noxfile.py | 107 + 3rdparty/pybind11/pybind11/__init__.py | 11 +- 3rdparty/pybind11/pybind11/__main__.py | 18 +- 3rdparty/pybind11/pybind11/_version.py | 6 +- 3rdparty/pybind11/pybind11/commands.py | 33 +- 3rdparty/pybind11/pybind11/setup_helpers.py | 278 +- 3rdparty/pybind11/pyproject.toml | 60 +- 3rdparty/pybind11/setup.cfg | 49 +- 3rdparty/pybind11/setup.py | 121 +- 3rdparty/pybind11/tests/CMakeLists.txt | 337 ++- 3rdparty/pybind11/tests/conftest.py | 31 +- 3rdparty/pybind11/tests/constructor_stats.h | 143 +- .../pybind11/tests/cross_module_gil_utils.cpp | 48 +- ...s_module_interleaved_error_already_set.cpp | 51 + 3rdparty/pybind11/tests/env.py | 20 +- .../tests/extra_python_package/test_files.py | 164 +- .../extra_setuptools/test_setuphelper.py | 68 +- 3rdparty/pybind11/tests/local_bindings.h | 52 +- 3rdparty/pybind11/tests/object.h | 112 +- .../tests/pybind11_cross_module_tests.cpp | 90 +- 3rdparty/pybind11/tests/pybind11_tests.cpp | 54 +- 3rdparty/pybind11/tests/pybind11_tests.h | 54 +- 3rdparty/pybind11/tests/pytest.ini | 13 +- 3rdparty/pybind11/tests/requirements.txt | 17 +- 3rdparty/pybind11/tests/test_async.cpp | 5 +- 3rdparty/pybind11/tests/test_async.py | 1 - 3rdparty/pybind11/tests/test_buffers.cpp | 134 +- 3rdparty/pybind11/tests/test_buffers.py | 23 +- .../pybind11/tests/test_builtin_casters.cpp | 337 ++- .../pybind11/tests/test_builtin_casters.py | 319 ++- .../pybind11/tests/test_call_policies.cpp | 44 +- 3rdparty/pybind11/tests/test_call_policies.py | 34 +- 3rdparty/pybind11/tests/test_callbacks.cpp | 181 +- 3rdparty/pybind11/tests/test_callbacks.py | 57 +- 3rdparty/pybind11/tests/test_chrono.cpp | 33 +- 3rdparty/pybind11/tests/test_chrono.py | 41 +- 3rdparty/pybind11/tests/test_class.cpp | 392 +-- 3rdparty/pybind11/tests/test_class.py | 23 +- .../tests/test_cmake_build/CMakeLists.txt | 7 +- .../pybind11/tests/test_cmake_build/embed.cpp | 8 +- .../installed_embed/CMakeLists.txt | 6 +- .../installed_function/CMakeLists.txt | 3 +- .../installed_target/CMakeLists.txt | 3 +- .../subdirectory_embed/CMakeLists.txt | 8 +- .../subdirectory_function/CMakeLists.txt | 5 +- .../subdirectory_target/CMakeLists.txt | 5 +- .../pybind11/tests/test_cmake_build/test.py | 6 +- 3rdparty/pybind11/tests/test_const_name.cpp | 55 + 3rdparty/pybind11/tests/test_const_name.py | 29 + .../tests/test_constants_and_functions.cpp | 116 +- .../tests/test_constants_and_functions.py | 12 +- 3rdparty/pybind11/tests/test_copy_move.cpp | 236 +- 3rdparty/pybind11/tests/test_copy_move.py | 13 +- .../tests/test_custom_type_casters.cpp | 174 +- .../tests/test_custom_type_casters.py | 12 +- .../pybind11/tests/test_custom_type_setup.cpp | 41 + .../pybind11/tests/test_custom_type_setup.py | 48 + .../pybind11/tests/test_docstring_options.cpp | 63 +- .../pybind11/tests/test_docstring_options.py | 4 +- 3rdparty/pybind11/tests/test_eigen.cpp | 269 +- 3rdparty/pybind11/tests/test_eigen.py | 91 +- .../pybind11/tests/test_embed/CMakeLists.txt | 8 +- 3rdparty/pybind11/tests/test_embed/catch.cpp | 29 +- .../tests/test_embed/external_module.cpp | 11 +- .../tests/test_embed/test_interpreter.cpp | 180 +- .../tests/test_embed/test_interpreter.py | 8 +- .../tests/test_embed/test_trampoline.py | 16 + 3rdparty/pybind11/tests/test_enum.cpp | 106 +- 3rdparty/pybind11/tests/test_enum.py | 69 +- 3rdparty/pybind11/tests/test_eval.cpp | 53 +- 3rdparty/pybind11/tests/test_eval.py | 21 +- 3rdparty/pybind11/tests/test_eval_call.py | 1 - 3rdparty/pybind11/tests/test_exceptions.cpp | 232 +- 3rdparty/pybind11/tests/test_exceptions.h | 13 + 3rdparty/pybind11/tests/test_exceptions.py | 196 +- .../tests/test_factory_constructors.cpp | 323 ++- .../tests/test_factory_constructors.py | 24 +- 3rdparty/pybind11/tests/test_gil_scoped.cpp | 49 +- 3rdparty/pybind11/tests/test_gil_scoped.py | 1 - 3rdparty/pybind11/tests/test_iostream.cpp | 95 +- 3rdparty/pybind11/tests/test_iostream.py | 155 +- .../tests/test_kwargs_and_defaults.cpp | 282 +- .../tests/test_kwargs_and_defaults.py | 123 +- .../pybind11/tests/test_local_bindings.cpp | 47 +- .../pybind11/tests/test_local_bindings.py | 6 +- .../tests/test_methods_and_attributes.cpp | 322 ++- .../tests/test_methods_and_attributes.py | 94 +- 3rdparty/pybind11/tests/test_modules.cpp | 72 +- 3rdparty/pybind11/tests/test_modules.py | 52 +- .../tests/test_multiple_inheritance.cpp | 277 +- .../tests/test_multiple_inheritance.py | 148 +- 3rdparty/pybind11/tests/test_numpy_array.cpp | 470 ++-- 3rdparty/pybind11/tests/test_numpy_array.py | 74 +- 3rdparty/pybind11/tests/test_numpy_dtypes.cpp | 399 ++- 3rdparty/pybind11/tests/test_numpy_dtypes.py | 71 +- .../pybind11/tests/test_numpy_vectorize.cpp | 94 +- .../pybind11/tests/test_numpy_vectorize.py | 2 +- 3rdparty/pybind11/tests/test_opaque_types.cpp | 20 +- 3rdparty/pybind11/tests/test_opaque_types.py | 8 +- .../tests/test_operator_overloading.cpp | 198 +- .../tests/test_operator_overloading.py | 15 +- 3rdparty/pybind11/tests/test_pickling.cpp | 110 +- 3rdparty/pybind11/tests/test_pickling.py | 60 +- 3rdparty/pybind11/tests/test_pytypes.cpp | 671 ++++- 3rdparty/pybind11/tests/test_pytypes.py | 517 +++- .../tests/test_sequences_and_iterators.cpp | 420 ++- .../tests/test_sequences_and_iterators.py | 78 +- 3rdparty/pybind11/tests/test_smart_ptr.cpp | 512 ++-- 3rdparty/pybind11/tests/test_smart_ptr.py | 29 +- 3rdparty/pybind11/tests/test_stl.cpp | 403 ++- 3rdparty/pybind11/tests/test_stl.py | 123 +- 3rdparty/pybind11/tests/test_stl_binders.cpp | 111 +- 3rdparty/pybind11/tests/test_stl_binders.py | 48 +- .../tests/test_tagbased_polymorphic.cpp | 117 +- .../tests/test_tagbased_polymorphic.py | 1 - 3rdparty/pybind11/tests/test_thread.cpp | 66 + 3rdparty/pybind11/tests/test_thread.py | 42 + 3rdparty/pybind11/tests/test_union.py | 1 - .../pybind11/tests/test_virtual_functions.cpp | 341 ++- .../pybind11/tests/test_virtual_functions.py | 78 +- .../pybind11/tests/valgrind-numpy-scipy.supp | 140 + 3rdparty/pybind11/tests/valgrind-python.supp | 117 + 3rdparty/pybind11/tools/FindCatch.cmake | 2 + 3rdparty/pybind11/tools/FindEigen3.cmake | 3 + .../pybind11/tools/FindPythonLibsNew.cmake | 58 +- 3rdparty/pybind11/tools/JoinPaths.cmake | 23 + 3rdparty/pybind11/tools/check-style.sh | 6 +- .../codespell_ignore_lines_from_errors.py | 35 + 3rdparty/pybind11/tools/libsize.py | 6 +- 3rdparty/pybind11/tools/make_changelog.py | 63 + 3rdparty/pybind11/tools/pybind11.pc.in | 7 + 3rdparty/pybind11/tools/pybind11Common.cmake | 86 +- .../pybind11/tools/pybind11Config.cmake.in | 19 +- .../pybind11/tools/pybind11NewTools.cmake | 104 +- 3rdparty/pybind11/tools/pybind11Tools.cmake | 125 +- 3rdparty/pybind11/tools/setup_global.py.in | 14 +- 3rdparty/pybind11/tools/setup_main.py.in | 12 +- 214 files changed, 21644 insertions(+), 10025 deletions(-) create mode 100644 3rdparty/pybind11/.clang-format create mode 100644 3rdparty/pybind11/.codespell-ignore-lines create mode 100644 3rdparty/pybind11/.gitattributes create mode 100644 3rdparty/pybind11/.github/CODEOWNERS create mode 100644 3rdparty/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 3rdparty/pybind11/.github/workflows/upstream.yml create mode 100644 3rdparty/pybind11/docs/_static/css/custom.css create mode 100644 3rdparty/pybind11/include/pybind11/detail/type_caster_base.h create mode 100644 3rdparty/pybind11/include/pybind11/gil.h create mode 100644 3rdparty/pybind11/include/pybind11/stl/filesystem.h create mode 100644 3rdparty/pybind11/noxfile.py create mode 100644 3rdparty/pybind11/tests/cross_module_interleaved_error_already_set.cpp create mode 100644 3rdparty/pybind11/tests/test_const_name.cpp create mode 100644 3rdparty/pybind11/tests/test_const_name.py create mode 100644 3rdparty/pybind11/tests/test_custom_type_setup.cpp create mode 100644 3rdparty/pybind11/tests/test_custom_type_setup.py create mode 100644 3rdparty/pybind11/tests/test_embed/test_trampoline.py create mode 100644 3rdparty/pybind11/tests/test_exceptions.h create mode 100644 3rdparty/pybind11/tests/test_thread.cpp create mode 100644 3rdparty/pybind11/tests/test_thread.py create mode 100644 3rdparty/pybind11/tests/valgrind-numpy-scipy.supp create mode 100644 3rdparty/pybind11/tests/valgrind-python.supp create mode 100644 3rdparty/pybind11/tools/JoinPaths.cmake create mode 100644 3rdparty/pybind11/tools/codespell_ignore_lines_from_errors.py create mode 100755 3rdparty/pybind11/tools/make_changelog.py create mode 100644 3rdparty/pybind11/tools/pybind11.pc.in diff --git a/3rdparty/pybind11/.appveyor.yml b/3rdparty/pybind11/.appveyor.yml index 149a8a3dc9..360760ac8d 100644 --- a/3rdparty/pybind11/.appveyor.yml +++ b/3rdparty/pybind11/.appveyor.yml @@ -1,6 +1,6 @@ version: 1.0.{build} image: -- Visual Studio 2015 +- Visual Studio 2017 test: off skip_branch_with_pr: true build: @@ -11,15 +11,13 @@ environment: matrix: - PYTHON: 36 CONFIG: Debug - - PYTHON: 27 - CONFIG: Debug install: - ps: | - $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + $env:CMAKE_GENERATOR = "Visual Studio 15 2017" if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" python -W ignore -m pip install --upgrade pip wheel - python -W ignore -m pip install pytest numpy --no-warn-script-location + python -W ignore -m pip install pytest numpy --no-warn-script-location pytest-timeout - ps: | Start-FileDownload 'https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.zip' 7z x eigen-3.3.7.zip -y > $null diff --git a/3rdparty/pybind11/.clang-format b/3rdparty/pybind11/.clang-format new file mode 100644 index 0000000000..b477a16037 --- /dev/null +++ b/3rdparty/pybind11/.clang-format @@ -0,0 +1,38 @@ +--- +# See all possible options and defaults with: +# clang-format --style=llvm --dump-config +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AllowShortLambdasOnASingleLine: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: All +BreakConstructorInitializers: BeforeColon +ColumnLimit: 99 +CommentPragmas: 'NOLINT:.*|^ IWYU pragma:' +IncludeBlocks: Regroup +IndentCaseLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +Language: Cpp +SpaceAfterCStyleCast: true +Standard: Cpp11 +StatementMacros: ['PyObject_HEAD'] +TabWidth: 4 +IncludeCategories: + - Regex: '' + Priority: 4 + - Regex: '.*' + Priority: 5 +... diff --git a/3rdparty/pybind11/.clang-tidy b/3rdparty/pybind11/.clang-tidy index e29d929897..23018386c1 100644 --- a/3rdparty/pybind11/.clang-tidy +++ b/3rdparty/pybind11/.clang-tidy @@ -1,13 +1,77 @@ FormatStyle: file -Checks: ' -llvm-namespace-comment, -modernize-use-override, -readability-container-size-empty, -modernize-use-using, -modernize-use-equals-default, -modernize-use-auto, -modernize-use-emplace, -' +Checks: | + *bugprone*, + *performance*, + clang-analyzer-optin.cplusplus.VirtualCall, + clang-analyzer-optin.performance.Padding, + cppcoreguidelines-init-variables, + cppcoreguidelines-prefer-member-initializer, + cppcoreguidelines-pro-type-static-cast-downcast, + cppcoreguidelines-slicing, + google-explicit-constructor, + llvm-namespace-comment, + misc-definitions-in-headers, + misc-misplaced-const, + misc-non-copyable-objects, + misc-static-assert, + misc-throw-by-value-catch-by-reference, + misc-uniqueptr-reset-release, + misc-unused-parameters, + modernize-avoid-bind, + modernize-loop-convert, + modernize-make-shared, + modernize-redundant-void-arg, + modernize-replace-auto-ptr, + modernize-replace-disallow-copy-and-assign-macro, + modernize-replace-random-shuffle, + modernize-shrink-to-fit, + modernize-use-auto, + modernize-use-bool-literals, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-equals-default, + modernize-use-equals-delete, + modernize-use-noexcept, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + readability-avoid-const-params-in-decls, + readability-braces-around-statements, + readability-const-return-type, + readability-container-size-empty, + readability-delete-null-pointer, + readability-else-after-return, + readability-implicit-bool-conversion, + readability-inconsistent-declaration-parameter-name, + readability-make-member-function-const, + readability-misplaced-array-index, + readability-non-const-parameter, + readability-qualified-auto, + readability-redundant-function-ptr-dereference, + readability-redundant-smartptr-get, + readability-redundant-string-cstr, + readability-simplify-subscript-expr, + readability-static-accessed-through-instance, + readability-static-definition-in-anonymous-namespace, + readability-string-compare, + readability-suspicious-call-argument, + readability-uniqueptr-delete-release, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-reserved-identifier, + -bugprone-unused-raii, + +CheckOptions: +- key: modernize-use-equals-default.IgnoreMacros + value: false +- key: performance-for-range-copy.WarnOnAllAutoCopies + value: true +- key: performance-inefficient-string-concatenation.StrictMode + value: true +- key: performance-unnecessary-value-param.AllowedTypes + value: 'exception_ptr$;' +- key: readability-implicit-bool-conversion.AllowPointerConditions + value: true HeaderFilterRegex: 'pybind11/.*h' diff --git a/3rdparty/pybind11/.codespell-ignore-lines b/3rdparty/pybind11/.codespell-ignore-lines new file mode 100644 index 0000000000..2a01d63ebb --- /dev/null +++ b/3rdparty/pybind11/.codespell-ignore-lines @@ -0,0 +1,24 @@ +template + template + auto &this_ = static_cast(*this); + if (load_impl(temp, false)) { + ssize_t nd = 0; + auto trivial = broadcast(buffers, nd, shape); + auto ndim = (size_t) nd; + int nd; + ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; } + using op = op_impl; +template + template + class_ &def(const detail::op_ &op, const Extra &...extra) { + class_ &def_cast(const detail::op_ &op, const Extra &...extra) { +@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) +struct IntStruct { + explicit IntStruct(int v) : value(v){}; + ~IntStruct() { value = -value; } + IntStruct(const IntStruct &) = default; + IntStruct &operator=(const IntStruct &) = default; + py::class_(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); })); + py::implicitly_convertible(); + m.def("test", [](int expected, const IntStruct &in) { + [](int expected, const IntStruct &in) { diff --git a/3rdparty/pybind11/.gitattributes b/3rdparty/pybind11/.gitattributes new file mode 100644 index 0000000000..d611e1496d --- /dev/null +++ b/3rdparty/pybind11/.gitattributes @@ -0,0 +1 @@ +docs/*.svg binary diff --git a/3rdparty/pybind11/.github/CODEOWNERS b/3rdparty/pybind11/.github/CODEOWNERS new file mode 100644 index 0000000000..4e2c66902e --- /dev/null +++ b/3rdparty/pybind11/.github/CODEOWNERS @@ -0,0 +1,9 @@ +*.cmake @henryiii +CMakeLists.txt @henryiii +*.yml @henryiii +*.yaml @henryiii +/tools/ @henryiii +/pybind11/ @henryiii +noxfile.py @henryiii +.clang-format @henryiii +.clang-tidy @henryiii diff --git a/3rdparty/pybind11/.github/CONTRIBUTING.md b/3rdparty/pybind11/.github/CONTRIBUTING.md index 4ced21baaa..00b1fea4cf 100644 --- a/3rdparty/pybind11/.github/CONTRIBUTING.md +++ b/3rdparty/pybind11/.github/CONTRIBUTING.md @@ -53,6 +53,33 @@ derivative works thereof, in binary and source code form. ## Development of pybind11 +### Quick setup + +To setup a quick development environment, use [`nox`](https://nox.thea.codes). +This will allow you to do some common tasks with minimal setup effort, but will +take more time to run and be less flexible than a full development environment. +If you use [`pipx run nox`](https://pipx.pypa.io), you don't even need to +install `nox`. Examples: + +```bash +# List all available sessions +nox -l + +# Run linters +nox -s lint + +# Run tests on Python 3.9 +nox -s tests-3.9 + +# Build and preview docs +nox -s docs -- serve + +# Build SDists and wheels +nox -s build +``` + +### Full setup + To setup an ideal development environment, run the following commands on a system with CMake 3.14+: @@ -66,11 +93,10 @@ cmake --build build -j4 Tips: -* You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3 - only). +* You can use `virtualenv` (faster, from PyPI) instead of `venv`. * You can select any name for your environment folder; if it contains "env" it will be ignored by git. -* If you don’t have CMake 3.14+, just add “cmake” to the pip install command. +* If you don't have CMake 3.14+, just add "cmake" to the pip install command. * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. FindPython uses `-DPython_ROOT_DIR=/path/to` or @@ -78,7 +104,7 @@ Tips: ### Configuration options -In CMake, configuration options are given with “-D”. Options are stored in the +In CMake, configuration options are given with "-D". Options are stored in the build directory, in the `CMakeCache.txt` file, so they are remembered for each build directory. Two selections are special - the generator, given with `-G`, and the compiler, which is selected based on environment variables `CXX` and @@ -88,12 +114,12 @@ after the initial run. The valid options are: * `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo -* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+’s FindPython instead of the +* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+'s FindPython instead of the classic, deprecated, custom FindPythonLibs * `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests) * `-DBUILD_TESTING=ON`: Enable the tests * `-DDOWNLOAD_CATCH=ON`: Download catch to build the C++ tests -* `-DOWNLOAD_EIGEN=ON`: Download Eigen for the NumPy tests +* `-DDOWNLOAD_EIGEN=ON`: Download Eigen for the NumPy tests * `-DPYBIND11_INSTALL=ON/OFF`: Enable the install target (on by default for the master project) * `-DUSE_PYTHON_INSTALL_DIR=ON`: Try to install into the python dir @@ -126,13 +152,26 @@ cmake --build build --target check `--target` can be spelled `-t` in CMake 3.15+. You can also run individual tests with these targets: -* `pytest`: Python tests only +* `pytest`: Python tests only, using the +[pytest](https://docs.pytest.org/en/stable/) framework * `cpptest`: C++ tests only * `test_cmake_build`: Install / subdirectory tests If you want to build just a subset of tests, use -`-DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp"`. If this is -empty, all tests will be built. +`-DPYBIND11_TEST_OVERRIDE="test_callbacks;test_pickling"`. If this is +empty, all tests will be built. Tests are specified without an extension if they need both a .py and +.cpp file. + +You may also pass flags to the `pytest` target by editing `tests/pytest.ini` or +by using the `PYTEST_ADDOPTS` environment variable +(see [`pytest` docs](https://docs.pytest.org/en/2.7.3/customize.html#adding-default-options)). As an example: + +```bash +env PYTEST_ADDOPTS="--capture=no --exitfirst" \ + cmake --build build --target pytest +# Or using abbreviated flags +env PYTEST_ADDOPTS="-s -x" cmake --build build --target pytest +``` ### Formatting @@ -164,18 +203,46 @@ name, pre-commit): pre-commit install ``` +### Clang-Format + +As of v2.6.2, pybind11 ships with a [`clang-format`][clang-format] +configuration file at the top level of the repo (the filename is +`.clang-format`). Currently, formatting is NOT applied automatically, but +manually using `clang-format` for newly developed files is highly encouraged. +To check if a file needs formatting: + +```bash +clang-format -style=file --dry-run some.cpp +``` + +The output will show things to be fixed, if any. To actually format the file: + +```bash +clang-format -style=file -i some.cpp +``` + +Note that the `-style-file` option searches the parent directories for the +`.clang-format` file, i.e. the commands above can be run in any subdirectory +of the pybind11 repo. + ### Clang-Tidy -To run Clang tidy, the following recipe should work. Files will be modified in -place, so you can use git to monitor the changes. +[`clang-tidy`][clang-tidy] performs deeper static code analyses and is +more complex to run, compared to `clang-format`, but support for `clang-tidy` +is built into the pybind11 CMake configuration. To run `clang-tidy`, the +following recipe should work. Run the `docker` command from the top-level +directory inside your pybind11 git clone. Files will be modified in place, +so you can use git to monitor the changes. ```bash -docker run --rm -v $PWD:/pybind11 -it silkeh/clang:10 -apt-get update && apt-get install python3-dev python3-pytest -cmake -S pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-fix" -cmake --build build +docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:13 +apt-get update && apt-get install -y python3-dev python3-pytest +cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 +cmake --build build -j 2 ``` +You can add `--fix` to the options list if you want. + ### Include what you use To run include what you use, install (`brew install include-what-you-use` on @@ -186,12 +253,12 @@ cmake -S . -B build-iwyu -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=$(which include-what-y cmake --build build ``` -The report is sent to stderr; you can pip it into a file if you wish. +The report is sent to stderr; you can pipe it into a file if you wish. ### Build recipes This builds with the Intel compiler (assuming it is in your path, along with a -recent CMake and Python 3): +recent CMake and Python): ```bash python3 -m venv venv @@ -313,6 +380,8 @@ if you really want to. [pre-commit]: https://pre-commit.com +[clang-format]: https://clang.llvm.org/docs/ClangFormat.html +[clang-tidy]: https://clang.llvm.org/extra/clang-tidy/ [pybind11.readthedocs.org]: http://pybind11.readthedocs.org/en/latest [issue tracker]: https://github.com/pybind/pybind11/issues [gitter]: https://gitter.im/pybind/Lobby diff --git a/3rdparty/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml b/3rdparty/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000000..bd6a9a8e22 --- /dev/null +++ b/3rdparty/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,45 @@ +name: Bug Report +description: File an issue about a bug +title: "[BUG]: " +labels: [triage] +body: + - type: markdown + attributes: + value: | + Maintainers will only make a best effort to triage PRs. Please do your best to make the issue as easy to act on as possible, and only open if clearly a problem with pybind11 (ask first if unsure). + - type: checkboxes + id: steps + attributes: + label: Required prerequisites + description: Make sure you've completed the following steps before submitting your issue -- thank you! + options: + - label: Make sure you've read the [documentation](https://pybind11.readthedocs.io). Your issue may be addressed there. + required: true + - label: Search the [issue tracker](https://github.com/pybind/pybind11/issues) and [Discussions](https:/pybind/pybind11/discussions) to verify that this hasn't already been reported. +1 or comment there if it has. + required: true + - label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new). + required: false + + - type: textarea + id: description + attributes: + label: Problem description + placeholder: >- + Provide a short description, state the expected behavior and what + actually happens. Include relevant information like what version of + pybind11 you are using, what system you are on, and any useful commands + / output. + validations: + required: true + + - type: textarea + id: code + attributes: + label: Reproducible example code + placeholder: >- + The code should be minimal, have no external dependencies, isolate the + function(s) that cause breakage. Submit matched and complete C++ and + Python snippets that can be easily compiled and run to diagnose the + issue. If possible, make a PR with a new, failing test to give us a + starting point to work on! + render: text diff --git a/3rdparty/pybind11/.github/ISSUE_TEMPLATE/config.yml b/3rdparty/pybind11/.github/ISSUE_TEMPLATE/config.yml index 20e743136f..27f9a80441 100644 --- a/3rdparty/pybind11/.github/ISSUE_TEMPLATE/config.yml +++ b/3rdparty/pybind11/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Ask a question + url: https://github.com/pybind/pybind11/discussions/new + about: Please ask and answer questions here, or propose new ideas. - name: Gitter room url: https://gitter.im/pybind/Lobby about: A room for discussing pybind11 with an active community diff --git a/3rdparty/pybind11/.github/dependabot.yml b/3rdparty/pybind11/.github/dependabot.yml index c1eac3c443..2c7d170839 100644 --- a/3rdparty/pybind11/.github/dependabot.yml +++ b/3rdparty/pybind11/.github/dependabot.yml @@ -5,7 +5,3 @@ updates: directory: "/" schedule: interval: "daily" - ignore: - # Offical actions have moving tags like v1 - # that are used, so they don't need updates here - - dependency-name: "actions/*" diff --git a/3rdparty/pybind11/.github/pull_request_template.md b/3rdparty/pybind11/.github/pull_request_template.md index 5570f6f28b..54b7f5100d 100644 --- a/3rdparty/pybind11/.github/pull_request_template.md +++ b/3rdparty/pybind11/.github/pull_request_template.md @@ -1,3 +1,7 @@ + ## Description @@ -5,7 +9,8 @@ ## Suggested changelog entry: - + ```rst diff --git a/3rdparty/pybind11/.github/workflows/ci.yml b/3rdparty/pybind11/.github/workflows/ci.yml index 73424f92da..213c690050 100644 --- a/3rdparty/pybind11/.github/workflows/ci.yml +++ b/3rdparty/pybind11/.github/workflows/ci.yml @@ -9,6 +9,15 @@ on: - stable - v* +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + +env: + PIP_ONLY_BINARY: numpy + FORCE_COLOR: 3 + PYTEST_TIMEOUT: 300 + jobs: # This is the "main" test suite, which tests a large number of different # versions of default compilers and Python versions in GitHub Actions. @@ -16,74 +25,66 @@ jobs: strategy: fail-fast: false matrix: - runs-on: [ubuntu-latest, windows-latest, macos-latest] + runs-on: [ubuntu-latest, windows-2022, macos-latest] python: - - 2.7 - - 3.5 - - 3.6 - - 3.7 - - 3.8 - - 3.9 - # - 3.10.0-alpha.1 - need next release for pybind11 fix - - pypy2 - - pypy3 + - '3.6' + - '3.9' + - '3.10' + - '3.11-dev' + - 'pypy-3.7' + - 'pypy-3.8' + - 'pypy-3.9' # Items in here will either be added to the build matrix (if not # present), or add new keys to an existing matrix element if all the # existing keys match. # - # We support three optional keys: args (both build), args1 (first - # build), and args2 (second build). + # We support an optional key: args, for cmake args include: # Just add a key - runs-on: ubuntu-latest - python: 3.6 + python: '3.6' args: > -DPYBIND11_FINDPYTHON=ON - - runs-on: windows-latest - python: 3.6 + -DCMAKE_CXX_FLAGS="-D_=1" + - runs-on: ubuntu-latest + python: 'pypy-3.8' args: > -DPYBIND11_FINDPYTHON=ON - - runs-on: ubuntu-latest - python: 3.8 + - runs-on: windows-2019 + python: '3.6' args: > -DPYBIND11_FINDPYTHON=ON - - # These items will be removed from the build matrix, keys must match. - exclude: - # Currently 32bit only, and we build 64bit - - runs-on: windows-latest - python: pypy2 - - runs-on: windows-latest - python: pypy3 - - # Let's drop a few macOS runs since that tends to be 2.7 or 3.8+ - - runs-on: macos-latest - python: 3.6 - - runs-on: macos-latest - python: 3.7 + # Inject a couple Windows 2019 runs + - runs-on: windows-2019 + python: '3.9' name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - name: Setup Boost (Windows / Linux latest) - shell: bash - run: echo "BOOST_ROOT=$BOOST_ROOT_1_72_0" >> $GITHUB_ENV + - name: Setup Boost (Linux) + # Can't use boost + define _ + if: runner.os == 'Linux' && matrix.python != '3.6' + run: sudo apt-get install libboost-dev + + - name: Setup Boost (macOS) + if: runner.os == 'macOS' + run: brew install boost - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.12 - name: Cache wheels if: runner.os == 'macOS' - uses: actions/cache@v2 + uses: actions/cache@v3 with: # This path is specific to macOS - we really only need it for PyPy NumPy wheels # See https://github.com/actions/cache/blob/master/examples.md#python---pip @@ -93,7 +94,8 @@ jobs: key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }} - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Setup annotations on Linux if: runner.os == 'Linux' @@ -117,7 +119,7 @@ jobs: - name: C++11 tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11-dev' || matrix.python == 'pypy-3.8'))" run: cmake --build . --target cpptest -j 2 - name: Interface test C++11 @@ -127,7 +129,7 @@ jobs: run: git clean -fdx # Second build - C++17 mode and in a build directory - - name: Configure ${{ matrix.args2 }} + - name: Configure C++17 run: > cmake -S . -B build2 -DPYBIND11_WERROR=ON @@ -135,7 +137,6 @@ jobs: -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=17 ${{ matrix.args }} - ${{ matrix.args2 }} - name: Build run: cmake --build build2 -j 2 @@ -145,32 +146,117 @@ jobs: - name: C++ tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11-dev' || matrix.python == 'pypy-3.8'))" run: cmake --build build2 --target cpptest - - name: Interface test - run: cmake --build build2 --target test_cmake_build + # Third build - C++17 mode with unstable ABI + - name: Configure (unstable ABI) + run: > + cmake -S . -B build3 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + -DPYBIND11_INTERNALS_VERSION=10000000 + "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" + ${{ matrix.args }} - # Eventually Microsoft might have an action for setting up - # MSVC, but for now, this action works: - - name: Prepare compiler environment for Windows 🐍 2.7 - if: matrix.python == 2.7 && runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 + - name: Build (unstable ABI) + run: cmake --build build3 -j 2 - # This makes two environment variables available in the following step(s) - - name: Set Windows 🐍 2.7 environment variables - if: matrix.python == 2.7 && runner.os == 'Windows' - shell: bash - run: | - echo "DISTUTILS_USE_SDK=1" >> $GITHUB_ENV - echo "MSSdk=1" >> $GITHUB_ENV + - name: Python tests (unstable ABI) + run: cmake --build build3 --target pytest + + - name: Interface test + run: cmake --build build2 --target test_cmake_build # This makes sure the setup_helpers module can build packages using # setuptools - name: Setuptools helpers test run: pytest tests/extra_setuptools + if: "!(matrix.runs-on == 'windows-2022')" + + + deadsnakes: + strategy: + fail-fast: false + matrix: + include: + # TODO: Fails on 3.10, investigate + - python-version: "3.9" + python-debug: true + valgrind: true + - python-version: "3.11-dev" + python-debug: false + + name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup Python ${{ matrix.python-version }} (deadsnakes) + uses: deadsnakes/action@v2.1.1 + with: + python-version: ${{ matrix.python-version }} + debug: ${{ matrix.python-debug }} + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.12 + + - name: Valgrind cache + if: matrix.valgrind + uses: actions/cache@v3 + id: cache-valgrind + with: + path: valgrind + key: 3.16.1 # Valgrind version + + - name: Compile Valgrind + if: matrix.valgrind && steps.cache-valgrind.outputs.cache-hit != 'true' + run: | + VALGRIND_VERSION=3.16.1 + curl https://sourceware.org/pub/valgrind/valgrind-$VALGRIND_VERSION.tar.bz2 -o - | tar xj + mv valgrind-$VALGRIND_VERSION valgrind + cd valgrind + ./configure + make -j 2 > /dev/null + + - name: Install Valgrind + if: matrix.valgrind + working-directory: valgrind + run: | + sudo make install + sudo apt-get update + sudo apt-get install libc6-dbg # Needed by Valgrind + + - name: Prepare env + run: | + python -m pip install -r tests/requirements.txt + + - name: Configure + env: + SETUPTOOLS_USE_DISTUTILS: stdlib + run: > + cmake -S . -B build + -DCMAKE_BUILD_TYPE=Debug + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + + - name: Build + run: cmake --build build -j 2 + + - name: Python tests + run: cmake --build build --target pytest + + - name: C++ tests + run: cmake --build build --target cpptest + + - name: Run Valgrind on Python tests + if: matrix.valgrind + run: cmake --build build --target memcheck # Testing on clang using the excellent silkeh clang docker images @@ -195,12 +281,20 @@ jobs: std: 20 - clang: 10 std: 17 + - clang: 11 + std: 20 + - clang: 12 + std: 20 + - clang: 13 + std: 20 + - clang: 14 + std: 20 name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" container: "silkeh/clang:${{ matrix.clang }}" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add wget and python3 run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev @@ -230,11 +324,11 @@ jobs: # Testing NVCC; forces sources to behave like .cu files cuda: runs-on: ubuntu-latest - name: "🐍 3.8 • CUDA 11 • Ubuntu 20.04" - container: nvidia/cuda:11.0-devel-ubuntu20.04 + name: "🐍 3.8 • CUDA 11.2 • Ubuntu 20.04" + container: nvidia/cuda:11.2.2-devel-ubuntu20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND - name: Install 🐍 3 @@ -250,70 +344,74 @@ jobs: run: cmake --build build --target pytest - # Testing CentOS 8 + PGI compilers - centos-nvhpc8: - runs-on: ubuntu-latest - name: "🐍 3 • CentOS8 / PGI 20.7 • x64" - container: centos:8 - - steps: - - uses: actions/checkout@v2 - - - name: Add Python 3 and a few requirements - run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules - - - name: Install CMake with pip - run: | - python3 -m pip install --upgrade pip - python3 -m pip install cmake --prefer-binary - - - name: Install NVidia HPC SDK - run: yum -y install https://developer.download.nvidia.com/hpc-sdk/nvhpc-20-7-20.7-1.x86_64.rpm https://developer.download.nvidia.com/hpc-sdk/nvhpc-2020-20.7-1.x86_64.rpm - - - name: Configure - shell: bash - run: | - source /etc/profile.d/modules.sh - module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.7 - cmake -S . -B build -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=14 -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - - - name: Build - run: cmake --build build -j 2 --verbose - - - name: Python tests - run: cmake --build build --target pytest - - - name: C++ tests - run: cmake --build build --target cpptest - - - name: Interface test - run: cmake --build build --target test_cmake_build +# TODO: Internal compiler error - report to NVidia +# # Testing CentOS 8 + PGI compilers +# centos-nvhpc8: +# runs-on: ubuntu-latest +# name: "🐍 3 • CentOS8 / PGI 20.11 • x64" +# container: centos:8 +# +# steps: +# - uses: actions/checkout@v3 +# +# - name: Add Python 3 and a few requirements +# run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules +# +# - name: Install CMake with pip +# run: | +# python3 -m pip install --upgrade pip +# python3 -m pip install cmake --prefer-binary +# +# - name: Install NVidia HPC SDK +# run: > +# yum -y install +# https://developer.download.nvidia.com/hpc-sdk/20.11/nvhpc-20-11-20.11-1.x86_64.rpm +# https://developer.download.nvidia.com/hpc-sdk/20.11/nvhpc-2020-20.11-1.x86_64.rpm +# +# - name: Configure +# shell: bash +# run: | +# source /etc/profile.d/modules.sh +# module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.11 +# cmake -S . -B build -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=14 -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") +# +# - name: Build +# run: cmake --build build -j 2 --verbose +# +# - name: Python tests +# run: cmake --build build --target pytest +# +# - name: C++ tests +# run: cmake --build build --target cpptest +# +# - name: Interface test +# run: cmake --build build --target test_cmake_build # Testing on CentOS 7 + PGI compilers, which seems to require more workarounds centos-nvhpc7: runs-on: ubuntu-latest - name: "🐍 3 • CentOS7 / PGI 20.9 • x64" + name: "🐍 3 • CentOS7 / PGI 22.3 • x64" container: centos:7 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add Python 3 and a few requirements - run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 + run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 yum-utils - name: Install NVidia HPC SDK - run: yum -y install https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-20-9-20.9-1.x86_64.rpm https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-2020-20.9-1.x86_64.rpm + run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.3 # On CentOS 7, we have to filter a few tests (compiler internal error) - # and allow deeper templete recursion (not needed on CentOS 8 with a newer + # and allow deeper template recursion (not needed on CentOS 8 with a newer # standard library). On some systems, you many need further workarounds: # https://github.com/pybind/pybind11/pull/2475 - name: Configure shell: bash run: | source /etc/profile.d/modules.sh - module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.9 + module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.3 cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \ -DCMAKE_CXX_STANDARD=11 \ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \ @@ -338,26 +436,27 @@ jobs: - name: Interface test run: cmake3 --build build --target test_cmake_build + # Testing on GCC using the GCC docker images (only recent images supported) gcc: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - gcc: - - 7 - - latest - std: - - 11 include: - - gcc: 10 - std: 20 + - { gcc: 7, std: 11 } + - { gcc: 7, std: 17 } + - { gcc: 8, std: 14 } + - { gcc: 8, std: 17 } + - { gcc: 10, std: 17 } + - { gcc: 11, std: 20 } + - { gcc: 12, std: 20 } name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64" container: "gcc:${{ matrix.gcc }}" steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Add Python 3 run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev @@ -365,10 +464,8 @@ jobs: - name: Update pip run: python3 -m pip install --upgrade pip - - name: Setup CMake 3.18 - uses: jwlawson/actions-setup-cmake@v1.4 - with: - cmake-version: 3.18 + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.12 - name: Configure shell: bash @@ -392,6 +489,103 @@ jobs: run: cmake --build build --target test_cmake_build + # Testing on ICC using the oneAPI apt repo + icc: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + + name: "🐍 3 • ICC latest • x64" + + steps: + - uses: actions/checkout@v3 + + - name: Add apt repo + run: | + sudo apt-get update + sudo apt-get install -y wget build-essential pkg-config cmake ca-certificates gnupg + wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB + echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list + + - name: Add ICC & Python 3 + run: sudo apt-get update; sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic cmake python3-dev python3-numpy python3-pytest python3-pip + + - name: Update pip + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + python3 -m pip install --upgrade pip + + - name: Install dependencies + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + python3 -m pip install -r tests/requirements.txt + + - name: Configure C++11 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake -S . -B build-11 \ + -DPYBIND11_WERROR=ON \ + -DDOWNLOAD_CATCH=ON \ + -DDOWNLOAD_EIGEN=OFF \ + -DCMAKE_CXX_STANDARD=11 \ + -DCMAKE_CXX_COMPILER=$(which icpc) \ + -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") + + - name: Build C++11 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake --build build-11 -j 2 -v + + - name: Python tests C++11 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + sudo service apport stop + cmake --build build-11 --target check + + - name: C++ tests C++11 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake --build build-11 --target cpptest + + - name: Interface test C++11 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake --build build-11 --target test_cmake_build + + - name: Configure C++17 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake -S . -B build-17 \ + -DPYBIND11_WERROR=ON \ + -DDOWNLOAD_CATCH=ON \ + -DDOWNLOAD_EIGEN=OFF \ + -DCMAKE_CXX_STANDARD=17 \ + -DCMAKE_CXX_COMPILER=$(which icpc) \ + -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") + + - name: Build C++17 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake --build build-17 -j 2 -v + + - name: Python tests C++17 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + sudo service apport stop + cmake --build build-17 --target check + + - name: C++ tests C++17 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake --build build-17 --target cpptest + + - name: Interface test C++17 + run: | + set +e; source /opt/intel/oneapi/setvars.sh; set -e + cmake --build build-17 --target test_cmake_build + + # Testing on CentOS (manylinux uses a centos base, and this is an easy way # to get GCC 4.8, which is the manylinux1 compiler). centos: @@ -399,29 +593,37 @@ jobs: strategy: fail-fast: false matrix: - centos: - - 7 # GCC 4.8 - - 8 + container: + - "centos:7" # GCC 4.8 + - "almalinux:8" + - "almalinux:9" - name: "🐍 3 • CentOS ${{ matrix.centos }} • x64" - container: "centos:${{ matrix.centos }}" + name: "🐍 3 • ${{ matrix.container }} • x64" + container: "${{ matrix.container }}" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Add Python 3 + - name: Add Python 3 (RHEL 7) + if: matrix.container == 'centos:7' run: yum update -y && yum install -y python3-devel gcc-c++ make git + - name: Add Python 3 (RHEL 8+) + if: matrix.container != 'centos:7' + run: dnf update -y && dnf install -y python3-devel gcc-c++ make git + - name: Update pip run: python3 -m pip install --upgrade pip - name: Install dependencies - run: python3 -m pip install cmake -r tests/requirements.txt --prefer-binary + run: | + python3 -m pip install cmake -r tests/requirements.txt - name: Configure shell: bash run: > cmake -S . -B build + -DCMAKE_BUILD_TYPE=MinSizeRel -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON @@ -443,18 +645,18 @@ jobs: # This tests an "install" with the CMake tools install-classic: - name: "🐍 3.5 • Debian • x86 • Install" + name: "🐍 3.7 • Debian • x86 • Install" runs-on: ubuntu-latest - container: i386/debian:stretch + container: i386/debian:buster steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v1 # Required to run inside docker - name: Install requirements run: | apt-get update apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip - pip3 install "pytest==3.1.*" + pip3 install "pytest==6.*" - name: Configure for install run: > @@ -479,33 +681,32 @@ jobs: -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") working-directory: /build-tests - - name: Run tests + - name: Python tests run: make pytest -j 2 working-directory: /build-tests # This verifies that the documentation is not horribly broken, and does a - # basic sanity check on the SDist. + # basic validation check on the SDist. doxygen: name: "Documentation build test" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" - name: Install Doxygen run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04 - - name: Install docs & setup requirements - run: python3 -m pip install -r docs/requirements.txt - - name: Build docs - run: python3 -m sphinx -W -b html docs docs/.build + run: pipx run nox -s docs - name: Make SDist - run: python3 setup.py sdist + run: pipx run nox -s build -- --sdist - run: git status --ignored @@ -517,7 +718,7 @@ jobs: - name: Compare Dists (headers only) working-directory: include run: | - python3 -m pip install --user -U ../dist/* + python3 -m pip install --user -U ../dist/*.tar.gz installed=$(python3 -c "import pybind11; print(pybind11.get_include() + '/pybind11')") diff -rq $installed ./pybind11 @@ -526,42 +727,40 @@ jobs: fail-fast: false matrix: python: - - 3.5 - 3.6 - 3.7 - 3.8 - 3.9 - - pypy3 - # TODO: fix hang on pypy2 include: - python: 3.9 - args: -DCMAKE_CXX_STANDARD=20 -DDOWNLOAD_EIGEN=OFF + args: -DCMAKE_CXX_STANDARD=20 - python: 3.8 args: -DCMAKE_CXX_STANDARD=17 name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}" - runs-on: windows-latest + runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x86 - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.12 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@v1.11.0 with: arch: x86 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - name: Configure ${{ matrix.args }} @@ -575,102 +774,189 @@ jobs: - name: Build C++11 run: cmake --build build -j 2 - - name: Run tests + - name: Python tests run: cmake --build build -t pytest - win32-msvc2015: - name: "🐍 ${{ matrix.python }} • MSVC 2015 • x64" - runs-on: windows-latest + win32-debug: strategy: fail-fast: false matrix: python: - - 2.7 - - 3.6 - - 3.7 - # todo: check/cpptest does not support 3.8+ yet + - 3.8 + - 3.9 + + include: + - python: 3.9 + args: -DCMAKE_CXX_STANDARD=20 + - python: 3.8 + args: -DCMAKE_CXX_STANDARD=17 + + name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}" + runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Setup 🐍 ${{ matrix.python }} - uses: actions/setup-python@v2 + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} + architecture: x86 - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 + uses: jwlawson/actions-setup-cmake@v1.12 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@v1.11.0 with: - toolset: 14.0 + arch: x86 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - - name: Configure + - name: Configure ${{ matrix.args }} run: > cmake -S . -B build - -G "Visual Studio 14 2015" -A x64 + -G "Visual Studio 16 2019" -A Win32 + -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON + ${{ matrix.args }} + - name: Build C++11 + run: cmake --build build --config Debug -j 2 - - name: Build C++14 - run: cmake --build build -j 2 - - - name: Run all checks - run: cmake --build build -t check + - name: Python tests + run: cmake --build build --config Debug -t pytest - win32-msvc2017: - name: "🐍 ${{ matrix.python }} • MSVC 2017 • x64" - runs-on: windows-2016 + windows-2022: strategy: fail-fast: false matrix: python: - - 2.7 - - 3.5 - - 3.7 - std: - - 14 + - 3.9 - include: - - python: 2.7 - std: 17 - args: > - -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" + name: "🐍 ${{ matrix.python }} • MSVC 2022 C++20 • x64" + runs-on: windows-2022 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Setup 🐍 ${{ matrix.python }} - uses: actions/setup-python@v2 + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.4 - - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python3 -m pip install -r tests/requirements.txt - # First build - C++11 mode and inplace - - name: Configure + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.12 + + - name: Configure C++20 run: > cmake -S . -B build - -G "Visual Studio 15 2017" -A x64 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} - ${{ matrix.args }} + -DCMAKE_CXX_STANDARD=20 - - name: Build ${{ matrix.std }} + - name: Build C++20 run: cmake --build build -j 2 - - name: Run all checks - run: cmake --build build -t check + - name: Python tests + run: cmake --build build --target pytest + + - name: C++20 tests + run: cmake --build build --target cpptest -j 2 + + - name: Interface test C++20 + run: cmake --build build --target test_cmake_build + + mingw: + name: "🐍 3 • windows-latest • ${{ matrix.sys }}" + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + matrix: + include: + - { sys: mingw64, env: x86_64 } + - { sys: mingw32, env: i686 } + steps: + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + install: >- + git + mingw-w64-${{matrix.env}}-gcc + mingw-w64-${{matrix.env}}-python-pip + mingw-w64-${{matrix.env}}-python-numpy + mingw-w64-${{matrix.env}}-python-scipy + mingw-w64-${{matrix.env}}-cmake + mingw-w64-${{matrix.env}}-make + mingw-w64-${{matrix.env}}-python-pytest + mingw-w64-${{matrix.env}}-eigen3 + mingw-w64-${{matrix.env}}-boost + mingw-w64-${{matrix.env}}-catch + + - uses: actions/checkout@v3 + + - name: Configure C++11 + # LTO leads to many undefined reference like + # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&) + run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DCMAKE_VERBOSE_MAKEFILE=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build + + - name: Build C++11 + run: cmake --build build -j 2 + + - name: Python tests C++11 + run: cmake --build build --target pytest -j 2 + + - name: C++11 tests + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target cpptest -j 2 + + - name: Interface test C++11 + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target test_cmake_build + + - name: Clean directory + run: git clean -fdx + + - name: Configure C++14 + run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DCMAKE_VERBOSE_MAKEFILE=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build2 + + - name: Build C++14 + run: cmake --build build2 -j 2 + + - name: Python tests C++14 + run: cmake --build build2 --target pytest -j 2 + + - name: C++14 tests + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target cpptest -j 2 + + - name: Interface test C++14 + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target test_cmake_build + + - name: Clean directory + run: git clean -fdx + + - name: Configure C++17 + run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DCMAKE_VERBOSE_MAKEFILE=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build3 + + - name: Build C++17 + run: cmake --build build3 -j 2 + + - name: Python tests C++17 + run: cmake --build build3 --target pytest -j 2 + + - name: C++17 tests + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target cpptest -j 2 + + - name: Interface test C++17 + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build diff --git a/3rdparty/pybind11/.github/workflows/configure.yml b/3rdparty/pybind11/.github/workflows/configure.yml index 23f60229f3..edcad4198b 100644 --- a/3rdparty/pybind11/.github/workflows/configure.yml +++ b/3rdparty/pybind11/.github/workflows/configure.yml @@ -18,7 +18,7 @@ jobs: matrix: runs-on: [ubuntu-latest, macos-latest, windows-latest] arch: [x64] - cmake: [3.18] + cmake: ["3.23"] include: - runs-on: ubuntu-latest @@ -29,22 +29,18 @@ jobs: arch: x64 cmake: 3.7 - - runs-on: windows-2016 - arch: x86 - cmake: 3.8 - - - runs-on: windows-2016 - arch: x86 + - runs-on: windows-2019 + arch: x64 # x86 compilers seem to be missing on 2019 image cmake: 3.18 name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 architecture: ${{ matrix.arch }} @@ -55,7 +51,7 @@ jobs: # An action for adding a specific version of CMake: # https://github.com/jwlawson/actions-setup-cmake - name: Setup CMake ${{ matrix.cmake }} - uses: jwlawson/actions-setup-cmake@v1.3 + uses: jwlawson/actions-setup-cmake@v1.12 with: cmake-version: ${{ matrix.cmake }} diff --git a/3rdparty/pybind11/.github/workflows/format.yml b/3rdparty/pybind11/.github/workflows/format.yml index 5cebed17da..31d893c479 100644 --- a/3rdparty/pybind11/.github/workflows/format.yml +++ b/3rdparty/pybind11/.github/workflows/format.yml @@ -12,24 +12,33 @@ on: - stable - "v*" +env: + FORCE_COLOR: 3 + jobs: pre-commit: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.0 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Add matchers + run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" + - uses: pre-commit/action@v3.0.0 with: # Slow hooks are marked with manual - slow is okay here, run them too extra_args: --hook-stage manual --all-files clang-tidy: + # When making changes here, please also review the "Clang-Tidy" section + # in .github/CONTRIBUTING.md and update as needed. name: Clang-Tidy runs-on: ubuntu-latest - container: silkeh/clang:10 + container: silkeh/clang:13 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install requirements run: apt-get update && apt-get install -y python3-dev python3-pytest @@ -37,10 +46,10 @@ jobs: - name: Configure run: > cmake -S . -B build - -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--warnings-as-errors=*" + -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 - name: Build - run: cmake --build build -j 2 + run: cmake --build build -j 2 -- --keep-going diff --git a/3rdparty/pybind11/.github/workflows/pip.yml b/3rdparty/pybind11/.github/workflows/pip.yml index 4414a12ee4..f03a397019 100644 --- a/3rdparty/pybind11/.github/workflows/pip.yml +++ b/3rdparty/pybind11/.github/workflows/pip.yml @@ -12,24 +12,28 @@ on: types: - published +env: + PIP_ONLY_BINARY: numpy + jobs: # This builds the sdists and wheels and makes sure the files are exactly as - # expected. Using Windows and Python 2.7, since that is often the most + # expected. Using Windows and Python 3.6, since that is often the most # challenging matrix element. test-packaging: - name: 🐍 2.7 • 📦 tests • windows-latest + name: 🐍 3.6 • 📦 tests • windows-latest runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Setup 🐍 2.7 - uses: actions/setup-python@v2 + - name: Setup 🐍 3.6 + uses: actions/setup-python@v4 with: - python-version: 2.7 + python-version: 3.6 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Python Packaging tests run: pytest tests/extra_python_package/ @@ -42,15 +46,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup 🐍 3.8 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Prepare env - run: python -m pip install -r tests/requirements.txt build twine --prefer-binary + run: | + python -m pip install -r tests/requirements.txt build twine - name: Python Packaging tests run: pytest tests/extra_python_package/ @@ -64,13 +69,13 @@ jobs: run: twine check dist/* - name: Save standard package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: standard path: dist/pybind11-* - name: Save global package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: global path: dist/pybind11_global-* @@ -85,19 +90,21 @@ jobs: needs: [packaging] steps: - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" # Downloads all to directories matching the artifact names - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - name: Publish standard package - uses: pypa/gh-action-pypi-publish@v1.4.1 + uses: pypa/gh-action-pypi-publish@v1.5.1 with: password: ${{ secrets.pypi_password }} packages_dir: standard/ - name: Publish global package - uses: pypa/gh-action-pypi-publish@v1.4.1 + uses: pypa/gh-action-pypi-publish@v1.5.1 with: password: ${{ secrets.pypi_password_global }} packages_dir: global/ diff --git a/3rdparty/pybind11/.github/workflows/upstream.yml b/3rdparty/pybind11/.github/workflows/upstream.yml new file mode 100644 index 0000000000..95ff4cb83d --- /dev/null +++ b/3rdparty/pybind11/.github/workflows/upstream.yml @@ -0,0 +1,112 @@ + +name: Upstream + +on: + workflow_dispatch: + pull_request: + +concurrency: + group: upstream-${{ github.ref }} + cancel-in-progress: true + +env: + PIP_ONLY_BINARY: numpy + +jobs: + standard: + name: "🐍 3.11 latest internals • ubuntu-latest • x64" + runs-on: ubuntu-latest + if: "contains(github.event.pull_request.labels.*.name, 'python dev')" + + steps: + - uses: actions/checkout@v3 + + - name: Setup Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: "3.11-dev" + + - name: Setup Boost (Linux) + if: runner.os == 'Linux' + run: sudo apt-get install libboost-dev + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.12 + + - name: Prepare env + run: | + python -m pip install -r tests/requirements.txt + + - name: Setup annotations on Linux + if: runner.os == 'Linux' + run: python -m pip install pytest-github-actions-annotate-failures + + # First build - C++11 mode and inplace + - name: Configure C++11 + run: > + cmake -S . -B . + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=11 + + - name: Build C++11 + run: cmake --build . -j 2 + + - name: Python tests C++11 + run: cmake --build . --target pytest -j 2 + + - name: C++11 tests + run: cmake --build . --target cpptest -j 2 + + - name: Interface test C++11 + run: cmake --build . --target test_cmake_build + + - name: Clean directory + run: git clean -fdx + + # Second build - C++17 mode and in a build directory + - name: Configure C++17 + run: > + cmake -S . -B build2 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + ${{ matrix.args }} + ${{ matrix.args2 }} + + - name: Build + run: cmake --build build2 -j 2 + + - name: Python tests + run: cmake --build build2 --target pytest + + - name: C++ tests + run: cmake --build build2 --target cpptest + + # Third build - C++17 mode with unstable ABI + - name: Configure (unstable ABI) + run: > + cmake -S . -B build3 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + -DPYBIND11_INTERNALS_VERSION=10000000 + "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" + ${{ matrix.args }} + + - name: Build (unstable ABI) + run: cmake --build build3 -j 2 + + - name: Python tests (unstable ABI) + run: cmake --build build3 --target pytest + + - name: Interface test + run: cmake --build build3 --target test_cmake_build + + # This makes sure the setup_helpers module can build packages using + # setuptools + - name: Setuptools helpers test + run: pytest tests/extra_setuptools diff --git a/3rdparty/pybind11/.gitignore b/3rdparty/pybind11/.gitignore index 3f36b89e0c..3cf4fbbda0 100644 --- a/3rdparty/pybind11/.gitignore +++ b/3rdparty/pybind11/.gitignore @@ -41,3 +41,5 @@ pybind11Targets.cmake /.vscode /pybind11/include/* /pybind11/share/* +/docs/_build/* +.ipynb_checkpoints/ diff --git a/3rdparty/pybind11/.pre-commit-config.yaml b/3rdparty/pybind11/.pre-commit-config.yaml index 85254a8a98..5d794a7496 100644 --- a/3rdparty/pybind11/.pre-commit-config.yaml +++ b/3rdparty/pybind11/.pre-commit-config.yaml @@ -12,49 +12,112 @@ # # See https://github.com/pre-commit/pre-commit +# third-party content +exclude: ^tools/JoinPaths.cmake$ + repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: "v4.3.0" hooks: - id: check-added-large-files - id: check-case-conflict + - id: check-docstring-first - id: check-merge-conflict - id: check-symlinks + - id: check-toml - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: requirements-txt-fixer - id: trailing-whitespace - - id: fix-encoding-pragma + +# Upgrade old Python syntax +- repo: https://github.com/asottile/pyupgrade + rev: "v2.37.3" + hooks: + - id: pyupgrade + args: [--py36-plus] + +# Nicely sort includes +- repo: https://github.com/PyCQA/isort + rev: "5.10.1" + hooks: + - id: isort # Black, the code formatter, natively supports pre-commit - repo: https://github.com/psf/black - rev: 20.8b1 + rev: "22.8.0" # Keep in sync with blacken-docs hooks: - id: black - # By default, this ignores pyi files, though black supports them - types: [text] - files: \.pyi?$ + +# Also code format the docs +- repo: https://github.com/asottile/blacken-docs + rev: "v1.12.1" + hooks: + - id: blacken-docs + additional_dependencies: + - black==22.8.0 # keep in sync with black hook # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.9 + rev: "v1.3.1" hooks: - id: remove-tabs +- repo: https://github.com/sirosen/texthooks + rev: "0.4.0" + hooks: + - id: fix-ligatures + - id: fix-smartquotes + +# Autoremoves unused imports +- repo: https://github.com/hadialqattan/pycln + rev: "v2.1.1" + hooks: + - id: pycln + stages: [manual] + +# Checking for common mistakes +- repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.9.0" + hooks: + - id: python-check-blanket-noqa + - id: python-check-blanket-type-ignore + - id: python-no-log-warn + - id: python-use-type-annotations + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + +# Automatically remove noqa that are not used +- repo: https://github.com/asottile/yesqa + rev: "v1.4.0" + hooks: + - id: yesqa + additional_dependencies: &flake8_dependencies + - flake8-bugbear + - pep8-naming + # Flake8 also supports pre-commit natively (same author) -- repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.3 +- repo: https://github.com/PyCQA/flake8 + rev: "5.0.4" hooks: - id: flake8 - additional_dependencies: [flake8-bugbear, pep8-naming] exclude: ^(docs/.*|tools/.*)$ + additional_dependencies: *flake8_dependencies + +# PyLint has native support - not always usable, but works for us +- repo: https://github.com/PyCQA/pylint + rev: "v2.15.2" + hooks: + - id: pylint + files: ^pybind11 # CMake formatting - repo: https://github.com/cheshirekow/cmake-format-precommit - rev: v0.6.13 + rev: "v0.6.13" hooks: - id: cmake-format additional_dependencies: [pyyaml] @@ -63,38 +126,50 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.790 + rev: "v0.971" hooks: - id: mypy - # The default Python type ignores .pyi files, so let's rerun if detected - types: [text] - files: ^pybind11.*\.pyi?$ - # Running per-file misbehaves a bit, so just run on all files, it's fast - pass_filenames: false + args: [] + exclude: ^(tests|docs)/ + additional_dependencies: [nox, rich] # Checks the manifest for missing files (native support) - repo: https://github.com/mgedmin/check-manifest - rev: "0.43" + rev: "0.48" hooks: - id: check-manifest # This is a slow hook, so only run this if --hook-stage manual is passed stages: [manual] additional_dependencies: [cmake, ninja] -# The original pybind11 checks for a few C++ style items +# Check for spelling +# Use tools/codespell_ignore_lines_from_errors.py +# to rebuild .codespell-ignore-lines +- repo: https://github.com/codespell-project/codespell + rev: "v2.2.1" + hooks: + - id: codespell + exclude: ".supp$" + args: ["-x", ".codespell-ignore-lines"] + +# Check for common shell mistakes +- repo: https://github.com/shellcheck-py/shellcheck-py + rev: "v0.8.0.4" + hooks: + - id: shellcheck + +# Disallow some common capitalization mistakes - repo: local hooks: - id: disallow-caps name: Disallow improper capitalization language: pygrep - entry: PyBind|Numpy|Cmake|CCache - exclude: .pre-commit-config.yaml + entry: PyBind|Numpy|Cmake|CCache|PyTest + exclude: ^\.pre-commit-config.yaml$ -- repo: local +# Clang format the codebase automatically +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: "v14.0.6" hooks: - - id: check-style - name: Classic check-style - language: system - types: - - c++ - entry: ./tools/check-style.sh + - id: clang-format + types_or: [c++, c, cuda] diff --git a/3rdparty/pybind11/CMakeLists.txt b/3rdparty/pybind11/CMakeLists.txt index 2c08ff0b98..ee0975bc1f 100644 --- a/3rdparty/pybind11/CMakeLists.txt +++ b/3rdparty/pybind11/CMakeLists.txt @@ -7,13 +7,18 @@ cmake_minimum_required(VERSION 3.4) -# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with +# The `cmake_minimum_required(VERSION 3.4...3.22)` syntax does not work with # some versions of VS that have a patched CMake 3.11. This forces us to emulate # the behavior using the following workaround: -if(${CMAKE_VERSION} VERSION_LESS 3.18) +if(${CMAKE_VERSION} VERSION_LESS 3.22) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) else() - cmake_policy(VERSION 3.18) + cmake_policy(VERSION 3.22) +endif() + +# Avoid infinite recursion if tests include this as a subdirectory +if(DEFINED PYBIND11_MASTER_PROJECT) + return() endif() # Extract project version from source @@ -73,6 +78,10 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() + + set(pybind11_system "") + + set_property(GLOBAL PROPERTY USE_FOLDERS ON) else() set(PYBIND11_MASTER_PROJECT OFF) set(pybind11_system SYSTEM) @@ -82,6 +91,9 @@ endif() option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_NOPYTHON "Disable search for Python" OFF) +set(PYBIND11_INTERNALS_VERSION + "" + CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.") cmake_dependent_option( USE_PYTHON_INCLUDE_DIR @@ -98,6 +110,7 @@ set(PYBIND11_HEADERS include/pybind11/detail/descr.h include/pybind11/detail/init.h include/pybind11/detail/internals.h + include/pybind11/detail/type_caster_base.h include/pybind11/detail/typeid.h include/pybind11/attr.h include/pybind11/buffer_info.h @@ -109,6 +122,7 @@ set(PYBIND11_HEADERS include/pybind11/eigen.h include/pybind11/embed.h include/pybind11/eval.h + include/pybind11/gil.h include/pybind11/iostream.h include/pybind11/functional.h include/pybind11/numpy.h @@ -116,7 +130,8 @@ set(PYBIND11_HEADERS include/pybind11/pybind11.h include/pybind11/pytypes.h include/pybind11/stl.h - include/pybind11/stl_bind.h) + include/pybind11/stl_bind.h + include/pybind11/stl/filesystem.h) # Compare with grep and warn if mismatched if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) @@ -159,14 +174,33 @@ endif() # You can also place ifs *in* the Config.in, but not here. # This section builds targets, but does *not* touch Python - -# Build the headers-only target (no Python included): -# (long name used here to keep this from clashing in subdirectory mode) -add_library(pybind11_headers INTERFACE) -add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target -add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember +# Non-IMPORT targets cannot be defined twice +if(NOT TARGET pybind11_headers) + # Build the headers-only target (no Python included): + # (long name used here to keep this from clashing in subdirectory mode) + add_library(pybind11_headers INTERFACE) + add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target + add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember + + target_include_directories( + pybind11_headers ${pybind11_system} INTERFACE $ + $) + + target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals + cxx_right_angle_brackets) + if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "") + target_compile_definitions( + pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}") + endif() +else() + # It is invalid to install a target twice, too. + set(PYBIND11_INSTALL OFF) +endif() include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") +# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files +# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet +include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake") # Relative directory setting if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS) @@ -175,20 +209,18 @@ elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR) file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) endif() -# Fill in headers target -target_include_directories( - pybind11_headers ${pybind11_system} INTERFACE $ - $) - -target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals - cxx_right_angle_brackets) - if(PYBIND11_INSTALL) install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) set(PYBIND11_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}" CACHE STRING "install path for pybind11Config.cmake") + if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(pybind11_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}") + else() + set(pybind11_INCLUDEDIR "\$\{PACKAGE_PREFIX_DIR\}/${CMAKE_INSTALL_INCLUDEDIR}") + endif() + configure_package_config_file( tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) @@ -233,6 +265,16 @@ if(PYBIND11_INSTALL) NAMESPACE "pybind11::" DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + # pkg-config support + if(NOT prefix_for_pc_file) + set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}") + endif() + join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/") + # Uninstall target if(PYBIND11_MASTER_PROJECT) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in" diff --git a/3rdparty/pybind11/MANIFEST.in b/3rdparty/pybind11/MANIFEST.in index aed183e874..033303a74a 100644 --- a/3rdparty/pybind11/MANIFEST.in +++ b/3rdparty/pybind11/MANIFEST.in @@ -1,6 +1,5 @@ recursive-include pybind11/include/pybind11 *.h recursive-include pybind11 *.py recursive-include pybind11 py.typed -recursive-include pybind11 *.pyi include pybind11/share/cmake/pybind11/*.cmake include LICENSE README.rst pyproject.toml setup.py setup.cfg diff --git a/3rdparty/pybind11/README.rst b/3rdparty/pybind11/README.rst index 1474cb95af..3c75edb575 100644 --- a/3rdparty/pybind11/README.rst +++ b/3rdparty/pybind11/README.rst @@ -3,20 +3,16 @@ **pybind11 — Seamless operability between C++11 and Python** -|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |CI| |Build status| +|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions| |CI| |Build status| -.. warning:: +|Repology| |PyPI package| |Conda-forge| |Python Versions| - Combining older versions of pybind11 (< 2.6.0) with the brand-new Python - 3.9.0 will trigger undefined behavior that typically manifests as crashes - during interpreter shutdown (but could also destroy your data. **You have been - warned.**) +`Setuptools example `_ +• `Scikit-build example `_ +• `CMake example `_ + +.. start - We recommend that you wait for Python 3.9.1 slated for release in December, - which will include a `fix `_ - that resolves this problem. In the meantime, please update to the latest - version of pybind11 (2.6.0 or newer), which includes a temporary workaround - specifically when Python 3.9.0 is detected at runtime. **pybind11** is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing @@ -36,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python -with everything stripped away that isn’t relevant for binding +with everything stripped away that isn't relevant for binding generation. Without comments, the core header files only require ~4K -lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++ +lines of code and depend on Python (3.6+, or PyPy) and the C++ standard library. This compact implementation was possible thanks to some of the new C++11 language features (specifically: tuples, lambda functions and variadic templates). Since its creation, this library has @@ -82,8 +78,8 @@ Goodies In addition to the core functionality, pybind11 provides some extra goodies: -- Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an - implementation-agnostic interface. +- Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic + interface (pybind11 2.9 was the last version to support Python 2 and 3.5). - It is possible to bind C++11 lambda functions with captured variables. The lambda capture data is stored inside the resulting @@ -92,8 +88,8 @@ goodies: - pybind11 uses C++11 move constructors and move assignment operators whenever possible to efficiently transfer custom data types. -- It’s easy to expose the internal storage of custom data types through - Pythons’ buffer protocols. This is handy e.g. for fast conversion +- It's easy to expose the internal storage of custom data types through + Pythons' buffer protocols. This is handy e.g. for fast conversion between C++ matrix classes like Eigen and NumPy without expensive copy operations. @@ -101,7 +97,7 @@ goodies: transparently applied to all entries of one or more NumPy array arguments. -- Python’s slice-based access and assignment operations can be +- Python's slice-based access and assignment operations can be supported with just a few lines of code. - Everything is contained in just a few header files; there is no need @@ -110,7 +106,7 @@ goodies: - Binaries are generally smaller by a factor of at least 2 compared to equivalent bindings generated by Boost.Python. A recent pybind11 conversion of PyRosetta, an enormous Boost.Python binding project, - `reported `_ + `reported `_ a binary size reduction of **5.4x** and compile time reduction by **5.8x**. @@ -123,15 +119,14 @@ goodies: Supported compilers ------------------- -1. Clang/LLVM 3.3 or newer (for Apple Xcode’s clang, this is 5.0.0 or +1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer) 2. GCC 4.8 or newer -3. Microsoft Visual Studio 2015 Update 3 or newer -4. Intel C++ compiler 18 or newer - (`possible issue `_ on 20.2) -5. Cygwin/GCC (tested on 2.5.1) -6. NVCC (CUDA 11.0 tested) -7. NVIDIA PGI (20.7 and 20.9 tested) +3. Microsoft Visual Studio 2017 or newer +4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) +5. Cygwin/GCC (previously tested on 2.5.1) +6. NVCC (CUDA 11.0 tested in CI) +7. NVIDIA PGI (20.9 tested in CI) About ----- @@ -139,9 +134,9 @@ About This project was created by `Wenzel Jakob `_. Significant features and/or improvements to the code were contributed by Jonas Adler, Lori A. Burns, -Sylvain Corlay, Eric Cousineau, Ralf Grosse-Kunstleve, Trent Houliston, Axel +Sylvain Corlay, Eric Cousineau, Aaron Gokaslan, Ralf Grosse-Kunstleve, Trent Houliston, Axel Huebl, @hulucc, Yannick Jadoul, Sergey Lyskov Johan Mabille, Tomasz Miąsko, -Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim +Dean Moldovan, Ben Pritchard, Jason Rhinelander, Boris Schäling, Pim Schellart, Henry Schreiner, Ivan Smirnov, Boris Staletic, and Patrick Stewart. We thank Google for a generous financial contribution to the continuous @@ -165,7 +160,7 @@ to the terms and conditions of this license. .. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest :target: http://pybind11.readthedocs.org/en/latest -.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue +.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue.svg :target: http://pybind11.readthedocs.org/en/stable .. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg :target: https://gitter.im/pybind/Lobby @@ -173,3 +168,13 @@ to the terms and conditions of this license. :target: https://github.com/pybind/pybind11/actions .. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true :target: https://ci.appveyor.com/project/wjakob/pybind11 +.. |PyPI package| image:: https://img.shields.io/pypi/v/pybind11.svg + :target: https://pypi.org/project/pybind11/ +.. |Conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pybind11.svg + :target: https://github.com/conda-forge/pybind11-feedstock +.. |Repology| image:: https://repology.org/badge/latest-versions/python:pybind11.svg + :target: https://repology.org/project/python:pybind11/versions +.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg + :target: https://pypi.org/project/pybind11/ +.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github + :target: https://github.com/pybind/pybind11/discussions diff --git a/3rdparty/pybind11/docs/Doxyfile b/3rdparty/pybind11/docs/Doxyfile index c8562952ef..09138db364 100644 --- a/3rdparty/pybind11/docs/Doxyfile +++ b/3rdparty/pybind11/docs/Doxyfile @@ -18,6 +18,4 @@ ALIASES += "endrst=\endverbatim" QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = NO -PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ - PY_MAJOR_VERSION=3 \ - PYBIND11_NOINLINE +PREDEFINED = PYBIND11_NOINLINE diff --git a/3rdparty/pybind11/docs/_static/css/custom.css b/3rdparty/pybind11/docs/_static/css/custom.css new file mode 100644 index 0000000000..7a49a6ac4f --- /dev/null +++ b/3rdparty/pybind11/docs/_static/css/custom.css @@ -0,0 +1,3 @@ +.highlight .go { + color: #707070; +} diff --git a/3rdparty/pybind11/docs/advanced/cast/custom.rst b/3rdparty/pybind11/docs/advanced/cast/custom.rst index a779444c24..8138cac619 100644 --- a/3rdparty/pybind11/docs/advanced/cast/custom.rst +++ b/3rdparty/pybind11/docs/advanced/cast/custom.rst @@ -26,7 +26,9 @@ The following Python snippet demonstrates the intended usage from the Python sid def __int__(self): return 123 + from example import print + print(A()) To register the necessary conversion routines, it is necessary to add an @@ -36,7 +38,7 @@ type is explicitly allowed. .. code-block:: cpp - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template <> struct type_caster { public: /** @@ -44,7 +46,7 @@ type is explicitly allowed. * function signatures and declares a local variable * 'value' of type inty */ - PYBIND11_TYPE_CASTER(inty, _("inty")); + PYBIND11_TYPE_CASTER(inty, const_name("inty")); /** * Conversion part 1 (Python->C++): convert a PyObject into a inty @@ -76,7 +78,7 @@ type is explicitly allowed. return PyLong_FromLong(src.long_value); } }; - }} // namespace pybind11::detail + }} // namespace PYBIND11_NAMESPACE::detail .. note:: diff --git a/3rdparty/pybind11/docs/advanced/cast/eigen.rst b/3rdparty/pybind11/docs/advanced/cast/eigen.rst index e01472d5ae..a5c11a3f14 100644 --- a/3rdparty/pybind11/docs/advanced/cast/eigen.rst +++ b/3rdparty/pybind11/docs/advanced/cast/eigen.rst @@ -52,7 +52,7 @@ can be mapped *and* if the numpy array is writeable (that is the passed variable will be transparently carried out directly on the ``numpy.ndarray``. -This means you can can write code such as the following and have it work as +This means you can write code such as the following and have it work as expected: .. code-block:: cpp @@ -112,7 +112,7 @@ example: .. code-block:: python a = MyClass() - m = a.get_matrix() # flags.writeable = True, flags.owndata = False + m = a.get_matrix() # flags.writeable = True, flags.owndata = False v = a.view_matrix() # flags.writeable = False, flags.owndata = False c = a.copy_matrix() # flags.writeable = True, flags.owndata = True # m[5,6] and v[5,6] refer to the same element, c[5,6] does not. @@ -203,7 +203,7 @@ adding the ``order='F'`` option when creating an array: .. code-block:: python - myarray = np.array(source, order='F') + myarray = np.array(source, order="F") Such an object will be passable to a bound function accepting an ``Eigen::Ref`` (or similar column-major Eigen type). diff --git a/3rdparty/pybind11/docs/advanced/cast/overview.rst b/3rdparty/pybind11/docs/advanced/cast/overview.rst index b0e32a52f9..011bd4c7a3 100644 --- a/3rdparty/pybind11/docs/advanced/cast/overview.rst +++ b/3rdparty/pybind11/docs/advanced/cast/overview.rst @@ -75,91 +75,96 @@ The following basic data types are supported out of the box (some may require an additional extension header to be included). To pass other data structures as arguments and return values, refer to the section on binding :ref:`classes`. -+------------------------------------+---------------------------+-------------------------------+ -| Data type | Description | Header file | -+====================================+===========================+===============================+ -| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``char`` | Character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` | -| ``std::u16string_view``, etc. | | | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::pair`` | Pair of two custom types | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::complex`` | Complex numbers | :file:`pybind11/complex.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::array`` | STL static array | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::vector`` | STL dynamic array | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::deque`` | STL double-ended queue | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::valarray`` | STL value array | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::list`` | STL linked list | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::map`` | STL ordered map | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::unordered_map`` | STL unordered map | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::set`` | STL ordered set | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::unordered_set`` | STL unordered set | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::optional`` | STL optional type (C++17) | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::experimental::optional`` | STL optional type (exp.) | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | -+------------------------------------+---------------------------+-------------------------------+ ++------------------------------------+---------------------------+-----------------------------------+ +| Data type | Description | Header file | ++====================================+===========================+===================================+ +| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``char`` | Character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` | +| ``std::u16string_view``, etc. | | | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::pair`` | Pair of two custom types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::complex`` | Complex numbers | :file:`pybind11/complex.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::array`` | STL static array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::vector`` | STL dynamic array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::deque`` | STL double-ended queue | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::valarray`` | STL value array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::list`` | STL linked list | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::map`` | STL ordered map | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::unordered_map`` | STL unordered map | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::set`` | STL ordered set | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::unordered_set`` | STL unordered set | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::optional`` | STL optional type (C++17) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::experimental::optional`` | STL optional type (exp.) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::filesystem::path`` | STL path (C++17) [#]_ | :file:`pybind11/stl/filesystem.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-----------------------------------+ + +.. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and + ``os.PathLike`` is converted to ``std::filesystem::path``. diff --git a/3rdparty/pybind11/docs/advanced/cast/stl.rst b/3rdparty/pybind11/docs/advanced/cast/stl.rst index 70fde0d2f4..03d49b2950 100644 --- a/3rdparty/pybind11/docs/advanced/cast/stl.rst +++ b/3rdparty/pybind11/docs/advanced/cast/stl.rst @@ -42,7 +42,7 @@ types: .. code-block:: cpp // `boost::optional` as an example -- can be any `std::optional`-like container - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template struct type_caster> : optional_caster> {}; }} @@ -54,7 +54,7 @@ for custom variant types: .. code-block:: cpp // `boost::variant` as an example -- can be any `std::variant`-like container - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template struct type_caster> : variant_caster> {}; @@ -66,18 +66,27 @@ for custom variant types: return boost::apply_visitor(args...); } }; - }} // namespace pybind11::detail + }} // namespace PYBIND11_NAMESPACE::detail The ``visit_helper`` specialization is not required if your ``name::variant`` provides a ``name::visit()`` function. For any other function name, the specialization must be included to tell pybind11 how to visit the variant. +.. warning:: + + When converting a ``variant`` type, pybind11 follows the same rules as when + determining which function overload to call (:ref:`overload_resolution`), and + so the same caveats hold. In particular, the order in which the ``variant``'s + alternatives are listed is important, since pybind11 will try conversions in + this order. This means that, for example, when converting ``variant``, + the ``bool`` variant will never be selected, as any Python ``bool`` is already + an ``int`` and is convertible to a C++ ``int``. Changing the order of alternatives + (and using ``variant``, in this example) provides a solution. + .. note:: pybind11 only supports the modern implementation of ``boost::variant`` which makes use of variadic templates. This requires Boost 1.56 or newer. - Additionally, on Windows, MSVC 2017 is required because ``boost::variant`` - falls back to the old non-variadic implementation on MSVC 2015. .. _opaque: diff --git a/3rdparty/pybind11/docs/advanced/cast/strings.rst b/3rdparty/pybind11/docs/advanced/cast/strings.rst index e25701ecab..e246c5219a 100644 --- a/3rdparty/pybind11/docs/advanced/cast/strings.rst +++ b/3rdparty/pybind11/docs/advanced/cast/strings.rst @@ -1,14 +1,6 @@ Strings, bytes and Unicode conversions ###################################### -.. note:: - - This section discusses string handling in terms of Python 3 strings. For - Python 2.7, replace all occurrences of ``str`` with ``unicode`` and - ``bytes`` with ``str``. Python 2.7 users may find it best to use ``from - __future__ import unicode_literals`` to avoid unintentionally using ``str`` - instead of ``unicode``. - Passing Python strings to C++ ============================= @@ -36,13 +28,13 @@ everywhere `_. } ); -.. code-block:: python +.. code-block:: pycon - >>> utf8_test('🎂') + >>> utf8_test("🎂") utf-8 is icing on the cake. 🎂 - >>> utf8_charptr('🍕') + >>> utf8_charptr("🍕") My favorite food is 🍕 @@ -58,9 +50,9 @@ Passing bytes to C++ -------------------- A Python ``bytes`` object will be passed to C++ functions that accept -``std::string`` or ``char*`` *without* conversion. On Python 3, in order to -make a function *only* accept ``bytes`` (and not ``str``), declare it as taking -a ``py::bytes`` argument. +``std::string`` or ``char*`` *without* conversion. In order to make a function +*only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes`` +argument. Returning C++ strings to Python @@ -80,7 +72,7 @@ raise a ``UnicodeDecodeError``. } ); -.. code-block:: python +.. code-block:: pycon >>> isinstance(example.std_string_return(), str) True @@ -114,7 +106,7 @@ conversion has the same overhead as implicit conversion. } ); -.. code-block:: python +.. code-block:: pycon >>> str_output() 'Send your résumé to Alice in HR' @@ -143,7 +135,7 @@ returned to Python as ``bytes``, then one can return the data as a } ); -.. code-block:: python +.. code-block:: pycon >>> example.return_bytes() b'\xba\xd0\xba\xd0' @@ -160,7 +152,7 @@ encoding, but cannot convert ``std::string`` back to ``bytes`` implicitly. } ); -.. code-block:: python +.. code-block:: pycon >>> isinstance(example.asymmetry(b"have some bytes"), str) True @@ -204,11 +196,6 @@ decoded to Python ``str``. } ); -.. warning:: - - Wide character strings may not work as described on Python 2.7 or Python - 3.3 compiled with ``--enable-unicode=ucs2``. - Strings in multibyte encodings such as Shift-JIS must transcoded to a UTF-8/16/32 before being returned to Python. @@ -229,16 +216,16 @@ character. m.def("pass_char", [](char c) { return c; }); m.def("pass_wchar", [](wchar_t w) { return w; }); -.. code-block:: python +.. code-block:: pycon - >>> example.pass_char('A') + >>> example.pass_char("A") 'A' While C++ will cast integers to character types (``char c = 0x65;``), pybind11 does not convert Python integers to characters implicitly. The Python function ``chr()`` can be used to convert integers to characters. -.. code-block:: python +.. code-block:: pycon >>> example.pass_char(0x65) TypeError @@ -259,17 +246,17 @@ a combining acute accent). The combining character will be lost if the two-character sequence is passed as an argument, even though it renders as a single grapheme. -.. code-block:: python +.. code-block:: pycon - >>> example.pass_wchar('é') + >>> example.pass_wchar("é") 'é' - >>> combining_e_acute = 'e' + '\u0301' + >>> combining_e_acute = "e" + "\u0301" >>> combining_e_acute 'é' - >>> combining_e_acute == 'é' + >>> combining_e_acute == "é" False >>> example.pass_wchar(combining_e_acute) @@ -278,9 +265,9 @@ single grapheme. Normalizing combining characters before passing the character literal to C++ may resolve *some* of these issues: -.. code-block:: python +.. code-block:: pycon - >>> example.pass_wchar(unicodedata.normalize('NFC', combining_e_acute)) + >>> example.pass_wchar(unicodedata.normalize("NFC", combining_e_acute)) 'é' In some languages (Thai for example), there are `graphemes that cannot be diff --git a/3rdparty/pybind11/docs/advanced/classes.rst b/3rdparty/pybind11/docs/advanced/classes.rst index 4927902069..01a490b721 100644 --- a/3rdparty/pybind11/docs/advanced/classes.rst +++ b/3rdparty/pybind11/docs/advanced/classes.rst @@ -9,7 +9,7 @@ that you are already familiar with the basics from :doc:`/classes`. Overriding virtual functions in Python ====================================== -Suppose that a C++ class or interface has a virtual function that we'd like to +Suppose that a C++ class or interface has a virtual function that we'd like to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is given as a specific example of how one would do this with traditional C++ code). @@ -133,14 +133,14 @@ a virtual method call. >>> from example import * >>> d = Dog() >>> call_go(d) - u'woof! woof! woof! ' + 'woof! woof! woof! ' >>> class Cat(Animal): ... def go(self, n_times): - ... return "meow! " * n_times + ... return "meow! " * n_times ... >>> c = Cat() >>> call_go(c) - u'meow! meow! meow! ' + 'meow! meow! meow! ' If you are defining a custom constructor in a derived Python class, you *must* ensure that you explicitly call the bound C++ constructor using ``__init__``, @@ -159,8 +159,9 @@ Here is an example: class Dachshund(Dog): def __init__(self, name): - Dog.__init__(self) # Without this, a TypeError is raised. + Dog.__init__(self) # Without this, a TypeError is raised. self.name = name + def bark(self): return "yap!" @@ -259,7 +260,7 @@ override the ``name()`` method): .. note:: - Note the trailing commas in the ``PYBIND11_OVERIDE`` calls to ``name()`` + Note the trailing commas in the ``PYBIND11_OVERRIDE`` calls to ``name()`` and ``bark()``. These are needed to portably implement a trampoline for a function that does not take any arguments. For functions that take a nonzero number of arguments, the trailing comma must be omitted. @@ -804,7 +805,7 @@ to bind these two functions: } )); -The ``__setstate__`` part of the ``py::picke()`` definition follows the same +The ``__setstate__`` part of the ``py::pickle()`` definition follows the same rules as the single-argument version of ``py::init()``. The return type can be a value, pointer or holder type. See :ref:`custom_constructors` for details. @@ -812,26 +813,21 @@ An instance can now be pickled as follows: .. code-block:: python - try: - import cPickle as pickle # Use cPickle on Python 2.7 - except ImportError: - import pickle + import pickle p = Pickleable("test_value") p.setExtra(15) - data = pickle.dumps(p, 2) + data = pickle.dumps(p) .. note:: - Note that only the cPickle module is supported on Python 2.7. - - The second argument to ``dumps`` is also crucial: it selects the pickle - protocol version 2, since the older version 1 is not supported. Newer - versions are also fine—for instance, specify ``-1`` to always use the - latest available version. Beware: failure to follow these instructions - will cause important pybind11 memory allocation routines to be skipped - during unpickling, which will likely lead to memory corruption and/or - segmentation faults. + If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are + not supported. Newer versions are also fine; for instance, specify ``-1`` to + always use the latest available version. Beware: failure to follow these + instructions will cause important pybind11 memory allocation routines to be + skipped during unpickling, which will likely lead to memory corruption + and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and + version 4 for Python 3.8+. .. seealso:: @@ -848,11 +844,9 @@ Python normally uses references in assignments. Sometimes a real copy is needed to prevent changing all copies. The ``copy`` module [#f5]_ provides these capabilities. -On Python 3, a class with pickle support is automatically also (deep)copy +A class with pickle support is automatically also (deep)copy compatible. However, performance can be improved by adding custom -``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods -are mandatory for (deep)copy compatibility, because pybind11 only supports -cPickle. +``__copy__`` and ``__deepcopy__`` methods. For simple classes (deep)copy can be enabled by using the copy constructor, which should look as follows: @@ -1124,13 +1118,6 @@ described trampoline: py::class_(m, "A") // <-- `Trampoline` here .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! -.. note:: - - MSVC 2015 has a compiler bug (fixed in version 2017) which - requires a more explicit function binding in the form of - ``.def("foo", static_cast(&Publicist::foo));`` - where ``int (A::*)() const`` is the type of ``A::foo``. - Binding final classes ===================== @@ -1153,12 +1140,65 @@ error: >>> class PyFinalChild(IsFinal): ... pass + ... TypeError: type 'IsFinal' is not an acceptable base type .. note:: This attribute is currently ignored on PyPy .. versionadded:: 2.6 +Binding classes with template parameters +======================================== + +pybind11 can also wrap classes that have template parameters. Consider these classes: + +.. code-block:: cpp + + struct Cat {}; + struct Dog {}; + + template + struct Cage { + Cage(PetType& pet); + PetType& get(); + }; + +C++ templates may only be instantiated at compile time, so pybind11 can only +wrap instantiated templated classes. You cannot wrap a non-instantiated template: + +.. code-block:: cpp + + // BROKEN (this will not compile) + py::class_(m, "Cage"); + .def("get", &Cage::get); + +You must explicitly specify each template/type combination that you want to +wrap separately. + +.. code-block:: cpp + + // ok + py::class_>(m, "CatCage") + .def("get", &Cage::get); + + // ok + py::class_>(m, "DogCage") + .def("get", &Cage::get); + +If your class methods have template parameters you can wrap those as well, +but once again each instantiation must be explicitly specified: + +.. code-block:: cpp + + typename + struct MyClass { + template + T fn(V v); + }; + + py::class>(m, "MyClassT") + .def("fn", &MyClass::fn); + Custom automatic downcasters ============================ @@ -1188,7 +1228,7 @@ whether a downcast is safe, you can proceed by specializing the std::string bark() const { return sound; } }; - namespace pybind11 { + namespace PYBIND11_NAMESPACE { template<> struct polymorphic_type_hook { static const void *get(const Pet *src, const std::type_info*& type) { // note that src may be nullptr @@ -1199,7 +1239,7 @@ whether a downcast is safe, you can proceed by specializing the return src; } }; - } // namespace pybind11 + } // namespace PYBIND11_NAMESPACE When pybind11 wants to convert a C++ pointer of type ``Base*`` to a Python object, it calls ``polymorphic_type_hook::get()`` to @@ -1247,7 +1287,7 @@ Accessing the type object You can get the type object from a C++ class that has already been registered using: -.. code-block:: python +.. code-block:: cpp py::type T_py = py::type::of(); @@ -1259,3 +1299,37 @@ object, just like ``type(ob)`` in Python. Other types, like ``py::type::of()``, do not work, see :ref:`type-conversions`. .. versionadded:: 2.6 + +Custom type setup +================= + +For advanced use cases, such as enabling garbage collection support, you may +wish to directly manipulate the ``PyHeapTypeObject`` corresponding to a +``py::class_`` definition. + +You can do that using ``py::custom_type_setup``: + +.. code-block:: cpp + + struct OwnsPythonObjects { + py::object value = py::none(); + }; + py::class_ cls( + m, "OwnsPythonObjects", py::custom_type_setup([](PyHeapTypeObject *heap_type) { + auto *type = &heap_type->ht_type; + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + type->tp_traverse = [](PyObject *self_base, visitproc visit, void *arg) { + auto &self = py::cast(py::handle(self_base)); + Py_VISIT(self.value.ptr()); + return 0; + }; + type->tp_clear = [](PyObject *self_base) { + auto &self = py::cast(py::handle(self_base)); + self.value = py::none(); + return 0; + }; + })); + cls.def(py::init<>()); + cls.def_readwrite("value", &OwnsPythonObjects::value); + +.. versionadded:: 2.8 diff --git a/3rdparty/pybind11/docs/advanced/embedding.rst b/3rdparty/pybind11/docs/advanced/embedding.rst index dfdaad2d7f..dd980d483a 100644 --- a/3rdparty/pybind11/docs/advanced/embedding.rst +++ b/3rdparty/pybind11/docs/advanced/embedding.rst @@ -40,15 +40,15 @@ The essential structure of the ``main.cpp`` file looks like this: } The interpreter must be initialized before using any Python API, which includes -all the functions and classes in pybind11. The RAII guard class `scoped_interpreter` +all the functions and classes in pybind11. The RAII guard class ``scoped_interpreter`` takes care of the interpreter lifetime. After the guard is destroyed, the interpreter shuts down and clears its memory. No Python functions can be called after this. Executing Python code ===================== -There are a few different ways to run Python code. One option is to use `eval`, -`exec` or `eval_file`, as explained in :ref:`eval`. Here is a quick example in +There are a few different ways to run Python code. One option is to use ``eval``, +``exec`` or ``eval_file``, as explained in :ref:`eval`. Here is a quick example in the context of an executable with an embedded interpreter: .. code-block:: cpp @@ -108,7 +108,7 @@ The two approaches can also be combined: Importing modules ================= -Python modules can be imported using `module_::import()`: +Python modules can be imported using ``module_::import()``: .. code-block:: cpp @@ -122,6 +122,7 @@ embedding the interpreter. This makes it easy to import local Python files: """calc.py located in the working directory""" + def add(i, j): return i + j @@ -133,7 +134,7 @@ embedding the interpreter. This makes it easy to import local Python files: int n = result.cast(); assert(n == 3); -Modules can be reloaded using `module_::reload()` if the source is modified e.g. +Modules can be reloaded using ``module_::reload()`` if the source is modified e.g. by an external process. This can be useful in scenarios where the application imports a user defined data processing script which needs to be updated after changes by the user. Note that this function does not reload modules recursively. @@ -143,7 +144,7 @@ changes by the user. Note that this function does not reload modules recursively Adding embedded modules ======================= -Embedded binary modules can be added using the `PYBIND11_EMBEDDED_MODULE` macro. +Embedded binary modules can be added using the ``PYBIND11_EMBEDDED_MODULE`` macro. Note that the definition must be placed at global scope. They can be imported like any other module. @@ -169,7 +170,7 @@ like any other module. Unlike extension modules where only a single binary module can be created, on the embedded side an unlimited number of modules can be added using multiple -`PYBIND11_EMBEDDED_MODULE` definitions (as long as they have unique names). +``PYBIND11_EMBEDDED_MODULE`` definitions (as long as they have unique names). These modules are added to Python's list of builtins, so they can also be imported in pure Python files loaded by the interpreter. Everything interacts @@ -215,9 +216,9 @@ naturally: Interpreter lifetime ==================== -The Python interpreter shuts down when `scoped_interpreter` is destroyed. After +The Python interpreter shuts down when ``scoped_interpreter`` is destroyed. After this, creating a new instance will restart the interpreter. Alternatively, the -`initialize_interpreter` / `finalize_interpreter` pair of functions can be used +``initialize_interpreter`` / ``finalize_interpreter`` pair of functions can be used to directly set the state at any time. Modules created with pybind11 can be safely re-initialized after the interpreter @@ -229,8 +230,8 @@ global data. All the details can be found in the CPython documentation. .. warning:: - Creating two concurrent `scoped_interpreter` guards is a fatal error. So is - calling `initialize_interpreter` for a second time after the interpreter + Creating two concurrent ``scoped_interpreter`` guards is a fatal error. So is + calling ``initialize_interpreter`` for a second time after the interpreter has already been initialized. Do not use the raw CPython API functions ``Py_Initialize`` and @@ -241,7 +242,7 @@ global data. All the details can be found in the CPython documentation. Sub-interpreter support ======================= -Creating multiple copies of `scoped_interpreter` is not possible because it +Creating multiple copies of ``scoped_interpreter`` is not possible because it represents the main Python interpreter. Sub-interpreters are something different and they do permit the existence of multiple interpreters. This is an advanced feature of the CPython API and should be handled with care. pybind11 does not @@ -257,5 +258,5 @@ We'll just mention a couple of caveats the sub-interpreters support in pybind11: 2. Managing multiple threads, multiple interpreters and the GIL can be challenging and there are several caveats here, even within the pure CPython API (please refer to the Python docs for details). As for - pybind11, keep in mind that `gil_scoped_release` and `gil_scoped_acquire` + pybind11, keep in mind that ``gil_scoped_release`` and ``gil_scoped_acquire`` do not take sub-interpreters into account. diff --git a/3rdparty/pybind11/docs/advanced/exceptions.rst b/3rdparty/pybind11/docs/advanced/exceptions.rst index 5eae556247..2211caf5d3 100644 --- a/3rdparty/pybind11/docs/advanced/exceptions.rst +++ b/3rdparty/pybind11/docs/advanced/exceptions.rst @@ -43,18 +43,28 @@ at its exception handler. | | of bounds access in ``__getitem__``, | | | ``__setitem__``, etc.) | +--------------------------------------+--------------------------------------+ -| :class:`pybind11::value_error` | ``ValueError`` (used to indicate | -| | wrong value passed in | -| | ``container.remove(...)``) | -+--------------------------------------+--------------------------------------+ | :class:`pybind11::key_error` | ``KeyError`` (used to indicate out | | | of bounds access in ``__getitem__``, | | | ``__setitem__`` in dict-like | | | objects, etc.) | +--------------------------------------+--------------------------------------+ +| :class:`pybind11::value_error` | ``ValueError`` (used to indicate | +| | wrong value passed in | +| | ``container.remove(...)``) | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::type_error` | ``TypeError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::buffer_error` | ``BufferError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::import_error` | ``ImportError`` | ++--------------------------------------+--------------------------------------+ +| :class:`pybind11::attribute_error` | ``AttributeError`` | ++--------------------------------------+--------------------------------------+ +| Any other exception | ``RuntimeError`` | ++--------------------------------------+--------------------------------------+ Exception translation is not bidirectional. That is, *catching* the C++ -exceptions defined above above will not trap exceptions that originate from +exceptions defined above will not trap exceptions that originate from Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below ` for further details. @@ -67,9 +77,10 @@ Registering custom translators If the default exception conversion policy described above is insufficient, pybind11 also provides support for registering custom exception translators. -To register a simple exception conversion that translates a C++ exception into -a new Python exception using the C++ exception's ``what()`` method, a helper -function is available: +Similar to pybind11 classes, exception translators can be local to the module +they are defined in or global to the entire python session. To register a simple +exception conversion that translates a C++ exception into a new Python exception +using the C++ exception's ``what()`` method, a helper function is available: .. code-block:: cpp @@ -79,29 +90,39 @@ This call creates a Python exception class with the name ``PyExp`` in the given module and automatically converts any encountered exceptions of type ``CppExp`` into Python exceptions of type ``PyExp``. +A matching function is available for registering a local exception translator: + +.. code-block:: cpp + + py::register_local_exception(module, "PyExp"); + + It is possible to specify base class for the exception using the third -parameter, a `handle`: +parameter, a ``handle``: .. code-block:: cpp py::register_exception(module, "PyExp", PyExc_RuntimeError); + py::register_local_exception(module, "PyExp", PyExc_RuntimeError); -Then `PyExp` can be caught both as `PyExp` and `RuntimeError`. +Then ``PyExp`` can be caught both as ``PyExp`` and ``RuntimeError``. The class objects of the built-in Python exceptions are listed in the Python documentation on `Standard Exceptions `_. -The default base class is `PyExc_Exception`. +The default base class is ``PyExc_Exception``. -When more advanced exception translation is needed, the function -``py::register_exception_translator(translator)`` can be used to register +When more advanced exception translation is needed, the functions +``py::register_exception_translator(translator)`` and +``py::register_local_exception_translator(translator)`` can be used to register functions that can translate arbitrary exception types (and which may include -additional logic to do so). The function takes a stateless callable (e.g. a +additional logic to do so). The functions takes a stateless callable (e.g. a function pointer or a lambda function without captured variables) with the call signature ``void(std::exception_ptr)``. When a C++ exception is thrown, the registered exception translators are tried in reverse order of registration (i.e. the last registered translator gets the -first shot at handling the exception). +first shot at handling the exception). All local translators will be tried +before a global translator is tried. Inside the translator, ``std::rethrow_exception`` should be used within a try block to re-throw the exception. One or more catch clauses to catch @@ -156,6 +177,57 @@ section. may be explicitly (re-)thrown to delegate it to the other, previously-declared existing exception translators. + Note that ``libc++`` and ``libstdc++`` `behave differently `_ + with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``. + See also: "Problems with C++ exceptions" under `GCC Wiki `_. + + +Local vs Global Exception Translators +===================================== + +When a global exception translator is registered, it will be applied across all +modules in the reverse order of registration. This can create behavior where the +order of module import influences how exceptions are translated. + +If module1 has the following translator: + +.. code-block:: cpp + + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const std::invalid_argument &e) { + PyErr_SetString("module1 handled this") + } + } + +and module2 has the following similar translator: + +.. code-block:: cpp + + py::register_exception_translator([](std::exception_ptr p) { + try { + if (p) std::rethrow_exception(p); + } catch (const std::invalid_argument &e) { + PyErr_SetString("module2 handled this") + } + } + +then which translator handles the invalid_argument will be determined by the +order that module1 and module2 are imported. Since exception translators are +applied in the reverse order of registration, which ever module was imported +last will "win" and that translator will be applied. + +If there are multiple pybind11 modules that share exception types (either +standard built-in or custom) loaded into a single python instance and +consistent error handling behavior is needed, then local translators should be +used. + +Changing the previous example to use ``register_local_exception_translator`` +would mean that when invalid_argument is thrown in the module2 code, the +module2 translator will always handle it, while in module1, the module1 +translator will do the same. + .. _handling_python_exceptions_cpp: Handling exceptions from Python in C++ @@ -188,7 +260,7 @@ For example: } catch (py::error_already_set &e) { if (e.matches(PyExc_FileNotFoundError)) { py::print("missing.txt not found"); - } else if (e.match(PyExc_PermissionError)) { + } else if (e.matches(PyExc_PermissionError)) { py::print("missing.txt found but not accessible"); } else { throw; @@ -253,6 +325,34 @@ Alternately, to ignore the error, call `PyErr_Clear Any Python error must be thrown or cleared, or Python/pybind11 will be left in an invalid state. +Chaining exceptions ('raise from') +================================== + +Python has a mechanism for indicating that exceptions were caused by other +exceptions: + +.. code-block:: py + + try: + print(1 / 0) + except Exception as exc: + raise RuntimeError("could not divide by zero") from exc + +To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It +sets the current python error indicator, so to continue propagating the exception +you should ``throw py::error_already_set()``. + +.. code-block:: cpp + + try { + py::eval("print(1 / 0")); + } catch (py::error_already_set &e) { + py::raise_from(e, PyExc_RuntimeError, "could not divide by zero"); + throw py::error_already_set(); + } + +.. versionadded:: 2.8 + .. _unraisable_exceptions: Handling unraisable exceptions diff --git a/3rdparty/pybind11/docs/advanced/functions.rst b/3rdparty/pybind11/docs/advanced/functions.rst index ebdff9c93d..69e3d8a1df 100644 --- a/3rdparty/pybind11/docs/advanced/functions.rst +++ b/3rdparty/pybind11/docs/advanced/functions.rst @@ -50,7 +50,7 @@ implied transfer of ownership, i.e.: .. code-block:: cpp - m.def("get_data", &get_data, return_value_policy::reference); + m.def("get_data", &get_data, py::return_value_policy::reference); On the other hand, this is not the right policy for many other situations, where ignoring ownership could lead to resource leaks. @@ -90,17 +90,18 @@ The following table provides an overview of available policies: | | return value is referenced by Python. This is the default policy for | | | property getters created via ``def_property``, ``def_readwrite``, etc. | +--------------------------------------------------+----------------------------------------------------------------------------+ -| :enum:`return_value_policy::automatic` | **Default policy.** This policy falls back to the policy | +| :enum:`return_value_policy::automatic` | This policy falls back to the policy | | | :enum:`return_value_policy::take_ownership` when the return value is a | | | pointer. Otherwise, it uses :enum:`return_value_policy::move` or | | | :enum:`return_value_policy::copy` for rvalue and lvalue references, | | | respectively. See above for a description of what all of these different | -| | policies do. | +| | policies do. This is the default policy for ``py::class_``-wrapped types. | +--------------------------------------------------+----------------------------------------------------------------------------+ | :enum:`return_value_policy::automatic_reference` | As above, but use policy :enum:`return_value_policy::reference` when the | | | return value is a pointer. This is the default conversion policy for | | | function arguments when calling Python functions manually from C++ code | -| | (i.e. via handle::operator()). You probably won't need to use this. | +| | (i.e. via ``handle::operator()``) and the casters in ``pybind11/stl.h``. | +| | You probably won't need to use this explicitly. | +--------------------------------------------------+----------------------------------------------------------------------------+ Return value policies can also be applied to properties: @@ -119,7 +120,7 @@ targeted arguments can be passed through the :class:`cpp_function` constructor: .. code-block:: cpp class_(m, "MyClass") - .def_property("data" + .def_property("data", py::cpp_function(&MyClass::getData, py::return_value_policy::copy), py::cpp_function(&MyClass::setData) ); @@ -182,6 +183,9 @@ relies on the ability to create a *weak reference* to the nurse object. When the nurse object is not a pybind11-registered type and does not support weak references, an exception will be thrown. +If you use an incorrect argument index, you will get a ``RuntimeError`` saying +``Could not activate keep_alive!``. You should review the indices you're using. + Consider the following example: here, the binding code for a list append operation ties the lifetime of the newly added element to the underlying container: @@ -228,7 +232,7 @@ is equivalent to the following pseudocode: }); The only requirement is that ``T`` is default-constructible, but otherwise any -scope guard will work. This is very useful in combination with `gil_scoped_release`. +scope guard will work. This is very useful in combination with ``gil_scoped_release``. See :ref:`gil`. Multiple guards can also be specified as ``py::call_guard``. The @@ -251,7 +255,7 @@ For instance, the following statement iterates over a Python ``dict``: .. code-block:: cpp - void print_dict(py::dict dict) { + void print_dict(const py::dict& dict) { /* Easily interact with Python types */ for (auto item : dict) std::cout << "key=" << std::string(py::str(item.first)) << ", " @@ -268,7 +272,7 @@ And used in Python as usual: .. code-block:: pycon - >>> print_dict({'foo': 123, 'bar': 'hello'}) + >>> print_dict({"foo": 123, "bar": "hello"}) key=foo, value=123 key=bar, value=hello @@ -289,7 +293,7 @@ Such functions can also be created using pybind11: .. code-block:: cpp - void generic(py::args args, py::kwargs kwargs) { + void generic(py::args args, const py::kwargs& kwargs) { /// .. do something with args if (kwargs) /// .. do something with kwargs @@ -302,8 +306,9 @@ The class ``py::args`` derives from ``py::tuple`` and ``py::kwargs`` derives from ``py::dict``. You may also use just one or the other, and may combine these with other -arguments as long as the ``py::args`` and ``py::kwargs`` arguments are the last -arguments accepted by the function. +arguments. Note, however, that ``py::kwargs`` must always be the last argument +of the function, and ``py::args`` implies that any further arguments are +keyword-only (see :ref:`keyword_only_arguments`). Please refer to the other examples for details on how to iterate over these, and on how to cast their entries into C++ objects. A demonstration is also @@ -362,10 +367,12 @@ like so: py::class_("MyClass") .def("myFunction", py::arg("arg") = static_cast(nullptr)); +.. _keyword_only_arguments: + Keyword-only arguments ====================== -Python 3 introduced keyword-only arguments by specifying an unnamed ``*`` +Python implements keyword-only arguments by specifying an unnamed ``*`` argument in a function definition: .. code-block:: python @@ -373,10 +380,11 @@ argument in a function definition: def f(a, *, b): # a can be positional or via keyword; b must be via keyword pass + f(a=1, b=2) # good f(b=2, a=1) # good - f(1, b=2) # good - f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given + f(1, b=2) # good + f(1, 2) # TypeError: f() takes 1 positional argument but 2 were given Pybind11 provides a ``py::kw_only`` object that allows you to implement the same behaviour by specifying the object between positional and keyword-only @@ -387,11 +395,19 @@ argument annotations when registering the function: m.def("f", [](int a, int b) { /* ... */ }, py::arg("a"), py::kw_only(), py::arg("b")); -Note that you currently cannot combine this with a ``py::args`` argument. This -feature does *not* require Python 3 to work. - .. versionadded:: 2.6 +A ``py::args`` argument implies that any following arguments are keyword-only, +as if ``py::kw_only()`` had been specified in the same relative location of the +argument list as the ``py::args`` argument. The ``py::kw_only()`` may be +included to be explicit about this, but is not required. + +.. versionchanged:: 2.9 + This can now be combined with ``py::args``. Before, ``py::args`` could only + occur at the end of the argument list, or immediately before a ``py::kwargs`` + argument at the end. + + Positional-only arguments ========================= @@ -524,6 +540,8 @@ The default behaviour when the tag is unspecified is to allow ``None``. not allow ``None`` as argument. To pass optional argument of these copied types consider using ``std::optional`` +.. _overload_resolution: + Overload resolution order ========================= @@ -559,3 +577,38 @@ prefers earlier-defined overloads to later-defined ones. .. versionadded:: 2.6 The ``py::prepend()`` tag. + +Binding functions with template parameters +========================================== + +You can bind functions that have template parameters. Here's a function: + +.. code-block:: cpp + + template + void set(T t); + +C++ templates cannot be instantiated at runtime, so you cannot bind the +non-instantiated function: + +.. code-block:: cpp + + // BROKEN (this will not compile) + m.def("set", &set); + +You must bind each instantiated function template separately. You may bind +each instantiation with the same name, which will be treated the same as +an overloaded function: + +.. code-block:: cpp + + m.def("set", &set); + m.def("set", &set); + +Sometimes it's more clear to bind them with separate names, which is also +an option: + +.. code-block:: cpp + + m.def("setInt", &set); + m.def("setString", &set); diff --git a/3rdparty/pybind11/docs/advanced/misc.rst b/3rdparty/pybind11/docs/advanced/misc.rst index b3f3b2265a..edab15fcb7 100644 --- a/3rdparty/pybind11/docs/advanced/misc.rst +++ b/3rdparty/pybind11/docs/advanced/misc.rst @@ -84,7 +84,7 @@ could be realized as follows (important changes highlighted): }); } -The ``call_go`` wrapper can also be simplified using the `call_guard` policy +The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy (see :ref:`call_policies`) which yields the same result: .. code-block:: cpp diff --git a/3rdparty/pybind11/docs/advanced/pycpp/numpy.rst b/3rdparty/pybind11/docs/advanced/pycpp/numpy.rst index 19ed10b3bf..07c969305d 100644 --- a/3rdparty/pybind11/docs/advanced/pycpp/numpy.rst +++ b/3rdparty/pybind11/docs/advanced/pycpp/numpy.rst @@ -87,7 +87,7 @@ buffer objects (e.g. a NumPy matrix). /* Request a buffer descriptor from Python */ py::buffer_info info = b.request(); - /* Some sanity checks ... */ + /* Some basic validation checks ... */ if (info.format != py::format_descriptor::format()) throw std::runtime_error("Incompatible format: expected a double array!"); @@ -150,8 +150,10 @@ NumPy array containing double precision values. When it is invoked with a different type (e.g. an integer or a list of integers), the binding code will attempt to cast the input into a NumPy array -of the requested type. Note that this feature requires the -:file:`pybind11/numpy.h` header to be included. +of the requested type. This feature requires the :file:`pybind11/numpy.h` +header to be included. Note that :file:`pybind11/numpy.h` does not depend on +the NumPy headers, and thus can be used without declaring a build-time +dependency on NumPy; NumPy>=1.7.0 is a runtime dependency. Data in NumPy arrays is not guaranteed to packed in a dense manner; furthermore, entries can be separated by arbitrary column and row strides. @@ -169,6 +171,31 @@ template parameter, and it ensures that non-conforming arguments are converted into an array satisfying the specified requirements instead of trying the next function overload. +There are several methods on arrays; the methods listed below under references +work, as well as the following functions based on the NumPy API: + +- ``.dtype()`` returns the type of the contained values. + +- ``.strides()`` returns a pointer to the strides of the array (optionally pass + an integer axis to get a number). + +- ``.flags()`` returns the flag settings. ``.writable()`` and ``.owndata()`` + are directly available. + +- ``.offset_at()`` returns the offset (optionally pass indices). + +- ``.squeeze()`` returns a view with length-1 axes removed. + +- ``.view(dtype)`` returns a view of the array with a different dtype. + +- ``.reshape({i, j, ...})`` returns a view of the array with a different shape. + ``.resize({...})`` is also available. + +- ``.index_at(i, j, ...)`` gets the count from the beginning to a given index. + + +There are also several methods for getting references (described below). + Structured types ================ @@ -231,8 +258,8 @@ by the compiler. The result is returned as a NumPy array of type .. code-block:: pycon - >>> x = np.array([[1, 3],[5, 7]]) - >>> y = np.array([[2, 4],[6, 8]]) + >>> x = np.array([[1, 3], [5, 7]]) + >>> y = np.array([[2, 4], [6, 8]]) >>> z = 3 >>> result = vectorized_func(x, y, z) @@ -343,21 +370,21 @@ The returned proxy object supports some of the same methods as ``py::array`` so that it can be used as a drop-in replacement for some existing, index-checked uses of ``py::array``: -- ``r.ndim()`` returns the number of dimensions +- ``.ndim()`` returns the number of dimensions -- ``r.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to +- ``.data(1, 2, ...)`` and ``r.mutable_data(1, 2, ...)``` returns a pointer to the ``const T`` or ``T`` data, respectively, at the given indices. The latter is only available to proxies obtained via ``a.mutable_unchecked()``. -- ``itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. +- ``.itemsize()`` returns the size of an item in bytes, i.e. ``sizeof(T)``. -- ``ndim()`` returns the number of dimensions. +- ``.ndim()`` returns the number of dimensions. -- ``shape(n)`` returns the size of dimension ``n`` +- ``.shape(n)`` returns the size of dimension ``n`` -- ``size()`` returns the total number of elements (i.e. the product of the shapes). +- ``.size()`` returns the total number of elements (i.e. the product of the shapes). -- ``nbytes()`` returns the number of bytes used by the referenced elements +- ``.nbytes()`` returns the number of bytes used by the referenced elements (i.e. ``itemsize()`` times ``size()``). .. seealso:: @@ -368,15 +395,13 @@ uses of ``py::array``: Ellipsis ======== -Python 3 provides a convenient ``...`` ellipsis notation that is often used to +Python provides a convenient ``...`` ellipsis notation that is often used to slice multidimensional arrays. For instance, the following snippet extracts the middle dimensions of a tensor with the first and last index set to zero. -In Python 2, the syntactic sugar ``...`` is not available, but the singleton -``Ellipsis`` (of type ``ellipsis``) can still be used directly. .. code-block:: python - a = # a NumPy array + a = ... # a NumPy array b = a[0, ..., 0] The function ``py::ellipsis()`` function can be used to perform the same @@ -387,8 +412,6 @@ operation on the C++ side: py::array a = /* A NumPy array */; py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; -.. versionchanged:: 2.6 - ``py::ellipsis()`` is now also avaliable in Python 2. Memory view =========== @@ -410,7 +433,7 @@ following: { 2, 4 }, // shape (rows, cols) { sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes ); - }) + }); This approach is meant for providing a ``memoryview`` for a C/C++ buffer not managed by Python. The user is responsible for managing the lifetime of the @@ -426,11 +449,7 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer: buffer, // buffer pointer sizeof(uint8_t) * 8 // buffer size ); - }) - -.. note:: - - ``memoryview::from_memory`` is not available in Python 2. + }); .. versionchanged:: 2.6 ``memoryview::from_memory`` added. diff --git a/3rdparty/pybind11/docs/advanced/pycpp/object.rst b/3rdparty/pybind11/docs/advanced/pycpp/object.rst index 6c7525ceaf..93e1a94d8f 100644 --- a/3rdparty/pybind11/docs/advanced/pycpp/object.rst +++ b/3rdparty/pybind11/docs/advanced/pycpp/object.rst @@ -20,6 +20,40 @@ Available types include :class:`handle`, :class:`object`, :class:`bool_`, Be sure to review the :ref:`pytypes_gotchas` before using this heavily in your C++ API. +.. _instantiating_compound_types: + +Instantiating compound Python types from C++ +============================================ + +Dictionaries can be initialized in the :class:`dict` constructor: + +.. code-block:: cpp + + using namespace pybind11::literals; // to bring in the `_a` literal + py::dict d("spam"_a=py::none(), "eggs"_a=42); + +A tuple of python objects can be instantiated using :func:`py::make_tuple`: + +.. code-block:: cpp + + py::tuple tup = py::make_tuple(42, py::none(), "spam"); + +Each element is converted to a supported Python type. + +A `simple namespace`_ can be instantiated using + +.. code-block:: cpp + + using namespace pybind11::literals; // to bring in the `_a` literal + py::object SimpleNamespace = py::module_::import("types").attr("SimpleNamespace"); + py::object ns = SimpleNamespace("spam"_a=py::none(), "eggs"_a=42); + +Attributes on a namespace can be modified with the :func:`py::delattr`, +:func:`py::getattr`, and :func:`py::setattr` functions. Simple namespaces can +be useful as lightweight stand-ins for class instances. + +.. _simple namespace: https://docs.python.org/3/library/types.html#types.SimpleNamespace + .. _casting_back_and_forth: Casting back and forth @@ -30,7 +64,7 @@ types to Python, which can be done using :func:`py::cast`: .. code-block:: cpp - MyClass *cls = ..; + MyClass *cls = ...; py::object obj = py::cast(cls); The reverse direction uses the following syntax: @@ -132,6 +166,7 @@ Keyword arguments are also supported. In Python, there is the usual call syntax: def f(number, say, to): ... # function code + f(1234, say="hello", to=some_instance) # keyword call in Python In C++, the same call can be made using: diff --git a/3rdparty/pybind11/docs/advanced/pycpp/utilities.rst b/3rdparty/pybind11/docs/advanced/pycpp/utilities.rst index c15051fb96..af0f9cb2b0 100644 --- a/3rdparty/pybind11/docs/advanced/pycpp/utilities.rst +++ b/3rdparty/pybind11/docs/advanced/pycpp/utilities.rst @@ -28,7 +28,7 @@ Capturing standard output from ostream Often, a library will use the streams ``std::cout`` and ``std::cerr`` to print, but this does not play well with Python's standard ``sys.stdout`` and ``sys.stderr`` -redirection. Replacing a library's printing with `py::print ` may not +redirection. Replacing a library's printing with ``py::print `` may not be feasible. This can be fixed using a guard around the library function that redirects output to the corresponding Python streams: @@ -47,15 +47,26 @@ redirects output to the corresponding Python streams: call_noisy_func(); }); +.. warning:: + + The implementation in ``pybind11/iostream.h`` is NOT thread safe. Multiple + threads writing to a redirected ostream concurrently cause data races + and potentially buffer overflows. Therefore it is currently a requirement + that all (possibly) concurrent redirected ostream writes are protected by + a mutex. #HelpAppreciated: Work on iostream.h thread safety. For more + background see the discussions under + `PR #2982 `_ and + `PR #2995 `_. + This method respects flushes on the output streams and will flush if needed when the scoped guard is destroyed. This allows the output to be redirected in real time, such as to a Jupyter notebook. The two arguments, the C++ stream and the Python output, are optional, and default to standard output if not given. An -extra type, `py::scoped_estream_redirect `, is identical +extra type, ``py::scoped_estream_redirect ``, is identical except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful with -`py::call_guard`, which allows multiple items, but uses the default constructor: +``py::call_guard``, which allows multiple items, but uses the default constructor: -.. code-block:: py +.. code-block:: cpp // Alternative: Call single function using call guard m.def("noisy_func", &call_noisy_function, @@ -63,7 +74,7 @@ except for defaulting to ``std::cerr`` and ``sys.stderr``; this can be useful wi py::scoped_estream_redirect>()); The redirection can also be done in Python with the addition of a context -manager, using the `py::add_ostream_redirect() ` function: +manager, using the ``py::add_ostream_redirect() `` function: .. code-block:: cpp @@ -92,7 +103,7 @@ arguments to disable one of the streams if needed. Evaluating Python expressions from strings and files ==================================================== -pybind11 provides the `eval`, `exec` and `eval_file` functions to evaluate +pybind11 provides the ``eval``, ``exec`` and ``eval_file`` functions to evaluate Python expressions and statements. The following example illustrates how they can be used. diff --git a/3rdparty/pybind11/docs/advanced/smart_ptrs.rst b/3rdparty/pybind11/docs/advanced/smart_ptrs.rst index da57748ca5..3c40ce1237 100644 --- a/3rdparty/pybind11/docs/advanced/smart_ptrs.rst +++ b/3rdparty/pybind11/docs/advanced/smart_ptrs.rst @@ -77,6 +77,7 @@ segmentation fault). .. code-block:: python from example import Parent + print(Parent().get_child()) The problem is that ``Parent::get_child()`` returns a pointer to an instance of @@ -156,7 +157,7 @@ specialized: PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr); // Only needed if the type's `.get()` goes by another name - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template struct holder_helper> { // <-- specialization static const T *get(const SmartPtr &p) { return p.getPointer(); } diff --git a/3rdparty/pybind11/docs/basics.rst b/3rdparty/pybind11/docs/basics.rst index b9d386c3f3..e9b24c7fa7 100644 --- a/3rdparty/pybind11/docs/basics.rst +++ b/3rdparty/pybind11/docs/basics.rst @@ -32,8 +32,7 @@ The last line will both compile and run the tests. Windows ------- -On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies -on various C++11 language features that break older versions of Visual Studio. +On Windows, only **Visual Studio 2017** and newer are supported. .. Note:: @@ -109,7 +108,7 @@ a file named :file:`example.cpp` with the following contents: PYBIND11_MODULE(example, m) { m.doc() = "pybind11 example plugin"; // optional module docstring - m.def("add", &add, "A function which adds two numbers"); + m.def("add", &add, "A function that adds two numbers"); } .. [#f1] In practice, implementation and binding code will generally be located @@ -136,7 +135,14 @@ On Linux, the above example can be compiled using the following command: .. code-block:: bash - $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) + +.. note:: + + If you used :ref:`include_as_a_submodule` to get the pybind11 source, then + use ``$(python3-config --includes) -Iextern/pybind11/include`` instead of + ``$(python3 -m pybind11 --includes)`` in the above compilation, as + explained in :ref:`building_manually`. For more details on the required compiler flags on Linux and macOS, see :ref:`building_manually`. For complete cross-platform compilation instructions, @@ -159,12 +165,12 @@ load and execute the example: .. code-block:: pycon $ python - Python 2.7.10 (default, Aug 22 2015, 20:33:39) - [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin + Python 3.9.10 (main, Jan 15 2022, 11:48:04) + [Clang 13.0.0 (clang-1300.0.29.3)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import example >>> example.add(1, 2) - 3L + 3 >>> .. _keyword_args: diff --git a/3rdparty/pybind11/docs/benchmark.py b/3rdparty/pybind11/docs/benchmark.py index 33d78fb4e6..2150b6ca78 100644 --- a/3rdparty/pybind11/docs/benchmark.py +++ b/3rdparty/pybind11/docs/benchmark.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- -import random -import os -import time import datetime as dt +import os +import random nfns = 4 # Functions per class nargs = 4 # Arguments per function @@ -13,20 +11,20 @@ def generate_dummy_code_pybind11(nclasses=10): bindings = "" for cl in range(nclasses): - decl += "class cl%03i;\n" % cl + decl += f"class cl{cl:03};\n" decl += "\n" for cl in range(nclasses): - decl += "class cl%03i {\n" % cl + decl += f"class {cl:03} {{\n" decl += "public:\n" - bindings += ' py::class_(m, "cl%03i")\n' % (cl, cl) + bindings += f' py::class_(m, "cl{cl:03}")\n' for fn in range(nfns): ret = random.randint(0, nclasses - 1) params = [random.randint(0, nclasses - 1) for i in range(nargs)] - decl += " cl%03i *fn_%03i(" % (ret, fn) - decl += ", ".join("cl%03i *" % p for p in params) + decl += f" cl{ret:03} *fn_{fn:03}(" + decl += ", ".join(f"cl{p:03} *" for p in params) decl += ");\n" - bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn) + bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n' decl += "};\n\n" bindings += " ;\n" @@ -44,23 +42,20 @@ def generate_dummy_code_boost(nclasses=10): bindings = "" for cl in range(nclasses): - decl += "class cl%03i;\n" % cl + decl += f"class cl{cl:03};\n" decl += "\n" for cl in range(nclasses): decl += "class cl%03i {\n" % cl decl += "public:\n" - bindings += ' py::class_("cl%03i")\n' % (cl, cl) + bindings += f' py::class_("cl{cl:03}")\n' for fn in range(nfns): ret = random.randint(0, nclasses - 1) params = [random.randint(0, nclasses - 1) for i in range(nargs)] - decl += " cl%03i *fn_%03i(" % (ret, fn) - decl += ", ".join("cl%03i *" % p for p in params) + decl += f" cl{ret:03} *fn_{fn:03}(" + decl += ", ".join(f"cl{p:03} *" for p in params) decl += ");\n" - bindings += ( - ' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy())\n' - % (fn, cl, fn) - ) + bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy())\n' decl += "};\n\n" bindings += " ;\n" @@ -76,7 +71,7 @@ def generate_dummy_code_boost(nclasses=10): for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: print("{") for i in range(0, 10): - nclasses = 2 ** i + nclasses = 2**i with open("test.cpp", "w") as f: f.write(codegen(nclasses)) n1 = dt.datetime.now() diff --git a/3rdparty/pybind11/docs/changelog.rst b/3rdparty/pybind11/docs/changelog.rst index 561baa5f1f..9c8ff423f4 100644 --- a/3rdparty/pybind11/docs/changelog.rst +++ b/3rdparty/pybind11/docs/changelog.rst @@ -6,6 +6,931 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. +Changes will be added here periodically from the "Suggested changelog entry" +block in pull request descriptions. + + + +IN DEVELOPMENT +-------------- + +Changes will be summarized here periodically. + +Version 2.10.0 (Jul 15, 2022) +----------------------------- + +Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC +2017 is limited due to availability of CI runners; we highly recommend MSVC +2019 or 2022 be used. Initial support added for Python 3.11. + +New features: + +* ``py::anyset`` & ``py::frozenset`` were added, with copying (cast) to + ``std::set`` (similar to ``set``). + `#3901 `_ + +* Support bytearray casting to string. + `#3707 `_ + +* ``type_caster`` was added. ``std::monostate`` is a tag type + that allows ``std::variant`` to act as an optional, or allows default + construction of a ``std::variant`` holding a non-default constructible type. + `#3818 `_ + +* ``pybind11::capsule::set_name`` added to mutate the name of the capsule instance. + `#3866 `_ + +* NumPy: dtype constructor from type number added, accessors corresponding to + Python API ``dtype.num``, ``dtype.byteorder``, ``dtype.flags`` and + ``dtype.alignment`` added. + `#3868 `_ + + +Changes: + +* Python 3.6 is now the minimum supported version. + `#3688 `_ + `#3719 `_ + +* The minimum version for MSVC is now 2017. + `#3722 `_ + +* Fix issues with CPython 3.11 betas and add to supported test matrix. + `#3923 `_ + +* ``error_already_set`` is now safer and more performant, especially for + exceptions with long tracebacks, by delaying computation. + `#1895 `_ + +* Improve exception handling in python ``str`` bindings. + `#3826 `_ + +* The bindings for capsules now have more consistent exception handling. + `#3825 `_ + +* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can now be + used to define classes in namespaces other than pybind11. + `#3797 `_ + +* Error printing code now uses ``PYBIND11_DETAILED_ERROR_MESSAGES`` instead of + requiring ``NDEBUG``, allowing use with release builds if desired. + `#3913 `_ + +* Implicit conversion of the literal ``0`` to ``pybind11::handle`` is now disabled. + `#4008 `_ + + +Bug fixes: + +* Fix exception handling when ``pybind11::weakref()`` fails. + `#3739 `_ + +* ``module_::def_submodule`` was missing proper error handling. This is fixed now. + `#3973 `_ + +* The behavior or ``error_already_set`` was made safer and the highly opaque + "Unknown internal error occurred" message was replaced with a more helpful + message. + `#3982 `_ + +* ``error_already_set::what()`` now handles non-normalized exceptions correctly. + `#3971 `_ + +* Support older C++ compilers where filesystem is not yet part of the standard + library and is instead included in ``std::experimental::filesystem``. + `#3840 `_ + +* Fix ``-Wfree-nonheap-object`` warnings produced by GCC by avoiding returning + pointers to static objects with ``return_value_policy::take_ownership``. + `#3946 `_ + +* Fix cast from pytype rvalue to another pytype. + `#3949 `_ + +* Ensure proper behavior when garbage collecting classes with dynamic attributes in Python >=3.9. + `#4051 `_ + +* A couple long-standing ``PYBIND11_NAMESPACE`` + ``__attribute__((visibility("hidden")))`` inconsistencies are now fixed + (affects only unusual environments). + `#4043 `_ + +* ``pybind11::detail::get_internals()`` is now resilient to in-flight Python + exceptions. + `#3981 `_ + +* Arrays with a dimension of size 0 are now properly converted to dynamic Eigen + matrices (more common in NumPy 1.23). + `#4038 `_ + +* Avoid catching unrelated errors when importing NumPy. + `#3974 `_ + +Performance and style: + +* Added an accessor overload of ``(object &&key)`` to reference steal the + object when using python types as keys. This prevents unnecessary reference + count overhead for attr, dictionary, tuple, and sequence look ups. Added + additional regression tests. Fixed a performance bug the caused accessor + assignments to potentially perform unnecessary copies. + `#3970 `_ + +* Perfect forward all args of ``make_iterator``. + `#3980 `_ + +* Avoid potential bug in pycapsule destructor by adding an ``error_guard`` to + one of the dtors. + `#3958 `_ + +* Optimize dictionary access in ``strip_padding`` for numpy. + `#3994 `_ + +* ``stl_bind.h`` bindings now take slice args as a const-ref. + `#3852 `_ + +* Made slice constructor more consistent, and improve performance of some + casters by allowing reference stealing. + `#3845 `_ + +* Change numpy dtype from_args method to use const ref. + `#3878 `_ + +* Follow rule of three to ensure ``PyErr_Restore`` is called only once. + `#3872 `_ + +* Added missing perfect forwarding for ``make_iterator`` functions. + `#3860 `_ + +* Optimize c++ to python function casting by using the rvalue caster. + `#3966 `_ + +* Optimize Eigen sparse matrix casting by removing unnecessary temporary. + `#4064 `_ + +* Avoid potential implicit copy/assignment constructors causing double free in + ``strdup_gaurd``. + `#3905 `_ + +* Enable clang-tidy checks ``misc-definitions-in-headers``, + ``modernize-loop-convert``, and ``modernize-use-nullptr``. + `#3881 `_ + `#3988 `_ + + +Build system improvements: + +* CMake: Fix file extension on Windows with cp36 and cp37 using FindPython. + `#3919 `_ + +* CMake: Support multiple Python targets (such as on vcpkg). + `#3948 `_ + +* CMake: Fix issue with NVCC on Windows. + `#3947 `_ + +* CMake: Drop the bitness check on cross compiles (like targeting WebAssembly + via Emscripten). + `#3959 `_ + +* Add MSVC builds in debug mode to CI. + `#3784 `_ + +* MSVC 2022 C++20 coverage was added to GitHub Actions, including Eigen. + `#3732 `_, + `#3741 `_ + + +Backend and tidying up: + +* New theme for the documentation. + `#3109 `_ + +* Remove idioms in code comments. Use more inclusive language. + `#3809 `_ + +* ``#include `` was removed from the ``pybind11/stl.h`` header. Your + project may break if it has a transitive dependency on this include. The fix + is to "Include What You Use". + `#3928 `_ + +* Avoid ``setup.py `` usage in internal tests. + `#3734 `_ + + +Version 2.9.2 (Mar 29, 2022) +---------------------------- + +Changes: + +* Enum now has an ``__index__`` method on Python <3.8 too. + `#3700 `_ + +* Local internals are now cleared after finalizing the interpreter. + `#3744 `_ + +Bug fixes: + +* Better support for Python 3.11 alphas. + `#3694 `_ + +* ``PYBIND11_TYPE_CASTER`` now uses fully qualified symbols, so it can be used + outside of ``pybind11::detail``. + `#3758 `_ + +* Some fixes for PyPy 3.9. + `#3768 `_ + +* Fixed a potential memleak in PyPy in ``get_type_override``. + `#3774 `_ + +* Fix usage of ``VISIBILITY_INLINES_HIDDEN``. + `#3721 `_ + + +Build system improvements: + +* Uses ``sysconfig`` module to determine installation locations on Python >= + 3.10, instead of ``distutils`` which has been deprecated. + `#3764 `_ + +* Support Catch 2.13.5+ (supporting GLIBC 2.34+). + `#3679 `_ + +* Fix test failures with numpy 1.22 by ignoring whitespace when comparing + ``str()`` of dtypes. + `#3682 `_ + + +Backend and tidying up: + +* clang-tidy: added ``readability-qualified-auto``, + ``readability-braces-around-statements``, + ``cppcoreguidelines-prefer-member-initializer``, + ``clang-analyzer-optin.performance.Padding``, + ``cppcoreguidelines-pro-type-static-cast-downcast``, and + ``readability-inconsistent-declaration-parameter-name``. + `#3702 `_, + `#3699 `_, + `#3716 `_, + `#3709 `_ + +* clang-format was added to the pre-commit actions, and the entire code base + automatically reformatted (after several iterations preparing for this leap). + `#3713 `_ + + +Version 2.9.1 (Feb 2, 2022) +--------------------------- + +Changes: + +* If possible, attach Python exception with ``py::raise_from`` to ``TypeError`` + when casting from C++ to Python. This will give additional info if Python + exceptions occur in the caster. Adds a test case of trying to convert a set + from C++ to Python when the hash function is not defined in Python. + `#3605 `_ + +* Add a mapping of C++11 nested exceptions to their Python exception + equivalent using ``py::raise_from``. This attaches the nested exceptions in + Python using the ``__cause__`` field. + `#3608 `_ + +* Propagate Python exception traceback using ``raise_from`` if a pybind11 + function runs out of overloads. + `#3671 `_ + +* ``py::multiple_inheritance`` is now only needed when C++ bases are hidden + from pybind11. + `#3650 `_ and + `#3659 `_ + + +Bug fixes: + +* Remove a boolean cast in ``numpy.h`` that causes MSVC C4800 warnings when + compiling against Python 3.10 or newer. + `#3669 `_ + +* Render ``py::bool_`` and ``py::float_`` as ``bool`` and ``float`` + respectively. + `#3622 `_ + +Build system improvements: + +* Fix CMake extension suffix computation on Python 3.10+. + `#3663 `_ + +* Allow ``CMAKE_ARGS`` to override CMake args in pybind11's own ``setup.py``. + `#3577 `_ + +* Remove a few deprecated c-headers. + `#3610 `_ + +* More uniform handling of test targets. + `#3590 `_ + +* Add clang-tidy readability check to catch potentially swapped function args. + `#3611 `_ + + +Version 2.9.0 (Dec 28, 2021) +---------------------------- + +This is the last version to support Python 2.7 and 3.5. + +New Features: + +* Allow ``py::args`` to be followed by other arguments; the remaining arguments + are implicitly keyword-only, as if a ``py::kw_only{}`` annotation had been + used. + `#3402 `_ + +Changes: + +* Make str/bytes/memoryview more interoperable with ``std::string_view``. + `#3521 `_ + +* Replace ``_`` with ``const_name`` in internals, avoid defining ``pybind::_`` + if ``_`` defined as macro (common gettext usage) + `#3423 `_ + + +Bug fixes: + +* Fix a rare warning about extra copy in an Eigen constructor. + `#3486 `_ + +* Fix caching of the C++ overrides. + `#3465 `_ + +* Add missing ``std::forward`` calls to some ``cpp_function`` overloads. + `#3443 `_ + +* Support PyPy 7.3.7 and the PyPy3.8 beta. Test python-3.11 on PRs with the + ``python dev`` label. + `#3419 `_ + +* Replace usage of deprecated ``Eigen::MappedSparseMatrix`` with + ``Eigen::Map>`` for Eigen 3.3+. + `#3499 `_ + +* Tweaks to support Microsoft Visual Studio 2022. + `#3497 `_ + +Build system improvements: + +* Nicer CMake printout and IDE organisation for pybind11's own tests. + `#3479 `_ + +* CMake: report version type as part of the version string to avoid a spurious + space in the package status message. + `#3472 `_ + +* Flags starting with ``-g`` in ``$CFLAGS`` and ``$CPPFLAGS`` are no longer + overridden by ``.Pybind11Extension``. + `#3436 `_ + +* Ensure ThreadPool is closed in ``setup_helpers``. + `#3548 `_ + +* Avoid LTS on ``mips64`` and ``ppc64le`` (reported broken). + `#3557 `_ + + +v2.8.1 (Oct 27, 2021) +--------------------- + +Changes and additions: + +* The simple namespace creation shortcut added in 2.8.0 was deprecated due to + usage of CPython internal API, and will be removed soon. Use + ``py::module_::import("types").attr("SimpleNamespace")``. + `#3374 `_ + +* Add C++ Exception type to throw and catch ``AttributeError``. Useful for + defining custom ``__setattr__`` and ``__getattr__`` methods. + `#3387 `_ + +Fixes: + +* Fixed the potential for dangling references when using properties with + ``std::optional`` types. + `#3376 `_ + +* Modernize usage of ``PyCodeObject`` on Python 3.9+ (moving toward support for + Python 3.11a1) + `#3368 `_ + +* A long-standing bug in ``eigen.h`` was fixed (originally PR #3343). The bug + was unmasked by newly added ``static_assert``'s in the Eigen 3.4.0 release. + `#3352 `_ + +* Support multiple raw inclusion of CMake helper files (Conan.io does this for + multi-config generators). + `#3420 `_ + +* Fix harmless warning on upcoming CMake 3.22. + `#3368 `_ + +* Fix 2.8.0 regression with MSVC 2017 + C++17 mode + Python 3. + `#3407 `_ + +* Fix 2.8.0 regression that caused undefined behavior (typically + segfaults) in ``make_key_iterator``/``make_value_iterator`` if dereferencing + the iterator returned a temporary value instead of a reference. + `#3348 `_ + + +v2.8.0 (Oct 4, 2021) +-------------------- + +New features: + +* Added ``py::raise_from`` to enable chaining exceptions. + `#3215 `_ + +* Allow exception translators to be optionally registered local to a module + instead of applying globally across all pybind11 modules. Use + ``register_local_exception_translator(ExceptionTranslator&& translator)`` + instead of ``register_exception_translator(ExceptionTranslator&& + translator)`` to keep your exception remapping code local to the module. + `#2650 `_ + +* Add ``make_simple_namespace`` function for instantiating Python + ``SimpleNamespace`` objects. **Deprecated in 2.8.1.** + `#2840 `_ + +* ``pybind11::scoped_interpreter`` and ``initialize_interpreter`` have new + arguments to allow ``sys.argv`` initialization. + `#2341 `_ + +* Allow Python builtins to be used as callbacks in CPython. + `#1413 `_ + +* Added ``view`` to view arrays with a different datatype. + `#987 `_ + +* Implemented ``reshape`` on arrays. + `#984 `_ + +* Enable defining custom ``__new__`` methods on classes by fixing bug + preventing overriding methods if they have non-pybind11 siblings. + `#3265 `_ + +* Add ``make_value_iterator()``, and fix ``make_key_iterator()`` to return + references instead of copies. + `#3293 `_ + +* Improve the classes generated by ``bind_map``: `#3310 `_ + + * Change ``.items`` from an iterator to a dictionary view. + * Add ``.keys`` and ``.values`` (both dictionary views). + * Allow ``__contains__`` to take any object. + +* ``pybind11::custom_type_setup`` was added, for customizing the + ``PyHeapTypeObject`` corresponding to a class, which may be useful for + enabling garbage collection support, among other things. + `#3287 `_ + + +Changes: + +* Set ``__file__`` constant when running ``eval_file`` in an embedded interpreter. + `#3233 `_ + +* Python objects and (C++17) ``std::optional`` now accepted in ``py::slice`` + constructor. + `#1101 `_ + +* The pybind11 proxy types ``str``, ``bytes``, ``bytearray``, ``tuple``, + ``list`` now consistently support passing ``ssize_t`` values for sizes and + indexes. Previously, only ``size_t`` was accepted in several interfaces. + `#3219 `_ + +* Avoid evaluating ``PYBIND11_TLS_REPLACE_VALUE`` arguments more than once. + `#3290 `_ + +Fixes: + +* Bug fix: enum value's ``__int__`` returning non-int when underlying type is + bool or of char type. + `#1334 `_ + +* Fixes bug in setting error state in Capsule's pointer methods. + `#3261 `_ + +* A long-standing memory leak in ``py::cpp_function::initialize`` was fixed. + `#3229 `_ + +* Fixes thread safety for some ``pybind11::type_caster`` which require lifetime + extension, such as for ``std::string_view``. + `#3237 `_ + +* Restore compatibility with gcc 4.8.4 as distributed by ubuntu-trusty, linuxmint-17. + `#3270 `_ + + +Build system improvements: + +* Fix regression in CMake Python package config: improper use of absolute path. + `#3144 `_ + +* Cached Python version information could become stale when CMake was re-run + with a different Python version. The build system now detects this and + updates this information. + `#3299 `_ + +* Specified UTF8-encoding in setup.py calls of open(). + `#3137 `_ + +* Fix a harmless warning from CMake 3.21 with the classic Python discovery. + `#3220 `_ + +* Eigen repo and version can now be specified as cmake options. + `#3324 `_ + + +Backend and tidying up: + +* Reduced thread-local storage required for keeping alive temporary data for + type conversion to one key per ABI version, rather than one key per extension + module. This makes the total thread-local storage required by pybind11 2 + keys per ABI version. + `#3275 `_ + +* Optimize NumPy array construction with additional moves. + `#3183 `_ + +* Conversion to ``std::string`` and ``std::string_view`` now avoids making an + extra copy of the data on Python >= 3.3. + `#3257 `_ + +* Remove const modifier from certain C++ methods on Python collections + (``list``, ``set``, ``dict``) such as (``clear()``, ``append()``, + ``insert()``, etc...) and annotated them with ``py-non-const``. + +* Enable readability ``clang-tidy-const-return`` and remove useless consts. + `#3254 `_ + `#3194 `_ + +* The clang-tidy ``google-explicit-constructor`` option was enabled. + `#3250 `_ + +* Mark a pytype move constructor as noexcept (perf). + `#3236 `_ + +* Enable clang-tidy check to guard against inheritance slicing. + `#3210 `_ + +* Legacy warning suppression pragma were removed from eigen.h. On Unix + platforms, please use -isystem for Eigen include directories, to suppress + compiler warnings originating from Eigen headers. Note that CMake does this + by default. No adjustments are needed for Windows. + `#3198 `_ + +* Format pybind11 with isort consistent ordering of imports + `#3195 `_ + +* The warnings-suppression "pragma clamp" at the top/bottom of pybind11 was + removed, clearing the path to refactoring and IWYU cleanup. + `#3186 `_ + +* Enable most bugprone checks in clang-tidy and fix the found potential bugs + and poor coding styles. + `#3166 `_ + +* Add ``clang-tidy-readability`` rules to make boolean casts explicit improving + code readability. Also enabled other misc and readability clang-tidy checks. + `#3148 `_ + +* Move object in ``.pop()`` for list. + `#3116 `_ + + + + +v2.7.1 (Aug 3, 2021) +--------------------- + +Minor missing functionality added: + +* Allow Python builtins to be used as callbacks in CPython. + `#1413 `_ + +Bug fixes: + +* Fix regression in CMake Python package config: improper use of absolute path. + `#3144 `_ + +* Fix Mingw64 and add to the CI testing matrix. + `#3132 `_ + +* Specified UTF8-encoding in setup.py calls of open(). + `#3137 `_ + +* Add clang-tidy-readability rules to make boolean casts explicit improving + code readability. Also enabled other misc and readability clang-tidy checks. + `#3148 `_ + +* Move object in ``.pop()`` for list. + `#3116 `_ + +Backend and tidying up: + +* Removed and fixed warning suppressions. + `#3127 `_ + `#3129 `_ + `#3135 `_ + `#3141 `_ + `#3142 `_ + `#3150 `_ + `#3152 `_ + `#3160 `_ + `#3161 `_ + + +v2.7.0 (Jul 16, 2021) +--------------------- + +New features: + +* Enable ``py::implicitly_convertible`` for + ``py::class_``-wrapped types. + `#3059 `_ + +* Allow function pointer extraction from overloaded functions. + `#2944 `_ + +* NumPy: added ``.char_()`` to type which gives the NumPy public ``char`` + result, which also distinguishes types by bit length (unlike ``.kind()``). + `#2864 `_ + +* Add ``pybind11::bytearray`` to manipulate ``bytearray`` similar to ``bytes``. + `#2799 `_ + +* ``pybind11/stl/filesystem.h`` registers a type caster that, on C++17/Python + 3.6+, converts ``std::filesystem::path`` to ``pathlib.Path`` and any + ``os.PathLike`` to ``std::filesystem::path``. + `#2730 `_ + +* A ``PYBIND11_VERSION_HEX`` define was added, similar to ``PY_VERSION_HEX``. + `#3120 `_ + + + +Changes: + +* ``py::str`` changed to exclusively hold ``PyUnicodeObject``. Previously + ``py::str`` could also hold ``bytes``, which is probably surprising, was + never documented, and can mask bugs (e.g. accidental use of ``py::str`` + instead of ``py::bytes``). + `#2409 `_ + +* Add a safety guard to ensure that the Python GIL is held when C++ calls back + into Python via ``object_api<>::operator()`` (e.g. ``py::function`` + ``__call__``). (This feature is available for Python 3.6+ only.) + `#2919 `_ + +* Catch a missing ``self`` argument in calls to ``__init__()``. + `#2914 `_ + +* Use ``std::string_view`` if available to avoid a copy when passing an object + to a ``std::ostream``. + `#3042 `_ + +* An important warning about thread safety was added to the ``iostream.h`` + documentation; attempts to make ``py::scoped_ostream_redirect`` thread safe + have been removed, as it was only partially effective. + `#2995 `_ + + +Fixes: + +* Performance: avoid unnecessary strlen calls. + `#3058 `_ + +* Fix auto-generated documentation string when using ``const T`` in + ``pyarray_t``. + `#3020 `_ + +* Unify error messages thrown by ``simple_collector``/``unpacking_collector``. + `#3013 `_ + +* ``pybind11::builtin_exception`` is now explicitly exported, which means the + types included/defined in different modules are identical, and exceptions + raised in different modules can be caught correctly. The documentation was + updated to explain that custom exceptions that are used across module + boundaries need to be explicitly exported as well. + `#2999 `_ + +* Fixed exception when printing UTF-8 to a ``scoped_ostream_redirect``. + `#2982 `_ + +* Pickle support enhancement: ``setstate`` implementation will attempt to + ``setattr`` ``__dict__`` only if the unpickled ``dict`` object is not empty, + to not force use of ``py::dynamic_attr()`` unnecessarily. + `#2972 `_ + +* Allow negative timedelta values to roundtrip. + `#2870 `_ + +* Fix unchecked errors could potentially swallow signals/other exceptions. + `#2863 `_ + +* Add null pointer check with ``std::localtime``. + `#2846 `_ + +* Fix the ``weakref`` constructor from ``py::object`` to create a new + ``weakref`` on conversion. + `#2832 `_ + +* Avoid relying on exceptions in C++17 when getting a ``shared_ptr`` holder + from a ``shared_from_this`` class. + `#2819 `_ + +* Allow the codec's exception to be raised instead of :code:`RuntimeError` when + casting from :code:`py::str` to :code:`std::string`. + `#2903 `_ + + +Build system improvements: + +* In ``setup_helpers.py``, test for platforms that have some multiprocessing + features but lack semaphores, which ``ParallelCompile`` requires. + `#3043 `_ + +* Fix ``pybind11_INCLUDE_DIR`` in case ``CMAKE_INSTALL_INCLUDEDIR`` is + absolute. + `#3005 `_ + +* Fix bug not respecting ``WITH_SOABI`` or ``WITHOUT_SOABI`` to CMake. + `#2938 `_ + +* Fix the default ``Pybind11Extension`` compilation flags with a Mingw64 python. + `#2921 `_ + +* Clang on Windows: do not pass ``/MP`` (ignored flag). + `#2824 `_ + +* ``pybind11.setup_helpers.intree_extensions`` can be used to generate + ``Pybind11Extension`` instances from cpp files placed in the Python package + source tree. + `#2831 `_ + +Backend and tidying up: + +* Enable clang-tidy performance, readability, and modernization checks + throughout the codebase to enforce best coding practices. + `#3046 `_, + `#3049 `_, + `#3051 `_, + `#3052 `_, + `#3080 `_, and + `#3094 `_ + + +* Checks for common misspellings were added to the pre-commit hooks. + `#3076 `_ + +* Changed ``Werror`` to stricter ``Werror-all`` for Intel compiler and fixed + minor issues. + `#2948 `_ + +* Fixed compilation with GCC < 5 when the user defines ``_GLIBCXX_USE_CXX11_ABI``. + `#2956 `_ + +* Added nox support for easier local testing and linting of contributions. + `#3101 `_ and + `#3121 `_ + +* Avoid RTD style issue with docutils 0.17+. + `#3119 `_ + +* Support pipx run, such as ``pipx run pybind11 --include`` for a quick compile. + `#3117 `_ + + + +v2.6.2 (Jan 26, 2021) +--------------------- + +Minor missing functionality added: + +* enum: add missing Enum.value property. + `#2739 `_ + +* Allow thread termination to be avoided during shutdown for CPython 3.7+ via + ``.disarm`` for ``gil_scoped_acquire``/``gil_scoped_release``. + `#2657 `_ + +Fixed or improved behavior in a few special cases: + +* Fix bug where the constructor of ``object`` subclasses would not throw on + being passed a Python object of the wrong type. + `#2701 `_ + +* The ``type_caster`` for integers does not convert Python objects with + ``__int__`` anymore with ``noconvert`` or during the first round of trying + overloads. + `#2698 `_ + +* When casting to a C++ integer, ``__index__`` is always called and not + considered as conversion, consistent with Python 3.8+. + `#2801 `_ + +Build improvements: + +* Setup helpers: ``extra_compile_args`` and ``extra_link_args`` automatically set by + Pybind11Extension are now prepended, which allows them to be overridden + by user-set ``extra_compile_args`` and ``extra_link_args``. + `#2808 `_ + +* Setup helpers: Don't trigger unused parameter warning. + `#2735 `_ + +* CMake: Support running with ``--warn-uninitialized`` active. + `#2806 `_ + +* CMake: Avoid error if included from two submodule directories. + `#2804 `_ + +* CMake: Fix ``STATIC`` / ``SHARED`` being ignored in FindPython mode. + `#2796 `_ + +* CMake: Respect the setting for ``CMAKE_CXX_VISIBILITY_PRESET`` if defined. + `#2793 `_ + +* CMake: Fix issue with FindPython2/FindPython3 not working with ``pybind11::embed``. + `#2662 `_ + +* CMake: mixing local and installed pybind11's would prioritize the installed + one over the local one (regression in 2.6.0). + `#2716 `_ + + +Bug fixes: + +* Fixed segfault in multithreaded environments when using + ``scoped_ostream_redirect``. + `#2675 `_ + +* Leave docstring unset when all docstring-related options are disabled, rather + than set an empty string. + `#2745 `_ + +* The module key in builtins that pybind11 uses to store its internals changed + from std::string to a python str type (more natural on Python 2, no change on + Python 3). + `#2814 `_ + +* Fixed assertion error related to unhandled (later overwritten) exception in + CPython 3.8 and 3.9 debug builds. + `#2685 `_ + +* Fix ``py::gil_scoped_acquire`` assert with CPython 3.9 debug build. + `#2683 `_ + +* Fix issue with a test failing on pytest 6.2. + `#2741 `_ + +Warning fixes: + +* Fix warning modifying constructor parameter 'flag' that shadows a field of + 'set_flag' ``[-Wshadow-field-in-constructor-modified]``. + `#2780 `_ + +* Suppressed some deprecation warnings about old-style + ``__init__``/``__setstate__`` in the tests. + `#2759 `_ + +Valgrind work: + +* Fix invalid access when calling a pybind11 ``__init__`` on a non-pybind11 + class instance. + `#2755 `_ + +* Fixed various minor memory leaks in pybind11's test suite. + `#2758 `_ + +* Resolved memory leak in cpp_function initialization when exceptions occurred. + `#2756 `_ + +* Added a Valgrind build, checking for leaks and memory-related UB, to CI. + `#2746 `_ + +Compiler support: + +* Intel compiler was not activating C++14 support due to a broken define. + `#2679 `_ + +* Support ICC and NVIDIA HPC SDK in C++17 mode. + `#2729 `_ + +* Support Intel OneAPI compiler (ICC 20.2) and add to CI. + `#2573 `_ + + + v2.6.1 (Nov 11, 2020) --------------------- @@ -14,7 +939,7 @@ v2.6.1 (Nov 11, 2020) and ``eval`` in pure Python. `#2616 `_ -* ``setup_helpers`` will no longer set a minimum macOS version lower than the +* ``setup_helpers`` will no longer set a minimum macOS version higher than the current version. `#2622 `_ @@ -142,7 +1067,7 @@ Packaging / building improvements: `#2338 `_ and `#2370 `_ - * Full integration with CMake’s C++ standard system and compile features + * Full integration with CMake's C++ standard system and compile features replaces ``PYBIND11_CPP_STANDARD``. * Generated config file is now portable to different Python/compiler/CMake @@ -374,7 +1299,7 @@ v2.4.0 (Sep 19, 2019) `#1888 `_. * ``py::details::overload_cast_impl`` is available in C++11 mode, can be used - like ``overload_cast`` with an additional set of parantheses. + like ``overload_cast`` with an additional set of parentheses. `#1581 `_. * Fixed ``get_include()`` on Conda. @@ -696,6 +1621,7 @@ v2.2.0 (August 31, 2017) from cpp_module import CppBase1, CppBase2 + class PyDerived(CppBase1, CppBase2): def __init__(self): CppBase1.__init__(self) # C++ bases must be initialized explicitly @@ -908,7 +1834,7 @@ v2.2.0 (August 31, 2017) * Intel C++ compiler compatibility fixes. `#937 `_. -* Fixed implicit conversion of `py::enum_` to integer types on Python 2.7. +* Fixed implicit conversion of ``py::enum_`` to integer types on Python 2.7. `#821 `_. * Added ``py::hash`` to fetch the hash value of Python objects, and diff --git a/3rdparty/pybind11/docs/classes.rst b/3rdparty/pybind11/docs/classes.rst index f3610ef367..c0c53135b8 100644 --- a/3rdparty/pybind11/docs/classes.rst +++ b/3rdparty/pybind11/docs/classes.rst @@ -44,14 +44,14 @@ interactive Python session demonstrating this example is shown below: % python >>> import example - >>> p = example.Pet('Molly') + >>> p = example.Pet("Molly") >>> print(p) >>> p.getName() - u'Molly' - >>> p.setName('Charly') + 'Molly' + >>> p.setName("Charly") >>> p.getName() - u'Charly' + 'Charly' .. seealso:: @@ -122,12 +122,12 @@ This makes it possible to write .. code-block:: pycon - >>> p = example.Pet('Molly') + >>> p = example.Pet("Molly") >>> p.name - u'Molly' - >>> p.name = 'Charly' + 'Molly' + >>> p.name = "Charly" >>> p.name - u'Charly' + 'Charly' Now suppose that ``Pet::name`` was a private internal variable that can only be accessed via setters and getters. @@ -174,10 +174,10 @@ Native Python classes can pick up new attributes dynamically: .. code-block:: pycon >>> class Pet: - ... name = 'Molly' + ... name = "Molly" ... >>> p = Pet() - >>> p.name = 'Charly' # overwrite existing + >>> p.name = "Charly" # overwrite existing >>> p.age = 2 # dynamically add a new attribute By default, classes exported from C++ do not support this and the only writable @@ -195,7 +195,7 @@ Trying to set any other attribute results in an error: .. code-block:: pycon >>> p = example.Pet() - >>> p.name = 'Charly' # OK, attribute defined in C++ + >>> p.name = "Charly" # OK, attribute defined in C++ >>> p.age = 2 # fail AttributeError: 'Pet' object has no attribute 'age' @@ -213,7 +213,7 @@ Now everything works as expected: .. code-block:: pycon >>> p = example.Pet() - >>> p.name = 'Charly' # OK, overwrite value in C++ + >>> p.name = "Charly" # OK, overwrite value in C++ >>> p.age = 2 # OK, dynamically add a new attribute >>> p.__dict__ # just like a native Python class {'age': 2} @@ -280,11 +280,11 @@ expose fields and methods of both types: .. code-block:: pycon - >>> p = example.Dog('Molly') + >>> p = example.Dog("Molly") >>> p.name - u'Molly' + 'Molly' >>> p.bark() - u'woof!' + 'woof!' The C++ classes defined above are regular non-polymorphic types with an inheritance relationship. This is reflected in Python: @@ -332,7 +332,7 @@ will automatically recognize this: >>> type(p) PolymorphicDog # automatically downcast >>> p.bark() - u'woof!' + 'woof!' Given a pointer to a polymorphic base, pybind11 performs automatic downcasting to the actual derived type. Note that this goes beyond the usual situation in @@ -434,8 +434,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth .def("set", overload_cast_()(&Pet::set), "Set the pet's age") .def("set", overload_cast_()(&Pet::set), "Set the pet's name"); -.. [#cpp14] A compiler which supports the ``-std=c++14`` flag - or Visual Studio 2015 Update 2 and newer. +.. [#cpp14] A compiler which supports the ``-std=c++14`` flag. .. note:: @@ -446,8 +445,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth Enumerations and internal types =============================== -Let's now suppose that the example class contains an internal enumeration type, -e.g.: +Let's now suppose that the example class contains internal types like enumerations, e.g.: .. code-block:: cpp @@ -457,10 +455,15 @@ e.g.: Cat }; + struct Attributes { + float age = 0; + }; + Pet(const std::string &name, Kind type) : name(name), type(type) { } std::string name; Kind type; + Attributes attr; }; The binding code for this example looks as follows: @@ -471,22 +474,28 @@ The binding code for this example looks as follows: pet.def(py::init()) .def_readwrite("name", &Pet::name) - .def_readwrite("type", &Pet::type); + .def_readwrite("type", &Pet::type) + .def_readwrite("attr", &Pet::attr); py::enum_(pet, "Kind") .value("Dog", Pet::Kind::Dog) .value("Cat", Pet::Kind::Cat) .export_values(); -To ensure that the ``Kind`` type is created within the scope of ``Pet``, the -``pet`` :class:`class_` instance must be supplied to the :class:`enum_`. + py::class_(pet, "Attributes") + .def(py::init<>()) + .def_readwrite("age", &Pet::Attributes::age); + + +To ensure that the nested types ``Kind`` and ``Attributes`` are created within the scope of ``Pet``, the +``pet`` :class:`class_` instance must be supplied to the :class:`enum_` and :class:`class_` constructor. The :func:`enum_::export_values` function exports the enum entries into the parent scope, which should be skipped for newer C++11-style strongly typed enums. .. code-block:: pycon - >>> p = Pet('Lucy', Pet.Cat) + >>> p = Pet("Lucy", Pet.Cat) >>> p.type Kind.Cat >>> int(p.type) @@ -508,7 +517,7 @@ The ``name`` property returns the name of the enum value as a unicode string. .. code-block:: pycon - >>> p = Pet( "Lucy", Pet.Cat ) + >>> p = Pet("Lucy", Pet.Cat) >>> pet_type = p.type >>> pet_type Pet.Cat diff --git a/3rdparty/pybind11/docs/compiling.rst b/3rdparty/pybind11/docs/compiling.rst index f26e6cf602..2b543be0be 100644 --- a/3rdparty/pybind11/docs/compiling.rst +++ b/3rdparty/pybind11/docs/compiling.rst @@ -42,10 +42,7 @@ An example of a ``setup.py`` using pybind11's helpers: ), ] - setup( - ..., - ext_modules=ext_modules - ) + setup(..., ext_modules=ext_modules) If you want to do an automatic search for the highest supported C++ standard, that is supported via a ``build_ext`` command override; it will only affect @@ -64,11 +61,20 @@ that is supported via a ``build_ext`` command override; it will only affect ), ] - setup( - ..., - cmdclass={"build_ext": build_ext}, - ext_modules=ext_modules - ) + setup(..., cmdclass={"build_ext": build_ext}, ext_modules=ext_modules) + +If you have single-file extension modules that are directly stored in the +Python source tree (``foo.cpp`` in the same directory as where a ``foo.py`` +would be located), you can also generate ``Pybind11Extensions`` using +``setup_helpers.intree_extensions``: ``intree_extensions(["path/to/foo.cpp", +...])`` returns a list of ``Pybind11Extensions`` which can be passed to +``ext_modules``, possibly after further customizing their attributes +(``libraries``, ``include_dirs``, etc.). By doing so, a ``foo.*.so`` extension +module will be generated and made available upon installation. + +``intree_extension`` will automatically detect if you are using a ``src``-style +layout (as long as no namespace packages are involved), but you can also +explicitly pass ``package_dir`` to it (as in ``setuptools.setup``). Since pybind11 does not require NumPy when building, a light-weight replacement for NumPy's parallel compilation distutils tool is included. Use it like this: @@ -84,22 +90,23 @@ for NumPy's parallel compilation distutils tool is included. Use it like this: The argument is the name of an environment variable to control the number of threads, such as ``NPY_NUM_BUILD_JOBS`` (as used by NumPy), though you can set -something different if you want. You can also pass ``default=N`` to set the -default number of threads (0 will take the number of threads available) and -``max=N``, the maximum number of threads; if you have a large extension you may -want set this to a memory dependent number. +something different if you want; ``CMAKE_BUILD_PARALLEL_LEVEL`` is another choice +a user might expect. You can also pass ``default=N`` to set the default number +of threads (0 will take the number of threads available) and ``max=N``, the +maximum number of threads; if you have a large extension you may want set this +to a memory dependent number. If you are developing rapidly and have a lot of C++ files, you may want to avoid rebuilding files that have not changed. For simple cases were you are using ``pip install -e .`` and do not have local headers, you can skip the -rebuild if a object file is newer than it's source (headers are not checked!) +rebuild if an object file is newer than its source (headers are not checked!) with the following: .. code-block:: python from pybind11.setup_helpers import ParallelCompile, naive_recompile - SmartCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() + ParallelCompile("NPY_NUM_BUILD_JOBS", needs_recompile=naive_recompile).install() If you have a more complex build, you can implement a smarter function and pass @@ -136,7 +143,7 @@ Your ``pyproject.toml`` file will likely look something like this: .. code-block:: toml [build-system] - requires = ["setuptools", "wheel", "pybind11==2.6.0"] + requires = ["setuptools>=42", "wheel", "pybind11~=2.6.1"] build-backend = "setuptools.build_meta" .. note:: @@ -147,10 +154,12 @@ Your ``pyproject.toml`` file will likely look something like this: in Python) using something like `cibuildwheel`_, remember that ``setup.py`` and ``pyproject.toml`` are not even contained in the wheel, so this high Pip requirement is only for source builds, and will not affect users of - your binary wheels. + your binary wheels. If you are building SDists and wheels, then + `pypa-build`_ is the recommended official tool. .. _PEP 517: https://www.python.org/dev/peps/pep-0517/ .. _cibuildwheel: https://cibuildwheel.readthedocs.io +.. _pypa-build: https://pypa-build.readthedocs.io/en/latest/ .. _setup_helpers-setup_requires: @@ -331,7 +340,7 @@ standard explicitly with set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ version selection") # or 11, 14, 17, 20 set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported - set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensionsn off + set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensions off The variables can also be set when calling CMake from the command line using the ``-D=`` flag. You can also manually set ``CXX_STANDARD`` @@ -401,16 +410,17 @@ can refer to the same [cmake_example]_ repository for a full sample project FindPython mode --------------- -CMake 3.12+ (3.15+ recommended) added a new module called FindPython that had a -highly improved search algorithm and modern targets and tools. If you use -FindPython, pybind11 will detect this and use the existing targets instead: +CMake 3.12+ (3.15+ recommended, 3.18.2+ ideal) added a new module called +FindPython that had a highly improved search algorithm and modern targets +and tools. If you use FindPython, pybind11 will detect this and use the +existing targets instead: .. code-block:: cmake - cmake_minumum_required(VERSION 3.15...3.18) + cmake_minimum_required(VERSION 3.15...3.22) project(example LANGUAGES CXX) - find_package(Python COMPONENTS Interpreter Development REQUIRED) + find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED) find_package(pybind11 CONFIG REQUIRED) # or add_subdirectory(pybind11) @@ -423,9 +433,8 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``. .. warning:: - If you use FindPython2 and FindPython3 to dual-target Python, use the - individual targets listed below, and avoid targets that directly include - Python parts. + If you use FindPython to multi-target Python versions, use the individual + targets listed below, and avoid targets that directly include Python parts. There are `many ways to hint or force a discovery of a specific Python installation `_), @@ -433,6 +442,14 @@ setting ``Python_ROOT_DIR`` may be the most common one (though with virtualenv/venv support, and Conda support, this tends to find the correct Python version more often than the old system did). +.. warning:: + + When the Python libraries (i.e. ``libpythonXX.a`` and ``libpythonXX.so`` + on Unix) are not available, as is the case on a manylinux image, the + ``Development`` component will not be resolved by ``FindPython``. When not + using the embedding functionality, CMake 3.18+ allows you to specify + ``Development.Module`` instead of ``Development`` to resolve this issue. + .. versionadded:: 2.6 Advanced: interface library targets @@ -444,11 +461,8 @@ available in all modes. The targets provided are: ``pybind11::headers`` Just the pybind11 headers and minimum compile requirements - ``pybind11::python2_no_register`` - Quiets the warning/error when mixing C++14 or higher and Python 2 - ``pybind11::pybind11`` - Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only) + Python headers + ``pybind11::headers`` ``pybind11::python_link_helper`` Just the "linking" part of pybind11:module @@ -457,7 +471,7 @@ available in all modes. The targets provided are: Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` ``pybind11::embed`` - Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Embed`` (FindPython) or Python libs + Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs ``pybind11::lto`` / ``pybind11::thin_lto`` An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization. @@ -491,7 +505,10 @@ You can use these targets to build complex applications. For example, the target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras) pybind11_extension(example) - pybind11_strip(example) + if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/macOS + pybind11_strip(example) + endif() set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden" CUDA_VISIBILITY_PRESET "hidden") @@ -504,7 +521,7 @@ Instead of setting properties, you can set ``CMAKE_*`` variables to initialize t compiler flags are provided to ensure high quality code generation. In contrast to the ``pybind11_add_module()`` command, the CMake interface provides a *composable* set of targets to ensure that you retain flexibility. - It can be expecially important to provide or set these properties; the + It can be especially important to provide or set these properties; the :ref:`FAQ ` contains an explanation on why these are needed. .. versionadded:: 2.6 @@ -557,10 +574,7 @@ On Linux, you can compile an example such as the one given in .. code-block:: bash - $ c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` - -The flags given here assume that you're using Python 3. For Python 2, just -change the executable appropriately (to ``python`` or ``python2``). + $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) The ``python3 -m pybind11 --includes`` command fetches the include paths for both pybind11 and Python headers. This assumes that pybind11 has been installed @@ -568,19 +582,13 @@ using ``pip`` or ``conda``. If it hasn't, you can also manually specify ``-I /include`` together with the Python includes path ``python3-config --includes``. -Note that Python 2.7 modules don't use a special suffix, so you should simply -use ``example.so`` instead of ``example`python3-config --extension-suffix```. -Besides, the ``--extension-suffix`` option may or may not be available, depending -on the distribution; in the latter case, the module extension can be manually -set to ``.so``. - On macOS: the build command is almost the same but it also requires passing the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when building the module: .. code-block:: bash - $ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup `python3 -m pybind11 --includes` example.cpp -o example`python3-config --extension-suffix` + $ c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) In general, it is advisable to include several additional build parameters that can considerably reduce the size of the created binary. Refer to section diff --git a/3rdparty/pybind11/docs/conf.py b/3rdparty/pybind11/docs/conf.py index 66db310e46..2da6773f4f 100644 --- a/3rdparty/pybind11/docs/conf.py +++ b/3rdparty/pybind11/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # pybind11 documentation build configuration file, created by # sphinx-quickstart on Sun Oct 11 19:23:48 2015. @@ -13,12 +12,11 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os -import shlex +import re import subprocess +import sys from pathlib import Path -import re DIR = Path(__file__).parent.resolve() @@ -37,6 +35,7 @@ # ones. extensions = [ "breathe", + "sphinx_copybutton", "sphinxcontrib.rsvgconverter", "sphinxcontrib.moderncmakedomain", ] @@ -127,23 +126,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only import and set the theme if we're building docs locally - import sphinx_rtd_theme - - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - - html_context = {"css_files": ["_static/theme_overrides.css"]} -else: - html_context = { - "css_files": [ - "//media.readthedocs.org/css/sphinx_rtd_theme.css", - "//media.readthedocs.org/css/readthedocs-doc-embed.css", - "_static/theme_overrides.css", - ] - } +html_theme = "furo" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -174,6 +157,10 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] +html_css_files = [ + "css/custom.css", +] + # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. @@ -239,6 +226,8 @@ # -- Options for LaTeX output --------------------------------------------- +latex_engine = "pdflatex" + latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', @@ -344,17 +333,20 @@ def generate_doxygen_xml(app): subprocess.call(["doxygen", "--version"]) retcode = subprocess.call(["doxygen"], cwd=app.confdir) if retcode < 0: - sys.stderr.write("doxygen error code: {}\n".format(-retcode)) + sys.stderr.write(f"doxygen error code: {-retcode}\n") except OSError as e: - sys.stderr.write("doxygen execution failed: {}\n".format(e)) + sys.stderr.write(f"doxygen execution failed: {e}\n") def prepare(app): with open(DIR.parent / "README.rst") as f: contents = f.read() - # Filter out section titles for index.rst for LaTeX if app.builder.name == "latex": + # Remove badges and stuff from start + contents = contents[contents.find(r".. start") :] + + # Filter out section titles for index.rst for LaTeX contents = re.sub(r"^(.*)\n[-~]{3,}$", r"**\1**", contents, flags=re.MULTILINE) with open(DIR / "readme.rst", "w") as f: diff --git a/3rdparty/pybind11/docs/faq.rst b/3rdparty/pybind11/docs/faq.rst index 8bf05a40a4..28498e7dfc 100644 --- a/3rdparty/pybind11/docs/faq.rst +++ b/3rdparty/pybind11/docs/faq.rst @@ -5,12 +5,10 @@ Frequently asked questions =========================================================== 1. Make sure that the name specified in PYBIND11_MODULE is identical to the -filename of the extension library (without suffixes such as .so) +filename of the extension library (without suffixes such as ``.so``). 2. If the above did not fix the issue, you are likely using an incompatible -version of Python (for instance, the extension library was compiled against -Python 2, while the interpreter is running on top of some version of Python -3, or vice versa). +version of Python that does not match what you compiled with. "Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" ======================================================================== @@ -54,7 +52,7 @@ provided by the caller -- in fact, it does nothing at all. .. code-block:: python def increment(i): - i += 1 # nope.. + i += 1 # nope.. pybind11 is also affected by such language-level conventions, which means that binding ``increment`` or ``increment_ptr`` will also create Python functions @@ -147,7 +145,7 @@ using C++14 template metaprogramming. .. _`faq:hidden_visibility`: -"‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]" +"'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]" ============================================================================================================ This error typically indicates that you are compiling without the required @@ -169,8 +167,8 @@ can be changed, but even if it isn't it is not always enough to guarantee complete independence of the symbols involved when not using ``-fvisibility=hidden``. -Additionally, ``-fvisiblity=hidden`` can deliver considerably binary size -savings. (See the following section for more details). +Additionally, ``-fvisibility=hidden`` can deliver considerably binary size +savings. (See the following section for more details.) .. _`faq:symhidden`: @@ -180,7 +178,7 @@ How can I create smaller binaries? To do its job, pybind11 extensively relies on a programming technique known as *template metaprogramming*, which is a way of performing computation at compile -time using type information. Template metaprogamming usually instantiates code +time using type information. Template metaprogramming usually instantiates code involving significant numbers of deeply nested types that are either completely removed or reduced to just a few instructions during the compiler's optimization phase. However, due to the nested nature of these types, the resulting symbol @@ -222,20 +220,6 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids potential serious issues when loading multiple modules and is required for proper pybind operation. See the previous FAQ entry for more details. -Working with ancient Visual Studio 2008 builds on Windows -========================================================= - -The official Windows distributions of Python are compiled using truly -ancient versions of Visual Studio that lack good C++11 support. Some users -implicitly assume that it would be impossible to load a plugin built with -Visual Studio 2015 into a Python distribution that was compiled using Visual -Studio 2008. However, no such issue exists: it's perfectly legitimate to -interface DLLs that are built with different compilers and/or C libraries. -Common gotchas to watch out for involve not ``free()``-ing memory region -that that were ``malloc()``-ed in another shared library, using data -structures with incompatible ABIs, and so on. pybind11 is very careful not -to make these types of mistakes. - How can I properly handle Ctrl-C in long-running functions? =========================================================== @@ -289,27 +273,7 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses the CMake Python detection in a system with several Python versions installed. This difference may cause inconsistencies and errors if *both* mechanisms are -used in the same project. Consider the following CMake code executed in a -system with Python 2.7 and 3.x installed: - -.. code-block:: cmake - - find_package(PythonInterp) - find_package(PythonLibs) - find_package(pybind11) - -It will detect Python 2.7 and pybind11 will pick it as well. - -In contrast this code: - -.. code-block:: cmake - - find_package(pybind11) - find_package(PythonInterp) - find_package(PythonLibs) - -will detect Python 3.x for pybind11 and may crash on -``find_package(PythonLibs)`` afterwards. +used in the same project. There are three possible solutions: diff --git a/3rdparty/pybind11/docs/installing.rst b/3rdparty/pybind11/docs/installing.rst index 2597285981..30b9f1853d 100644 --- a/3rdparty/pybind11/docs/installing.rst +++ b/3rdparty/pybind11/docs/installing.rst @@ -8,6 +8,8 @@ There are several ways to get the pybind11 source, which lives at developers recommend one of the first three ways listed here, submodule, PyPI, or conda-forge, for obtaining pybind11. +.. _include_as_a_submodule: + Include as a submodule ====================== @@ -16,7 +18,7 @@ as a submodule. From your git repository, use: .. code-block:: bash - git submodule add ../../pybind/pybind11 extern/pybind11 -b stable + git submodule add -b stable ../../pybind/pybind11 extern/pybind11 git submodule update --init This assumes you are placing your dependencies in ``extern/``, and that you are diff --git a/3rdparty/pybind11/docs/limitations.rst b/3rdparty/pybind11/docs/limitations.rst index be7300cdb6..def5ad659c 100644 --- a/3rdparty/pybind11/docs/limitations.rst +++ b/3rdparty/pybind11/docs/limitations.rst @@ -57,16 +57,16 @@ clean, well written patch would likely be accepted to solve them. Python 3.9.0 warning ^^^^^^^^^^^^^^^^^^^^ -Combining older versions of pybind11 (< 2.6.0) with Python on 3.9.0 will -trigger undefined behavior that typically manifests as crashes during +Combining older versions of pybind11 (< 2.6.0) with Python on exactly 3.9.0 +will trigger undefined behavior that typically manifests as crashes during interpreter shutdown (but could also destroy your data. **You have been warned**). -This issue has been -`fixed in Python `_. As a -mitigation until 3.9.1 is released and commonly used, pybind11 (2.6.0 or newer) -includes a temporary workaround specifically when Python 3.9.0 is detected at -runtime, leaking about 50 bytes of memory when a callback function is garbage -collected. For reference; the pybind11 test suite has about 2,000 such -callbacks, but only 49 are garbage collected before the end-of-process. Wheels -built with Python 3.9.0 will correctly avoid the leak when run in Python 3.9.1. +This issue was `fixed in Python `_. +As a mitigation for this bug, pybind11 2.6.0 or newer includes a workaround +specifically when Python 3.9.0 is detected at runtime, leaking about 50 bytes +of memory when a callback function is garbage collected. For reference, the +pybind11 test suite has about 2,000 such callbacks, but only 49 are garbage +collected before the end-of-process. Wheels (even if built with Python 3.9.0) +will correctly avoid the leak when run in Python 3.9.1, and this does not +affect other 3.X versions. diff --git a/3rdparty/pybind11/docs/pybind11-logo.png b/3rdparty/pybind11/docs/pybind11-logo.png index 4cbad54f797d3ced04d4048f282df5e4336d4af4..2d633a4d0c129d6bdd94b4134c9516fdc92bb192 100644 GIT binary patch literal 61034 zcmeGE<9BA=6D@$ow$(w$9XlPnW81cE8=Z7)vt!#G+jcs(-FN5xopbJga6jEKo+lsn zSkFS$teRDG?U4w1*&m2-cyJ&fAczv;B8ng&5LO@{pz<(Kz!R8?TV)Us%rBnG>duM= zZp8MEcBU5ACdAGj_9nz8?iQvXAnvP`>1K6g-yMVh9a6hPl=^eZ3Zz{_@$m6a{l+3q zZZ{hAoqJbSp+MQ$0sB7nob#`1%I~@)nlRh2a!J8zTMpNR>@bRu&u{S64&m?nc-@D+x(SMJUpJ-*v0amFD(c*X~896E5 z9)J(_&w2?wCToJTvjXFFER7z8M#%nWeFxTGvl8`GDrA zBv-bro>tf3cIi4HwluuNhS~$LpTl4{1URtj5K$ zTR9hUC-e%pevi-AE%ocVK2s*=vpEZ+X(eHfR*}fw8gdg6dK7MU4ou8$IjlC#O?b=K zi|6$B{bn0GbtLSYL+Kv(il(E7%jjwoC5s&l5O~Oqwd#d?&i!sbUDg`8oYLZUXkE_a zk$APsvN;dwqVU+bt!)lr)9S8T)oMEo5rbwkVhNy9qUxRfji}{}Gen#89$)UCpM4Fu zHd1 z2I4go-M=U(U=NuuM9+Txqki2K_GuA+ark+EJcPE)Kon8`sK6lg0C<1m?{O+j$?Fr%jrH3FyZ6ngjOZ11Qz2-kWv8U(;YiW&TI#ZSy;4tg?pq&2D2R6D>^o z_b;>pqfSJiw8*sBc$41yJQsT;_~Hw%@c{u@n$+!-3I0JZsPNGkX}b+#!w6sIbS!mO zXxy|R>A_)NSMaj`qz<5~Q!d*+(f z_-sjRy}Njg)8vyR7SO@KP{L@NYKK$N&XiJ7?sa%t%PmH2>klOrLnkolPJfWB3mUDQ zMg=w1!<8ElPSbJNWb{|l3#PVr=y%f27-5$t%Lb3i^-_a;C!&)J%C_X;CHfvF`y` z4EtCNho&#e1r$`nhF9#uTdYbO$4hY2n>cF0Slme2FZfM`mGIRK+3DLdTKSZN>It@X?Y z0x--GX>r`$kIb07SBf9gdxXn=&F{YRxiH}TJF6Pe!}Jcf=3#esRCS!(-i4)st0XpW zfMLw+g$c>>u8pv$`Qt5csNDZ5ZX-2I3g(ojoB51ZAo@)jtKD8m{K!(riX;N0dZTbr z?`Z!C+)98;1Hs0G)~JT^i}CCb$i160)tfZ6bQ`A5M8;NCF@upWCM=lLGj$x68;t2R zYyqb_ecR7MZT4wLog*yyQP@-Ynf+{vrAum0pS*m>*R{MC6HC zzC|kT)=Y`~L+~PgDS2#TX*2Pkm@i{vJ2z$5EI8e1-amGmNZo3H3T(O;20L zumOQ(I6P7Z9c%DaeYCZe<)gHqdy`mYS!#>$a7;8?DhjePdOhg#a3EPYcC`^B#jbs$ zJsNB|LYKMP$vzteqodjf_>jr0ur&gf|AmntHO&nsd$3rf1#Ei?)vZY^)OVsyP1Z}C z2cePEaI$+Zc}8pMGxS%!+JO6Ze0J#;q;$wLi6LtG4)p$(Oi+kj>q>saqupxypQ&Y` z@7Ez2jNmnFly~(8?3RxeS8xyCwjMVQ{A2J+FYfMTjTRYMN+~cFrwN8Tv1Js8)|#c- zORUIrBl_g*4z6kn(LhtjcsLLYF67UN1DZm7+JB{ zZ?p!7D*X$K>nhlC3PZ=dYxTBqh;;_FSN%2n`U>q_&++*~y*PTX0x}_#p(vR)Q#)E# zl^pHjjosC-4eiKt?~m`RO2Oe^D<3#An|laGs`fh-zGx2wA4h+u-}@ynp-~Gp43-h@ zSORmYxmQ7cPtw=GjV-!lk)nwdrj`zZtD1qnNyWm)DO(eUWth7x zVnfymGLxKT3wv?iU>%82Uf($#l1Ga9mpym~IH*b7pF9XwH2C=uMR-S0pV{8ZkzVXb zVI*8w6Z~<4Ko|UPH@4wcIm2J5>~59Q!*~xY)!CA8x!j6nf|g`P;ymnk*`UbAOc<+U z>4p8jQtrKmTR&;hv*E5+RPK;t59Vyw;?0YLUJqh?9eM0S_5Jzcd|7al9ZM>bi2B%p zebs5Ganh;}K~RiK@pgJ0VR-fRUnt45AO=3mo&2}K-QE259o~sKXOypoI7F4nPgG-H$mP=&bQ}`XBUTbN?|h{(#X8 z5K{*o+kky%35|x4NUFgdpYL5vp(3Qn*)4EQnj*&TUUjm6$Cgy{SYn~#LIQm$k}#;t z>A?MAN%h@8WSSaJZhY=Es*l0g8OvW#Gj&@&qACPpWMYbVQ0ETbC|tr4$tn}p&K@!v zgVAugEp5T}3lX%T z=x}fLO{=|W;8ge26 z5XWCesno86h=cmXI1#wLp}EfruYUykLEKDRP0jPD zNe-HWRtFf_g1;Y`Q`|-FaGkiGi@R+x(v!xy(S*}IaJr@lD86&pnC1_Qdi0QSWJKo9 z66!jkN12{Ez|5!1lg4JOfETsX@k+uXruV9o_&M^Cw(SGczJ?*Gord-K~TZnj6#Q*IUm`sBZNY*jmG{H&GDm7!Cv$#ije3<=-am|s*9h) z7xeYJT)UKHmdCkSeop;Ci4&b={sErW(mj{Nl-ilwW~qY8zu?dV4B(+0#$M@;N%4hw ze?@~IBU4eF+UtK!7cNms6@}AQetX;A=+}a^fDpF13!SCmrZ(9VCA87tc!hD<1L73y!)4c&NY7iS&C^NRAK$t8jS6~E!3`OlOA@6k?JFd$YKF+T}LcOt2BflWz z=%={(b|Ly;Dp($BYF?97n>6jejo&Ck%(dcSI*)|H?>nOAJLDy^BQJoky&s$XOR4Kny=Htq;+6 zQWMops<}DPwf&pISm_E@+PM%E4EM_{0iCT~MZe7=q`u)PSC5fDO!gHH2#L{k=L7MQ z?p%Fd8i`))d_FORnn>0LdOb)6q_$l_zI7o6@3{ikmwO0lG!CI}@byJhcxeVb#dE?l zYG#a%8X8XcOmUROH7cQK5gKU#=xK z#o!eB8>4a6<$y)9kX_S3MNfl>l4viK9O6i78lq;gK==wKXPkwPB|JOJ3(lKdCQdqc z2~B^CCizYvB8!91Cp6@Yo%EyQ$z#ns8v2aOekg zCUhS0R+#j`dfIcxM(=N3q>M!JdRP0kgln~rXz8y(ow}P4l+&aR#ST83o)1CXAfj{k z#c_HKU0JaE(-F zNPFT;lAGwr0pKi@sY+-}!hK$k)l98l4&eB)e%yRJf7edo-WsiIWqNwJYk1-a`8R2y0X5Ibl+YCS3E zm4}N6Rv~HG`9qrNdp$jBa>FfJmM{?N!La3{)Qo|J!pm+!jm%zUTo(S>BrXG0VTASG zF>LT(Y1Jw2#|uO2lXCD~2?OE(M6-uzavl%P-N>>EB&P%?0R zOFE?m38v^*3!R-MpeR9^V8VdNr-{V2fs!bD=WH9kP>K(|K&2_}&YuUiu1{NF~J7O1pObdAwiM(h}mD*Q_S>EP~VL(xW4Pmjo^khpPa=UxzVfo6Z9gEj9$@;rH|&+_h0LC5}wApT*H4fDJB(>lzu~LrGRv zzH6b%kf7eSE~W0ALaHJ>#^hxYkU``ck`-_Fnw?|Fb-gA)fT90^91?Xy(Ad@pMou$D z!^8ZdAxK>MCzzutyhK$z&o?J$rl%+H4tiqx4c#^0|8AaZ+vpTm4Xur@rlda%;E5 zG>LVYuxsxPXTrLJDE<>x*ql@t4@_adgSxV4h{1Q>oBm5>buz~5Kf=U)=pd_z)PXEkj^Q$4KF-Vc;{LT_eUCy?CZx=?uY`Q4 zOA&ap_-sG9X?e?tAw_t}(?vc&Z3t<&vPtGs9yy^lBhIu#g@&{jPX9!IZ5w6Epx!o$ zEWRcD%i9o3&sD;o7~=4QbL(FKScTEL>|D^#3>O3In9gjP=86RAc-q~gI#o4wXq5R$ zH`A~U)74YsFm{5!NN?RXg*99qEzyG_Qj3F1jqa zv9iZ8(thgOFD^&WZF!=pcB4BlH)dy3C*&ElY$3`_JVF$TQu8w)27M`cLFk*>jMEtC zp@lXR*ft#7PNA+BMKYex_?cH$J{ed{h!B2k0Lfai12X(RLm)t3s?WAqK%(Raz?5`WZUj2ko2N zBbylo_n`}A4gCp%^aa$gA*jnI`Im)bl=MmbL^fyRzI|g+-Q>>VY(WERyT$8{w82M7 ztwwdfXn_cHQ*tAtKKK>LC(JL;Go^yXc^MVeK71#`)`ua9$T?6B?3OjwIuchzi3pD64&EyR){!tsCGpt{c375Swy z_LTKJTv(&0E~Q+nFkm`c8~h^}TwyHvrE37~2c{i$(=5Jes~nqw)wph|Qrj6}ahgFg zBOVlfp?HFcfUFiqO87do)l+-aM@qIfETltI+~Aro@k%Q>~iw}QuD3QsMAQ(v);c*&b%hhKNuwM>wK;` zJ90)i=mdx88Y&VUX||kRKyKqzj-Zf`@)$@O)vU%hw6oT_?M9#9LFiB&qf$HPrvdRA z%24Wu2nZ4gA_$5SK`1rw1lC?$!wCcg9{uwR3X-0Q13ZLwmXHyJ-iN~i1E)i8iKVa=6WW=TB6CNVI$|1dHAQb#9_1Vzdwo)3`y|+|IhzVZD5N3g#q+)Mb=zY z0Xh(55DtI5fo>O27_g(bt~U^&4&(o?N9v=X{gEAZ$dmu35c#L1#Qi^486V7p!4P`| zP%NmSTvrf$x-qUUz(oG@r)e^pAl6F%YJUHQBaDRxJ_`#ASMEGF-H>3Q!9pYuV4%Rq z+10i9r5ze|ssZ)}4gLSYpl~RrWlLJoR9Z(z=WN9+C&onB%gd{xtc+SESqA2M_S^p( ziNXxI1jDAHqEZVEjr7N~uS}`L5D^ivOaz~S;rr+DByp1kQH8{xD#Hf8OlK6V4w(&)7~A~vvTvN+(Z3bE0G6u z{O8W8bC;(3vs7|nbb1!I+n}Q$u&MFCj%BYZbPW6?WMM70Ew+O_b#ni+191+%Z1#at zdc(IhkH?FRTJ3nD8dws)6~o}T*`b8P#sn0_!SD`S0uRawC(?Kr_Z-pIo^u5-)EBog?Fii?UwpKZRFvrEII>8ykN zM%%g)gZ>ZVBan`k_kg1uvqyE*)P6Ya%ob^$ek2<3TbazE2$`ZHv(nz5ou3!AGLn$N znl7RUi618z(`Q-*iTEG%k^iLa0#0l(n?oUNVlJ%^hr*oUzAvi$8m*iTk1j;@IUF}` z3aymUpEx78Fi!N>;NaT{>9^WqINFe}!5GJ@xG)g$$%?wZLNEh(LNL??h?G_rBee1* zCVrByrtvYM)W8&`KIes>%M;0009&@@bZXr}xVlNfxG4OolHq3~LXM+S9|1#B&$=|D zor)5#_TRF{N+l-t6NqLKq;AA2V(S;X{OuPEHg*sCp=fgFmN?V_vbh zG@-9RvkJ5Zi=RWvrWNpX7X>EtH;ex+7Uhy~&=-sX@@d+_eIpGzj?7+tlpT9c3*=D> z;#M*YIC7sc8es2Djh_SKl>hIP2YCk{#0>l+Y`yw%f=DD}W>%DAVX!36frf}A!S?+l zh?FCV+hjIb9Li~I!6?j1=Je7q)G+^T-lCwal~JixDqDY7kdr{wtZ^Um;k(cSF#pw@ z7?Xoh=o|k;gA6MNwrUmvhB0~~tFQP9+m1cX#Kc5;m%Efy6jyn1hzyX}AbD=SN`9Ip zJ5OC0hNLK>m|UL6v*8Q|G!8QzIEkey(%;+Xt9d0kYQKgr7|^gJ^&PI34hcd3XQzLE z&=fYD*sL}E>5uwYB9OgYM4XFlu+ZoV4J#va^eA)uw-76B4h2p*TnZQro`w zxwW~u{G{1L$J>9$Es`Uy1@(SdF>~tLS#Pqze-|D_Z)R>0M1ssP%kfdAn-9)yftLjk z#h5eVByDGBH@PI6^q3qbBk;+!iZAAt^yBS8CNgxpeG%)>rra4)L+>NQB0+$e*-~A4p;*=kNC0Jpt-KIJD^vpfI%N%L~JdK=PR%-4EUX zrsNXQ1p~}Kl8Xzn4J4osLmtmWvNo_?4HY_&CNkgU#l_I3CU#{NmAu+o)W*if(yA)i zlZ*fX5FjwVeE9;v>?C0TjfedG9EE|6r0~z8>}TQtHk1eoI<1|$j~!b_GlDvp2 zErcL@U)T*y3baOR#O#EUnBb+@A(|9>1~K~l~Fr18&u>?at>W()^5o1z65 z%A-i+0$!@XQ;mLoQ;allS+gF2lK;G#7M4LkOGF>o!-W+!;LMfvlTJC)M!n+v$7lSs z>|~<;ZV$q+68|Oy@-~YNoa6p8akbcDNyN!5`#uMMMs(s)s1kgT$UbEa0U)}09bM1nj- zZeHsZKnzJx5##B=EQ*P>6z24q&BT7+R@c^y?(gsG4ODX$)EIFJXRd&07;(m>UeC9> zbIyMNhO_6$1cGk#IkX-~`>mW!S2qBSQ#12KxKse^9-f-o*;RAZ80Y2$?(FO=XY+co zBHy6v_4oH9JeX0%Il~PbiDQhFs&wXr%Dn+#i@k|5)r-YU7Z)EWkE;uf>lMi|HWcFHQ}^2=b@l)Ui!4` z114O^ky?Vm>)+D`dH~?S%&3AuP6F~E_l*TT0qvmxKL*@DlkJA4y^xHL5AQ?>i+T5_ z3#xS5;i}%{4Jkcp5h8(1{i7m+|9=Z8+FO~mrov+f!JZp;{CmGVEERaEE1}GG2b1G} zckb=HRAk16`j0F|Y&dWA<8YuRLSO-2IUhGYrvMug@RuJ1jeI2vtlD9D*cCAAS+@v$b%;~ zx7t&bxM0@Fga7Yv0adlr)7tK1$#ksP6aYrY-Q)C}4BV(8pOqYrrt<<>@*sf|+IH(R zsow(35O1G6+qO(p8IQwFwERYmXpJf=68(sfsq|IG8(l%7ZBHKgBw=i_P5 z+?qD~)uG6g9${J^@Pwr0Cr|DWXM8p`OOqq8PzlMS`B+ww8^Ny3qXPi?UR|7>w@>x{ zc?xDPvSEJykg@)gH7)xk{^vtdb9$b!0N>iwX<4!FsqiIM-9-PMhSZyFb!K4MK>jm3 z#?dPLXT<|UlC~QVdeEonuRRZ-l5gvJZzyUo7|X=+bB8W&ZbP3Sm_JvGJm{kh`WhU! zE7tYp$gx#kM7r)2m6iJ~*Gd z{V!hfj`$DzLg1yU&ecDixNdI>*!_PX$TaL4VN&_@qLMR3LxRF@U^k=LU5}lYNx||; zdU|?}2YnzcpCyekd!`2?Hq>YJK@x0b#%^V%5YAR)Da4Hnm?E?49pLY)Wuw?6>DQ#+ z%VM>m;}3l$6}?&3_wc1)yii`j<9+`H)bBZ3q?`ZJjSyh22?2hzi0w49?B0S{K260h z`HABpQuhs2WmG8%9WWyh$o{4vd=|a11_#CRg2#4^U{~M3<%Ndlt(aL^u{WD+G91^N z{x@5n5A8%q3CvvybKXXWhm%6abp9XUfn~Qm6~ugBPZ+h{k{h-pLO^c7v(y=uB~no zUn=^o8GmHg=9~O|?4gmgvL&$u*itt}gyvw}9QiSsDhuKQLnNp@tUTF;rk z)L$5cq*@P?p%JWG9Yl3Rd~}Z!8Ev5Nj=%*{U^J6=u}Lz9;WrDJEBLY+<319CJV3o3 ze6tMA`Qcv-=1#O0<e5KbWAf$A}B8ABvr{u zU&2DDG*gBX20ERY>9f1)q51qz+Yf7EankUrTte+(eswi+h{&(QnA@%PHLK97TkmZb zo7@157q`ee9(w@q9IM3}b@=94_YaEzg^s_DEBYB0Do14}=Hp^Fa#V_T7`JVCFAM)G z!^WkbTMsLNFFuq4gCDLaF9Ou@zkI^{+%VvHy3kVjiC4~U{4Y>qC>KP%#}}ZP$H#K_ zvo5o0G`smSR;ZA=&%QafiI-Uu$zblt2K&Xv;z~h(^clnLIeD})WSbjywqJN4GbI^-~cvZ34MkTT7{J1&=CVxSl_s5P5P zKG|er`f>PMPMS>)8snWbF=(oIWmRL?P^L8vh-D_=+}xF?+*{8`*#LRO+jc9m{m*yX zpdhxossUQbDirT~5mhM3^fmp)D7j1ETu1&EvdF)FIqg^r2HIdjbh)l6)MN~f=ipqkBy0G3**Vl_dfi?;nk*jH^+@XW;jWS5f1`KlvhLn zSz)@DID$1$7dlH8`Y(q2J6QW0=P!Mawss=QsZfU(A%6PM&?LJNNd2h01&Yo~p{;## zw$Y%=%Fq!i?ccJ-om%(;ju!$V0tzg%78y7jwQ# zB-thHWg43geCD55mX_PDLo8<-wvTK%Y&Zy`gF|`pFj-ky$6!H!-cXalK=smo##Ri) z4Pr=Lb&<E%~jrqOvk+sN`+1ur8K$Pa%u99UFb6p3HA=BZ?N% z*(C=kAiIibIe=EGk^~x=P?|%ILqf0h2%i;O%md(8F{X6*lr;+-C56$0J`5{XEGygA zysr|e;xL$gPkW*jx|RV8*5cHr#-k%F5^kN`EX!x?q`qbs=`pLL9HG*lLsmq^#T~kgz1Lqc#I7iI z*GO9->oG?mEp#gw#zpu5=3zUI4?7oND@5{7LHzBvmP=7`NZKHO0kH|C4V@D=TGi zAxsJWN(BVm(1=bSDMCQ#DEeIo6l!j0~k^-FPS< z76gd>fBr-Q6d#vlLX1Adq5LLgqpv|#lI46PZo8jVynJ>EC8|TB59u!*ULB^T0*o}% zmk*ZJH}Yy;NTzBT%%iY=1Ml06E!t;m1oD9cxA>|v3qVzQnoa3aWe)%?FNmU$7+TDf@suZNjqq-b@F) z!K)Rmw$&CW@VbUWK&S+z&5fB6Beby%jqa0Yr%zVUvkg%aK_zY-+Fce@Ar zyVvffI&U%%kE7SiGc%w$Q_$U8)z(fwMQudWB@WF%%+1Y*HFDyxPcTfxM?JVix0WL! z@SW*caO?k?(`#Q)G?Y=XcPBWT$jR|sLuC8D9M`3fOD<@q8pgPSl7jSDu=H7{cijEW z;+PN~8j)(L{SC&Ig%rpaYJHj~>xN8LiCSieW3iW@9H`F>)AByp3Vc1N$DF>B?zuNw zp=2ZT_&>vA_1HG|8P@g~vkGBA9NoQ;?dZQ}HShenM(6Z91MyclR+5%0Rj7B>ARiY) zL>DIWX4m0!UUBjpwTsVCadyl@iUeL-Bwp)2n`$BX}|?mW5nNEVTE!KDwSB` z7>5$vxcD2|5uwGk#b-h>m(&Hbd4^=*&<$>7TPpq<)(=kV4tO?{)Tl-Sff4Vn21hhC z2F&+<_WQF37Pl_`$&`j3t+CUNL=Z^I7-aPleDzZK%Bw0IXG}~?9pgUWq1)#gpuAEJ zFwAFV^}i9-?f(7yH|w>lJfx{;l12gwAw2ETt)x-yw*__S=rC^_IY98pZ*0V@tgM8A z0K0njHn+0cQ=b3r8&`-GPnZ?T z*h{EmG_{#@V6a`dQ^2mp1hi|}9i4r>jj>4H2_OnP9jvQ>!7wJj4ycDrtXppk0odd5kZ z{p$WYoBQS#_HtdKIZg*@O27W~T`OK5&@+NI+TBP2C9APf#VL;h6L1uuCUCl(U^H2; z*!@yxMWQE>x{68&utY&~bF=EZcVOOii2FkQLVQpT7{&UCvV}FCkj{P@GLsqN$Yxqa6LiEp=e&gwWQ>Gqk z$%bDrGNC=9QJf z0-{CW)+NI-srH5~ho+_`3If#3{CuJ%E!3zp?PQ{<1#-Nz0{X2JBTn$r>B66G6ZV(E zCY!bSf9r7rJUXrieM*ooVaP;-pUsBq2dgFA#-;J^htrdjzW2v1z1gAPB?XBSA=HcJ z9V-)mo%a(j$FUN%XWT-g6xB-Wqr>KqeSqo?B*_$NV zhfO zLQ4Uvs-z9Vh(7?EX9}l4iwPe;(A&n0=R|1yOXYU4m9OQ?V2eh5K0`({n5`Bs@@1Qa zuJ-uM=`Oj2;@a{!)ba$B`ovdYmP2}^?e&3(=(1t`$yn~id2P-ZJsB2%*S{2;4khd; zFy@Yrj!l69i;Joa&CLZ>^cKU)5>64=e<5R=C$3)1nUGjo_~>RirB6Dvnydu@vriq) z{aLeY8q5bSXS}?)EiEnQ16;rV95_~_)2Ysq4?kjKW8<{n7EqX&vYa`XFHDNQ4D&rSpa z%b|<`z^S_G3L9^y#E=%HNF)fxan)Fg%Fd9@KmUL0uLxbw^ZYv(gF}pNf4v;uEAQ1^ z*PtE7+VK_Iclg%3HiLejnAo>xr7CFzcsL#E%bGy0+fov^a!Z?%B0w?e#J4>*k;T@oS@JXs)gNS!S)p|rq>cppC!r>UnBafG!*EROBe4oH`W{NPzLkFL4$iP>ynXG>!vz&6XREXPLVlC)Z*4)5O%_iR{j!+z&7QI^V%9Lsmcil}Bt z3k!qGW^sLr@`017;v87qTwL>M7!kG0<8%j!tdj5fvaPyjo1ORoF^N*n0?J)g@}Y%_ z#k{SwFBg>*H!6eI%S}qRc3cUhh0dr_v@hi^afn#{4|hRRaof!fjoT$!6DNa6w$>lI zuvzRzs=f78M~s9?e_{~ot$-^G5-M3DFpw#)%RYYZ!|K|D;-u+gXM3)!mVd(d*C|^6 zFp`pNqeaB@8#T9`R~2?#N4`#Uv?isp48_1Tqa(E=n%2XL9Yd5p&CDpl!@y(F4LrFc z)S7F%rbpsVn6R4*Cu;AK&j zG08p6;{->{QoghPbW%$S^%ylCH%U}M2P*UoMb#L=>9HXf%9n)ng{5|Vrl{~}F$3m7 znVbnRP^ch6O4+a(4o0r)<}gQBFlAl#%!F_@SSEGZa-fUp3jl|XDP;NGU2K zMzdRFe*{_nd0@HY*_-CMDC zKBrpgU8YPgSpZoD(rT?WT48s6+*ez@q!-!JOV^6^$7mO8u)#w3XjeoGGLMxcpxz>9lD&Y=iS~@53fs$`!Vykb^7rvt%&DaG$9#CZF%KtY|ZyBgLL8rO?1u;bEM9H%XH3%OM zPjc7xoDI9J2@B{@bRuVzp#;BP3Sd{M&*i}kyU{GYh^T6n#q|HtUYeOv_-Vw1kM_0A zy6dlF>(yEPRNv)eN^m&e3Qv~77FGM~q_QvNkDmn8r4QxR)kA=SG&4P&yJ@wbX?Vx| z-RVHOx3fez3<-Fd`|T^eURTBOX>!|Vl0xKgOv{8Q`eTj3NS}gKgw3g}!8U?ELpN5l z@=Bd5Ly;N{1(Oex#h1;Sn;C1XTaTBs^+pZRKG}2b+IQ~S`!~{TPMr=57!{tMnj}xk zfaOV=KjW@C;arps@O_PjNO&M26t%o8nP8zOjm%?ZpLU7{o&MjoRC%P`updJSBK{XN zK!@*);v5;DiA(5qQJ11(8Uu}My1?_#k^`VMF$LrVLF6Dow86V$_z7DL-H7H%9T_wq zG@&x$uV}b^hmv%0vJPtexd}V#Fxom@w|ti;1%GO2&p0Y@i<0CXF&; z`FG?4>c$fD!Td2DNWd)sK=1|nh(&DaB}qO(MOh%ae((2z7R_I{UG=xReAGKV*+90R zuJ_Wre1U!)JAS#zmjPb$u3LA|y$~NPt#2K-dK>aXkI+ z?bR|iVbFqQz<^% zDTt*!p>jU+;$mo_7gfCog!^JAKOwd@JNn0k259?0?wdlFTzEY8X*P`np>U~&LIlTT zXi+&v5K@nH34Y3iyzJft66XS+Gh_T<8W(gl)p|(@?o+K?`)KW574Q; zyD`F#pL+3r^x@_q?6{zSOzuh@lKxps78Mm~g9mJczX~PZy#NBW1h|n}_MddR6Mr5Y zgk)oO87w0Q%F#7v&K)f>y~N^T3TJ2ML}}`EgjX?A^;KBn3pYh7a{1TC%b~?eE#QRv zITk*@6@XyJh+J+mn@rP(f6Iq- ze>mq-UUag=M7i%e<6zSx_t`T2@zF*=HnK0M61DyQ35s}DopuJ8cCj0++0mVxrYe7s4Y zfePMl#Ob(;_Ej-7k=W4K7>Bz7As=+VUmHgqmSJTj>`MIl0(_%L1H$ zNV&1?BEJ?$Q~7M${!h2{6${e(;Auq0w160v-7`F&5D+{(bzIY=0qwz$(^cU^mo;Cs zU?R0xaMw7%uoaeVRG{bbk%q&a9%C}XslLm1Mw|1UbKGSc8vzOxDXjYWIhjstl$}@& z>hJH`Bbw_*`bznFuSZ)}jubZ)_1z;km96FcB6X$Gl|V|l?D5mn%hg8s*N1Z@d?9d< zP4_)SnJp;*d;40}Yi%wx4`(Y0jtCa#9Lh-lNpGveGL+>9C)?(OQz~ZW746SZ)~jzC zwCXh|NMT- z$`)-SF{-!)y>^q3OyJ3h<-hd_@%1PrT+_#3w{5JFTU8|K;o2?u^_;X`0qia z_A4>hJ*yb}qpQ{!#@%=%QSnI-9J)Eq^_57^EbE9Agr%}qU?8-aM!iMTR}};VH<}2D=3%TstKQW5fg^tkJJSq1qLrZ)IggEsA*sQ`0+-b z6*fHJ3l{>u1_eY+d;q{okGeNwzJPRby zw4Zoz>D_7)6XozHeLs(4=qi1FmCu-#g?g8rO+c=d@z7i{7Qz>(rBe&f9ChmcHMXeD z$;|wdt+@j|$NkB`*|}kFSjw+wK?*xJN{o?@9jSc+_GoviepQFl=Q|uoi^s$_?~nS} zKGXWXotpl?&OOHs{icn5=Bz>}!4>bO(iGrdNl6`y3_uD5TCW4c}?JJ-u{u5-@NyzT>_JckxX6A}DRTGCgn+q1K=G2zHh&(4AY!rwU0O_F$=IB@k@77AQ0j*b`5X*Jm+$TDGJ{&g<8 z(Ax{^Q;QUmIkwn6cdN5nqPJdcuoxxg2Yr`~r-rDtIbE!D-;JPT{>G6HT;dfk zr#7`-rAhU>Kh+`&U_h=rO^NX=KY<`Xfxo=IUXR!Ri;+M2CQSjfOQ>Q6sQmxVt<*c* zNxJTqOPJg)2$s)Pmu(g!ZYZ7JTIarx9B-Un_FN)$hP&CdfGYriCd%Ppe#Z|80{$}s zq6&&AXduetXlwIk(CbAfzS&e~4AEN+qM4klzPB|_pe`l!ER+Irfgo}L&XhkRm7M>q z%w=NXGbR?T-kD+~n~Mh;0|rHZKXr~i`%!`{SVomHARq?=eckpOnKs;Y{2 zCdFS@qAjl%?MY6N&;tf>88e^sT{e`Le9#Aod9*(1U^!D^E~kssuQk}y%Hm6rs#GFg zaBRni$3W-I^Ryk5K4B4}xms3KA`aZn0>Z_t>D>NaC;6rxkU`CRh@PcRwokIn-QOSxL-M}j>z61wcSHQb>Fk$_G zdY!ADUZr4Z1*kmAl15CJG4eQnnb(;5qZXH`rsn=?ua+g1i1!7vE9HreJtpVkyvsJA zH6)tIg*MMilY=UOd z;Bau)5Zv7@NN@-q+}+)SySrPE;1Vpjy9Jlv8sy+E_u>7%dv6s#Ql#qS?9T2?Pj}BI zBw$qd;T4-cRYUPX3u1FN&Ib#P(--pWSiRdO(tA!b`Xb3I`U7NPSs0mIjgEvo15O{~ z7=4PcRp9M1RZl zp2wvm(nB&;>?F%hpU;!3ai;pDZO+S4Kbw90u$N zj{xIBGiUO}+PcCCT0f+vOrK8ma{_I@UxM!j#Aw?u6(q$3LGN2ROEIJ$x7Y<6(q6wn zzuZoi0)QH2c=K;wtC4h|X|0E?jL6=tffe`ch*)gCoM2qPmMj*37U-ujJLuS1^BSFEWGSR#46&!8rAz4?!L zBQ7x^H2n{3m+r3g4PT#IYcz#G00<)@i!@k>LOA$`80&Ox{qDJD_o>7x=Q;_;- zJDs`nmX25C+~VSn_Qs?7%5lZ)k;}KPW>u`B4e4sws+Xrb@Ggvhs!x2(-tuu@%CfL;=7HWTXYD6Ree4DA&^=57P2s0Tv%F((+IX3n$W z6{73j6rzTNKLB`nMs#Rqk%faJ`)}yTUjGDl%^<&hO;6aFx4&fGfJ!rDseNDUrPBD@X;#deAX!o;pg!`7F1sGA^xp^WTt?e5x;4iUd`lQP75%UOudC8x; zHa?}*K@kk{lCo*3xq#$3aa*dyNuMwa{{VeWI~qDC@HicGkM_nq?eHd8&%>TmU+V8| zM(TGV4Eb#IHh#xZ;6RE>?1vtP+7+JT|E9Qf-+8xL5VsNcbmqH}47N_XQ@P*pUk$Oi z=ecHgT*wI?wrJ0;FdHB$)|~zBL7SX(U1@t@d>OOiwFuQ0UQMB@TI%`>8Qp zJ-G}fh>v6;nHz~^DU<=%-)vWGdZo@74v1Z;KoxybGpxs5V~>@AX)mQ>l@txwA@z& zzx$%Re6ZD9@|1472}vav`pyh1gFdG)?eWgUqBjc{!RHotkndKHwcnjamnUl1s>L2|DLZ3uW1JT8H*>yBU-R3Zpu4TActzR#M8Y>2GO6!1|dz@w*( zoR6>@R<_@8W%K)!Bp_+93^5rQ7Gs;KIO+cbl9vZfXhIo6lzj^VxYHK9QXPrNjS%3k zbeQ0XPzl%DwsJ=GE9e4PsqDYK6+T=6BtPX+%S;d{u{Vj>FErK;CTo=Q=@}#^^%$Ce zk{>_UO|gE-ULX7mz5^%@Xn-FDjQ<9=GGg*50wibxB#J$-41n=?>6fJnYZT|@V{u5U z0}a+!`#+XUmGe+@GYK3xv#_#}a@d}zGSNL^?WlS+8ng=>BhU6%0^zP9%X{eKsHz zm(%!>!M_;uX{@T2u@mBeh$ut9_=(uz6D6Yn`KC;Lyff~14>y_9X*I+7y=G(ZiC^-)5IF%v z=+Kp~-H$I?li@@2-yc}k0-hk?5wFrVxLVN*G_xTcpUWLI!}T?p799nE`#rz&f4k%H z+s9(D>(Ay*nLc(c)8Rwo9@mFZ#xmr}0;pkw=H*Q%zD;!I*@gliMuHMYjBM>dNlQ_^_4$F z02URYJT#yn-dys3Zd6r>o6Hfa6%N1{XL8u$VgD0wjR{nKmfgCz*dr-9Kp- z2XW#=ki7fp#m^M~F#6yQFh~OsTw3T6%CWW;KyR)rm9 zkJ2S%e>AV#JukA#UAWA|QNN=*yAj(OP&bHFdM&SeYBqI|M$KmDcYm$uc>nNm>7}nr zHUqz|>6ZBLd7|zfL8xw-6_>81@U`fm{`&Qd?ZQWV`*K&~aGheT+2SA+EeQ>D89-~nfWv;mMF?Em6E{K|u6Msj zwB$!|<$hhT7hLY!%h;$80nML_`9zQNC3E4FuP0e9K03`c(|jyLsjW2)&(@s(BB<+qJg0NYHS1) zM_O=U%<8E+A&NKTgd#FML7k);6KIojRG#H8Fol#il{WHss@CY)m1g0mm0a?)Uedzi z{Co&-2#f)yo^BrIEUomhu8+6&3X9Hog7NxdrEoKM!RCUa0Zf4)|LdvSKt+((8u zeS8x>K-Hhy*;#S#c6@iySL&a6?VXb1R_S%Ox%a7J@O5PW>`4!Ze9v+V$aVt{O*-qN z{_KWixkmM(|I_dk3aQ|mIt;}53aS3k;1kK9|MM!E!&T-k$Ea&#^XA^hZ}g;5_3b?4 zNAM@0MJO1?+y6HKy5>7{?XH)Q4)DYN#R-#w>riZYx=Kz=;^iPH&dRiKcs=5~`HE&M z{?~gwL|6D87iefiO+yHAqVUxLMllPUt3rY=V2mM)@A&4?+Gv!5SX)o-BcP_vnDl#a zgjz6!YKD4pZUrR#s3e%dv&JVKTmAc12#>)Ivg9j%C5?ybwHeIT#;77Fe$$}kzVYiK z1-u#OULBq&AZ(+U(&54MXTPxrk5$?-$%K1JQx?>|NT9EdV9ywrXaw}l$dxAh4W_8i z4>&mOdNp4wX`AZOHLpz2eu~lfBZi1s*xBWl>${Ts&Dv32c^k#19oz-Mp&0>#U%(k4 z1o}P1-!q8wI`vq3xP60a0u`M2!bw_eGZBYax6l**L9))K%V*8+~tgYgT%l6lyHX^oqna0m1ONMZYFNt zo}{f{-|lZ#y^>=i>H5$sl&`Nw65Mv81k2LCfdEdDkd%#NjX&VhXtk5oDNBF>HAr_q z?|$X1a$mnZEdwO%cTWAEHu(FDMQ{&jD)!IuwNl!5;A`2utfV;}{lQ+cQEw$xYteO_ z`dEQYRb*I5qySgh;R%pU2R7uuV{a4Xkl+6irW*SQ5a3#~ov6I`h+3QzHc*oI1deNYVs&wfLdK3uph%>*VVG$%9MsVWAV&BE?&<2_b*z90-W;n0BpRtCw{zrog}IP zQ%ESQQF>gK{m%eEXBja`2A05`{yNL->A1V6VbHAo$v^X+*2u9y>I+@bnEDcpNPD`d zx_qDogP)H#l`q|Y#9-#R89~gZH8KV@+HY%X>l#+_6s{F_SwgV-xZi%Jex^HVu-LPW zwe^^GzNW5j)%vtoq)GIMFN5NKZ-CKn$q<_K4^WVvMCCFnzbQqFzh%n4G%P14I;h07 zkY#b80-tzApjpvBYlI&qZJj%|CkFgxef z?J8645{>l1~rUH>T84tWjF;&lJuj?iD2Uf^cg?QIEo>N|gI;{m|t9c6QC=l6R$MQfk-p`i&>UZ_0e}B+q0DpH8 zoNo{$-nD@iH&mqXa+b)oL=qF|(1pd~LI*vtzyB=fSPoIAdA9HU>%_P`za(kV!i63^ckn5R}5UJl(=wQb0 z&oqf0X$(*;$WiB4*VIU{;Pns25SAien*YNV>q@$old=I+RVlzHLgKlbkd0sqF~Akc zDZ#K%HmXqjI=SjF8$<8-;KU3-^%FPxN-laiDir}tBLWi6pWy>Wc*Mj5OZPl_zNauk zkLS3F!I|2Ezh8z>2XOYw*Jue9NFsdgw%GpKV{W{iO9}U6G(;lMQXz27j#mPz_s-pr z7t4@k;7op4PkvaLhRMQV^+@HkZ9b_d_%iC)Xzt$OYQmj z4VWqH0pD3~XbyOCcey3rFd>oEP{JHCnqNi zSX;k-e|gNnjjYV{(QOL`)gWHq?f=yOR6SNAcBp|L$r<3&9=3eWzuai8p)i_j?hA$W zf4u;Q?rehf*sH(*N#r}z)2bjZWg$E7hqX$xfK{d00PJhc!_YhFLa!l)M?w%Pie(6r z2v0;mw}IzGs57&%Au^xrj0qFEqHdu>;KYeS2e(MQ?Y^I0vEK%`>yezAHjkoYXZlyC zIU9-{lgge{g!=4^X zd|jSGZ`|`cb~vMBV#o<%O2&z_e(&<_HM>f1l&mRbm$3h>B!J*?dv?9Pm077lpm2xyQ4L@|eg^4ODqUZouG0<|5EN ztLMzSDOruBLV<`(dP=IQ7@3O&%H7Io6YG`=v%4-6)Yv0HT?meOg+3(>_n;}T!prFW_47LsQAchMvp_eR zpDb`40q1ak$E=uqN7lB}I_r4k#Qga9RJX_6G$Muqv38wXN3H*AdVK-aZbse%dhyxL zdV6YkOKhI$J*rq;zSzLoso+TQJuwgW@4S{P!TRdL!#R3qC577B;v7kNAAkSjgX2#E1g4b zayGWy>vo_{6Y;IYunS@uQ?sSt6{R6DeUWnu>lL29vj9Narqp#mKA$KcH=AUof{YCzIpY=ZrFFp#)$p9J#x`H*9KgPN`=k_xbN00rGAp5v(frMmybU5+e>Klm5}b zlE=ULl%w+~eUk1OjrDX8`To}01GiC(gwxAz;SK=I2BW0-YOCEC*IlI;Nj&d&mVVz@ z-ygr-bk&;mLF2LMggN8FIlTTMdkh-{#Zcr*8)F!<0#;seO?k&2D)KpaFXHROpANr$ zbxFADFA!SW%q69oqY6R72y%`#-u0T!x~#BqBU+qUq@jI=c?+bEBV7-5Mp~Ree+@L+ zFv`i%S+19L`}(ijW&+no@`wLO68)!4dZv`{mMLJ-DL({>a&S?}Q|rPq%nf^c?1f5> zsd}o>Di>@_EUJQjv^r?P%u36nhB*AYdiZ7AvOQ9nu-Fa^>Q6jr85UtPDq}TPoK8Zy8RIfh~*h_fO}a`e!!+tzr6EikkH=3i*Xcq zsR5ukxSW2Dhs^tIlA!7FCDLd|P^c}-H2J*c-5T()Awsa=x$bO5X=;lvGQ&vNK0P#h z8*mIZkim42@Ermy(fLw|)I}Bb8&2+%P-J8)k|EX&3*k0Y%mhQPS;X6h5rzTVq2U({ ztFLk9DI>KDZ4REI!tSrV?}?V*UExpN=|7=Hm!m=>M3SU>E1?5l53i`T$ae8&QTyj3hq`;vCSqeIU%KHa`(MDF~va$thg$5AUp z^rR1*9JrGv0mvb9JuY=X)dgsFfVM?E8{UKJp=bV1U_Srr*RQe(M{aD<4Mfodl-Ux%)bt z!6^-#E#WH!o}6X#`F9F9*WVXMHkBcoG|Y~xhb$8GgN^;h*&pD9hc}QY+;E2PN4(?H zmxf6NJd_@a_9+lCULKalD^|*XcMlOxgv@f7Z;N*N6alRCe2)QZOi%ygx*9})Yr%S% zh1~cJ@Hj-yK>BJZ0?eF{iDH4n*uS}9urr|Gcw$0E0kR@s@i{#`_1I02;$SbxK?Vv< z=r^0yE`MPF6J_4rMu}5+JRX&DcHsP%_RMbM-J6fbLsF1YQGw>_vOdKo@Q?q)$kZ(uvhx6PjSBA|l=$bNG7vxkOQu<(H-f$=Lv zCEf4}zl`6a-bhNDOwe}@TbD1b&pSF#qS ze2~W~gQ&u)!7x=+nK1ljVoV7fC$HrH3NbKiz-70l3Hl$Mr7oeawHBjvmeq7wro_kV zO2eVip9S3__U3LoNy3Xb38!UD!;3t;uov%s<>6qTvqJf;=ZReNpdXgNSLX-%b<6%- zso2|i&CA6N{tMu{stA|FB>A#<`%%NmO{Tz36Sz(!o@Ia+NsylWYJB-AA?ODjk#zwVlLWnQ$avqZ^>fuz%?_bW~!{HSO1 z-U99|bpfjGGoUW%t=N}$7Gv*OF(E5eX54(I!*n(r*yRKkPvvtpQabH<-SXTqgYhIx zQ~A-QL87{`#p8laoSZ~Qu{ElC_4}uab#z6_gwpatkX1eo>}0l}@Tu^R*`w{D?y(p8 zhYht1&Ja39fY|`ZtT>;`o=U%mp|^iS>%Lw6fcWdcbK=lSAZGd)_E2@G z+u!@(Zm*Ic2q8@>RQb!$+5zJiIh%9UJw-h%ea}z%!|T&l>@|p>W5oNFX`PntYUMw{ zOKX+4KWPUI9y1;BP{YuXajw)&)f239-v^eCjR8ZfZ{#00EDcND^?i5F5Cf*{$g?v$ zd3lS506*#s1d4At3n^53#Y?GDGz;UGl1!M+TQC42#LC4LYr&CPZ}aId_*M6q>3M6Q zFFd+mJ_J@77ypEQh2L&599K$D2vKxeA5*7vyFk$oc_-HNPouc{Vm(BB(H6V;M(!=C z=iU-4OfG-$>no%sM%yj#=YyG~@z!$38)+^8t<=KA#78nbvEuTlQ zM)Sa3Ej8W@tGxC6W1K*uJ21Bm5CiPMjYwad{aq4hl;sw#^U*w{av}tC#>Ix9Sh0qf zAM(xr6<0EJUGj+@GmBU>x-mAcU@1=X@Ov_9CGNG;h|KTS;UgD=zr^lahdm9`LxcHo zM^xAhi1B1U_`zZN9tg+acE)4jX#l#Xc~AD7hHpzLc16G<;T9Acp{g_JC%Bwex7h_` zOHja)hy2pc9WJlSJU0bY`NEb2DH$fhw2&A*ZZJ9P*q?M|U57nhV& z(%KsQQylefrLP@gx6)AnL@)k#0{sKJ*NCR3r{JsA)5b^~3Y88?3|2Cl_Q6;e!-aBhl8+&Rf^J6Wry%vrvpqzfwi=kZ7L*aeNg%A!4tP{3M7R=dbF|G+;@W zfoE8FF;)^BvS2KyJETiJPM@lT0gS2cPs;CnwaqYpjLmom*Z9c1C43WT; zb#uMVK0vQ#D04o_un~_u5{gOhY{)+EjngJp0`D*nFhU3S84eK@L7FuHd^0{^CO{2C zL2V;;%fbd2Xu#SONG4MAZ4tH00j6t7xNZbswu}L03k9pWs0Dl&Muo68N}P#g?ilk6 z6+-P(IOC^32fKu9-*VQ92(Ch?KwinlwZFe*nQC!%xfk%DR3tTt#)~soDF)L~LVhcq zJu z>b;T0g$_@%$mV_G3e*+N=5s~-*k>5t3r~cD4t(jbR~FV~iJ=R|{KE#T#Na4ne;>i3 zN`g;O)%vkWLsP&UgeWOJ!3Ke+qyJ6gQ3i6?qZKffjztzZQflzm^9gaybqMkKEM#%U z^%f(Qsk}erOo=>_6S0N>^UPz2?Wo~!&n6xAD*Il%kJw{x2|wH8*mbS_7FDy#3~;V% zR$VY>O(14nFTIaYe<7N$Qe<$rMDlyjXG2u?~gae4NuU}RF%FeNWGX3|#GKj9?v zlQ6!QwNhd^Qt^vavc#VWFIJc7YO^{rxT@BoozwNOrHJ=e=)1GvA1$lQ*#FCDB*nCz z1LF{Nm!Y0zE~^I3S%zFOTbIRY=Rt40F`eh|HeBr2k9aP1hCq2Pr0KT8F<{FuWF>Sg zpbl07&a`T|e z50bQ{1|8t>vGF-aJ6-NWK6<>~N$rAxO=;Ddkd05LP_mJ* zgzqtlyouWH4RMUYSd>mtn;ozImPnZcRV*Q`8TrtG6git5nU4A;gVPho8Y%?i9M{Qs z2K>WgCC*6uwV>ym!ETq06Zh=6EpjYvvd;iSHoOeo9q6gxv)ieCe(|(uM2R73;{&2f zg5;_|P-w$vI(VB}&ys9*%smQ3v)WLCJ*yYbWa;=)A}5_-tF=zy6~)+wIdin@c2M#Z znQ^4RTzc@0JEt{AxVbMk!`bdpuHtFWM++TYx{IQt)C3m_dHd0ZhRz+V{6XgO5 zxtO-0FepWhGDy{Ea75x^MCz4bed6L`k;oR%^nEv;gl^iW=nKSW--!9x+zY1}a_PrS z=$@|5ANhohq60MroM-m~5{`cLM@)!*fY|IEI6)3=``y*OCu>dykHr%U*+)S2+j6j` zTxlgMsT~aQY0K3+DDhV%ulxi$8i$yaUGSRP$M-V_Frq zTqA%%76b#yFh-RkR9txsR|7Vn$NTvFUqu(}SluQM4#QZW zA)eBl&V%Z|t33a$dCIR00ukT@O2;1Y3w$h8^mc#9Ki#NCy%C>;`*l1lm(B<|`S>y? z?^6u+Kn~O9tct&ZVTIL&yJS=)?~&wTVmnUUN$T*8h&It-t|!sqOzag4c1~O&hcf7$ zBHW(3=;}(=>eo`)FW}Z4DvmXsn1TXFnn;Km3Xud)BB2#yp~25v6kiy)e~{iKhtICAEU-)}lOA&9n4_+n%_2VU(8l}`{14ml;FMbgi|1Sb zX^-a7@!W`WbUG5V zprq&5`Rl|N--j>c2stiHnaDA^Ci2$SiAqk)8N~E_&Ee%#(4a0%_n>&&+n{((daPkn zBc?U?EsWb!Z$dr%-%j7>Q$Rm+iPYyF=pAk4@vBi4zaQY83q-wiJ=w&jhi{ecntusU ze%WRFZ8mAr@)RXO>anQ=_w5brAFPr2o#GL>YSMT@~FOxLSV4ot_%I zV2TTn1Zw-<+84$E8HvxnD13Z=VHp|t;zgl9tRSC%-Q>xuq4ar~^{L#GOJm?7@80$O zCC-P(kt)i0Uz&L+R((l|-0WUoA*JJ2u@2b55JdS|8J`*iRO%JOg!h$EuIU0 zS{_jSgdeEhECi#xcs%5ZNvF3<=*3>CpG7=iHU$)FF3+bJJ7;qJZy1KE)Qp7(OMLS= z{$|P+maF8oKOj|t@EXR+()TYn%U1Bjf?Q-9&LKQDa_r8}yf?yie=zp24}2tph%(F< z4733A9+rs2-vb+mzgHrEIrE@dNE6+2Al$7t3LS%ua{Z6TX4YRJ&CJcwqXt0Sf>DV> zV12*WvDSgO*tejd5%|@levSwVgxxe}G5#(I9#&kpB|KQD0M)msCMW^U@qk}N@~)X| z*rnacf0j%cvc(3wki`a>2^D~G96sP^dW#HmnSXZ)`c{x{%<8-a9maRQ?rq;gFaJ5A z-{cdFgz4PH;nowu`D5D3lPg#68_MFo-qRfOdyuPQq{z1>LHZpWb-XT^%C!)Atd&zxP3jJbL7<~CgHda zu5~fW%9R{<`#s0UCj(8jW*Y`0OJ8r&;A4;bi6%UH*=H~E;wfhmv6~vTYDD4+&W8!K z54;eyX7x_~i>n*zH-|!zB+Dy-UqO}9yl6QbxlL&z#p5*{WSU&ztqyKve$;1h3GvLki95`f9KyQR0=kdFmWSd-@-o^K+7!Hs{3WD zs5U<>*Um}cSc#s0Ds7Z0(g~_L%sOt=)u<=VWdS;n0mC+X)db4f{gy|cim5!lWR9EV z4w7Q>omG)q29WdC$$OSX5o_9m3$7d?-apH<`tMq8VGaO-*e!OmzZv1ecZbPuSrJa&H4Vd_5XFI$F6kp@Xj)45^P5 zUC|wXKqcgNc6}F-M4n+N6d=Zq95A^dl>bOL6Sqz&91#A*je3xbjErXbybP9*>m5Mq;%?<=co+~R|^fngciNMPq-+S z=rODL4NDU~P*05dymICxqW;&<$>g~*IYN$=@YH@$t)GE4f8Av#HwGfh5wVGNaUHxl ztnL@4&pli&uNRp+&4ldC2!zR`x1U9XigV}@_BF^J+zb+V!0pugy_0xF92!SRj{eOl zlrTivA515!1X4t>w;%0}A2Q8!KbpuRW^cOJ8}@~I-l02ht%(jdJ%0xhG024sM{KB+ z^t&NVEiy_(X^&%XBrQ1x53j~=D6DF6K6Bmc3ryXmIfsVzOWtDoKZ~T^ApWxs=(or5 zt9%%e|LQ*;35n|;Gi$%}-`4b?m;n}kLJ8AWL}h~>LKu`OHUm6Fj?V|GOh0xtw06QI z)UM#<-M@3Inzj0*sfG)HVg`|&w|F2FzgF0#4$Uvu?o1J{22Cc5bauoq9s|st4`oCw zF`5z6$uKKjzcY)a+^JF$kLHYN^~DbuE9}58d4jhl{W?6}OQ-Hei+=jyWWZiP9(`zp z5*<3#|JgS#2UD1z#&1~;13dfCPyKn8(xZc7mohJNZ4Nq2NKhM@u&+=^1Zk@;4y~xN z5=LMuz*LKc!#4VA%~*9lO7Lwg&)_tZZhTx)>D%^a1`=7>)Ln%ANnuv}L1Q%A9^lil z_5vuK;>C5<`kcHhRjYR;O(hb4ID}1X)O~VPQr;iXi~ z87Mp2l+tpfVaM~`W+)v&FEf@oFLuEM&y ze3v&qVPj&d@EJmrk-)vyEbywKFmDATC6WQ<<`#G(q~)KLH5u4J4TTkVGElX4jGJzt zri3^og3*(bHk%DR&fE#ivTCc6No;GVWDT`S07&usUq5OHB`q!K4}KvmTwDNnyn+G1 zpmReNg9iDSKslYN!0S>KkMAuaRz?`Zm1LCNUpbG~e*xl`sDcQ8H4X#T2D8{z=3#<% z?(YutdR-ei<0&BvX}<69>S{H>)7~ykJ~_I2zyN}vHhA*R%-YQ31zPlKQi^0L7&>5 zXCQEJAE1F+gFig~aG&4q2P2A0=D{*C^WS5fMiiicO|&pkgp)&3=+6Xl6 zb!BomtE!bh+}i?`bn&bWc1Dw^4*Q-7;Tq-xC}OfhY3%I+ZNF}J0Q@DfrTYgxY7Me> zkY^aZHNjQ-dqvrne;#KA9AJ$S%3{Nro+i(JSS3*XIvc0IhXK4gAUYm`gy!q(+tf+Q zikl0~il=8Lrgl*CMIn%0Rp;o=$ijo@cp(|00kL@di6tBco)I2&6#YG{;>T&e*L*P0po{)|wSi_=w@S@q$bGyGwe^_rkm2AJg zb>vuYfV^hoqxzKpw4hTvc`%==!Wa=0`e+3Jql7P59H)?b1sdX7&S%eL3U|5U=8M21 zEaKBI%WG=cBaYAL(ijK8Tp)*u>`uV{^#U}Wb#5-E3eZ?*bCvY#EEyV$KU=Ar+d7vO z2H($D0*MtDxPiAo)Xqft>Z^*1`WB`{#zPK7k~zna`MBn%xP@1I#v1RpPq4+uaeBwR z8?e;m3z|+82sCUVP31R2WbZAHg?Ci%aQT#XRFuDN2bFjh&|rAvLQeo0KQ(v77ng~j zHG)N0%7QKX8ZNXLNUM!y)=pB9_7hh6+t+=C|u)3e4f~6a?ApR$|v1}=sH!K6Dr4Pq)F{lA>3MXp!2nTY3%!Jq zO%=_NQD5LOZ9wThoOY>iLqow~J1_aoc}R*}Xr@bxml>!hl)Re#l~TwS)FA_0I*vJw zqJqv3FF~T$xum`Fi#4pHFwSpG5YxhV!R7GzG>G=#MJ)YjpxwY~>>k)A$1Q&j>W1E6|9gMhbHjcsg@ z!JSXlRbc7&I1Xo;+B6|JZ@E~M)$|;LNro~JQ8-Qm%<+p!1UsMyK*Nx!Tz{nET(@Uq zG1D_qgUorq)I-Rt^jr~{Xa$ydZOy7$WFppRgwnuoYJR|yXV!X67SUKdQcdhffY(HT zr~mhy;&Bk9qS-bWrpbUBDK_am`mZr~UTxt%p|BhhcvsBaos?n*Z>i->tAWUI2aeQr zXaB|;JvU;rxyILOU2*2Zzq|5DmD97l&7S$1bCpTUMdp&&VVkI-PiLukA2_ofuE2q` zrLv9`S4+a`1AWS7H*DTOV8zwb0)XFhU_9p-{BA0n<-BUgT#%_n|4$w##>V55P9NhT{`1U$er_RKWI@2#v?5@?M7 z%YQ9qoNNJP(yy`NC=f2bZYvRl)wHFj z2*{I9MU;TEuwk5=cg9!9vb=iRo=;INNrZ;O_8z?Z`hGC8&sw46u>qsg#n~&<+`nN!gybqi+4-v+q2O@JsJjuV#d4BGDhz4!7hG1!;YF|CehAAnRh;1&zLg!-l zuD3cDc#*WyN<$gdwfw{wy_F3SGi<+km9DI!3+QgJ@ZO;*K~_VdHZTD&Ck2B14BVx( zbrkVvX0jtnJnw$_%h)?6d-Oa|l1Mu>Hn^yu-EB2x`sbE{29$`Cc(g7|Bc}HH_nHPv zZe2f=-CFB!c~u?};qVoZpLw*1J}F5Pp4~J$pH7=*4E4ouZwC3=iz!pmM8Zg8^jryO zrZ)bnn~ZgkrwwrWCT1!LMLSw@O;ExM3fg1u)VQpQHi*(O9d&ARGofLB!@T4I8h7%|hbls%u4%Ubc~sh3wsRun zFAI^!OmmBgFfSW_z2wY(b1FAF{p5ql}PK^1|pw+ z7*lEC69@)gwOGsYe%SuWB=tBy{*?2?NYw|NaO5~S6Tz@`k2{_f|2urT(RL*)$jI@} z2_AFynrU2{S_Szo%4+g7Sd#hn!@&DVFr=h>G5VbzpoRwJzqSa7m?m+mPgC8@X6V1QLW-o<+ad0?DB@;eKYGH;1{{7LyiC5@rM8&92+ zKTI@c#!OKhenzAcmWh;gkgFT4mf-RT&43$vnO{k}>!`~L1n=&9yzL|m5IJPNR5n}% z$}FNGvzreEN_bCbKA{mxOE#i9atQNZBR{2P47zxH;OP5aNYhVCu$&K}eTbAf& zT~Tjl;Q@8GrjC|JC7S3lJ}G&=9+t!74~!9w%0NPFWx#7@29i1DTO#fiBYMP!kk!Uh z=Iq9sLKb!#UNLfdT+DyG@(aR-m%&w=hv?p%ceGinMg5csqlMG$i2Pf)>pSEW-9&xsx|lW1EFv(8=S9vI$V#8he{uIm?YwlRVG#GX$7jpP6cC_@jd zf})chfwyX`a4NGL+QS6Ets@3*9l(1wJp(M^GC95TR!_wa&w?+`0n_#86jP4%4oE!- zxu>PcGa=w#FtYI>$kYf{Ok!c<(_+XH8;mRtMX?5+v-iGV`OM$LhD;-K$3IeT>V9WQ zO|2^3b{^= zzMGynL}TA~@Zj4;=fZXV+}+=8JWu-Q;#OECCW<>r_)8d?tx24ji-d=d4}RG=v^~Fu z1eS~<9 zBbSt#v#?_ZP+nbO!C)2~87(_Zz*msb3{P0(NSGyN?0w11vSX33?0;Tzz`V9{~;bg zfdHfz4z=JZ>Vkgi9j7;o8QT`uM%g%94z`$#3@vfvx=MZo$nJn3VmmyItHk4gzEWki zE$B3|Z-?TzmAll#Y;oY{*Bl`ELFSt0dr6d)!>gee;1w$p4ShGXm`;YXM9xgBsJ6`b zsPm`7N=V3uKnJc7Tjr{1kYIt?g9*(O^ zB!C|A{I44>3tJY|ZWUN>j-Vv4sM;aUyuY<8QRHrvew+_*0DCO(KjHq1#m0&&^s>uw*#lGv3f^{6dXbADV{Vg&9a4J0Z(R3WHQ?U`OIRVplqQ-lv) zt7~5{;1PypAx+6%14&9$MIvc4pB9m%=+x~u)YB%i30 zE*o!CHcdUcY?&2MI`P{1P~}thg%m$*?2UCpXr_J`IxigQU0>77{;= z#amEoW!*$<%81IzH8J)d@mBQZ8~rL@i6xfqsRG~Kr3sTM4*4|Zz8pZ0r~1MYKLNP}&hKfr4x z@w#bBr-n>p;&mV8(&N*Un`8{Vujf`r_(!l~dAFg5OQ9x4c_p9CCs2eqIzV(u$nV=s z{4{Ykq_%I`Hzx$PTKbQm1>PW;UfDo|nby9twr=Qpr)bi&%(&n&ksSV&&c_i+hQe~* zh2^L|Deu}68359>`le;FAtS@0M-5$EW=WxEB11!13}WVHgl4%W`Ry+1r0cjc`UD1! z*D>rz>hAYSB;N1MRuuoWG>(VcGg0;+UU8f(sRW0dur?aKbKD=uSF@qT7ED@~nar4o z5JT#~MN_p+<&^X{-x;o!TKtG7Z%_kEXjnKxs;8K#PP+zJnlh5pAiF(i0yP;TJs48e z9xx$@_y3oxV@%pMo-CvN_Te$I`B7s78lc$@qV30WiXtydk;sR&F;0s-G7Q_ng}P50bJg zjcwpSQ;E$QXyHQ#^#F&A?JI*8c_hX51+SodiO&RmO-GRXhP!-)D*Hbod=ybEo?4|8AJa5=kpB>|#iBee>@FgFDdx0Fiz8uW#-eS%w2Y zv0pSHgtBNyr&BgUaq#WV{)yGH%!&uym|f*wx}cKL4JI3yfwq(T;Vd6-W2<($Nx#ys zlMZlx-#!0ex3<={3h(#8G}9z=N>r{mA%|~BZv_A-08)9u1b?_##lO9y22+aPyUtC1 zq<#(>jdMM)h#ERvyij`oCu}=wI=_u+Yo;>43&j1l3n?nmL zc=`xeqqfm0bN-c?y48XFtYU^+D%YIH=l_XGhoGlYnJcxtS~<0*(o&je&ELRTaRGRMB>^O$8h8nG7mOrhr&lJpxNKJYDLF&5HT}4a^dZU%7)0D3#J}11@mO)0 z*87<^_IrnNI6h>S>;+XKVErGe-U28K=ldR}8|jc1>5`Q0mKG3@?k?$WkS^&4sfTXq zR6syLx;vzi?sxI``Of^`8OL!P6n3B8yXT&B?!CQ4NHR2H;P8`TL>#6ZwBvoG<={%7Uviwz)c9(MQ`k zi1-&Lgm1@VZG42q8ky0+1w-I?MT)Mte(00sX&Et)*QORg;h;=K7Li|Om0Upf&3YmF zddK_{Pgl&v$5v8GX_v?G1v;^{@}4SqlRm>lI`9O>^j57MH2Wfvpr+>mNuDqiW|V!E z7eBCTeR79jv2l(}X}G=X=G9{RAcvQJ2U@X}DI`<*U zT9rhHJuaOzDr4GVkua!0QGB`6r^9$Ryi!8T>VI|Rmeyq6dz}cA`s6!nbN?}FJ5e&* zdqLzp|Qme?vOF-DVj!zj;eOD=N_e0niI8iE*~5L0?2oYv>)Ag8ux` z8XlHdouDd-KmvGL^}+T^7A^F+feU|}da|ASATE7GxC{K@Z7Z4iV zD6Z8}$k;TCL(Aw~A@{pe3>KR>@r1X41Y;MVw`*WgM7AmFvDe!*9u7Wcy6+K?r}no6 zgXz8~mbQb5%?_`sm~S}W+P;~7_9)b4GukP;x15p)=tVg(hD5zF5$h(=$Pad+d6wou zWhV81>x|>lf@|F}MUe=Y8p>j#@>fb)y@+U0LKl{v+)Vfa({!nW%siKGP-_sNM^D$reb`0O9^TJbJF$5$G77(Ny2GO&3JPQ-YlfhQq*Bp+ltenYb zk(`q=tG~Cf3PRkx^$;r{KQkw}D#C;Ajw||Y^_$7GiL4w=4bVU?mVEJ-R%O*$5?$Q9 zf?X~b;HIo4CrHT&)g|uyyitlohs@91;fBJ-#>QC%L0S5Uy8h2#_|fIJ^hgW<##*?)KhYhQ3(G1+&)H5wn&iQlv6p9^?ed>R3VwfNb2~gRc5UxfcK@1`y{gAyI`% z&--0uGztL@RLQlA&749}kk~Sh@82iQwhYF0=jGa6mm?7pk@as*MlHakb&(i4zS(AVQq(^7 z3%Ye6o$?QysWpW#AK#uy4wCi`2s1RuE*jaQRjHhv`W3G`p+jPt@0vdj%Rx{??E#b_11-Suw5Idm@yTVgKc(Sn^JNZNynC< z#CWChL41NnrEbi9zpopOhPHS2_C8-5)`a6`|If(CiM0`Of^__Kz33&3MI#M2vU)V0 zerpx9G(UJ-3O@I5du^Honfn>5Ji9SJp(3LvmZ_jiEuFvyno@Q z<&A`bk^zN_F`Y!}-shj6T)JZ*tg-OJtd)6Xc;bOlp7@UT9vg$3JES*;po`Qf-J) zm{>HqEbD@Oz@xyo+EKeO=8f6*(Ll~ty6l?@jqOG z&IH}K3DA)|cbFxEDN!*c+ul*o$n3%KdWu6 zNvYytd&hX(H<@5|5W9hnNF!@d%>4HgkZQiGaUqX!hRARtyu}x1+SVG=rz4$TK41-( z3%wYYN7!HvNgvxR#hE-6)7J)*Y!Ap(i)A-$|E()C=!Q@gk+J38wd(ZGdZ?K0$|2=O zcrA?=NG6{Nlq0BD*6e6}dsaJg^;XkK>p!~)Av4K7U?NQaIp23p&8emiC!zKpy=v9jL#}UW2FRT14@4 zbVf|ZC8O(_5F7L{b$KO~e$Dx#x%1VLDu&9|cEZ zJc<2ggrcg{4otA20Ci-0FmI6q!MVx4`31EQFV`OhNxAjafGWGb^mi_US62)$ zX0KHwjM@5(5p)x~d7ojFJm8UyY|My4isH|E%{DZ0>7o=y8cacj-HlA#e56MVmXqP9 z7ez7F@m>+L9Y3@&>AF0&!oH)1r=s%Xfb4OJ!X$$OXp3ktFUp!v+MvG*rbbI(MgG9j zhi=?HPthpnMm^nYN>diRZ5T;K3hA!nGzkyWz*q!2p8D5m(gG;cc|T@K{G zaeJ_n;BzAi`EJ4Y-QQC*Sj;f<_5ctFaQ(jxw1_w*yAz9oK4f)bn^M2CQZ|v~NZy4? za)qrJ1EHT#Wn*z*^BhQ3>s#DAYFr$uv#S0(0f*Z?sKaGq;&oX80zh&Z8IA)V3BLva zOM6jefY&)}u_8f&i=c5`Z1bpN783ai$MAeMpWSfJ~0b8Gu z=S+qUZw4T_Hn>n+omk~^O~U(u=_&7q|H{aS9WC3~g)5pSErLjEv)c)cuz<_6Q9{8; zp4ok_aKr3Z5bnCAXxsU1afzNb08rg$OoY8V{kc}#N|&M572K)?x!{e0<$uc}qf?X* zOrMjRQF(krQN!MsA+ns5Y`(597k;48u;D%zy`YShxpbber;oAWyN$k8+}6w zMKEs=M@w~#TG>_C#pRoRbj5SxNN_;N>nXovz+!`Fs1A41YsTL3`PUpC{3*X9Xk@w# z5)+!B+}Byn)z3F)++uq#z}fvU%UH9ik+Up@mn8_J>!>d}%+*HzcB84ok)N;Cr8dIQ zqFoM4(sb&A9uH(HSxW@vZVk=O2Qbse^_U=B&fYCbE4O2}p@2n3z4+L|R@d=ZNIs-6 z82puM!d8~#V7{5lSRFmL%wJ7?IJL`rkHD#-#RHjE=iZB#A&(xLGXwFht_vU^P-Ec1 z6UBFG{buYej;`hXrE=|$DkK})4QTK{F@why5T|d=fv$&Ytm0(IILsbPva)-V@*+xKUAFW24y(jPkJPte2mf=fTL&lqW5ivgr@TH}uErHEL35fh1>-sQ@hwnX08bQCTACI`wlKGpB2gYfTT?7D3M>5A zT0pDB*y>JwCPm+Ou5_^J@OjhUpc}EsZ{P zGLkC+SYaGT^QQxoH!VV){(srufFMP68Yj`AJeW?SNwtG<^v$#zQE1W~_0nfH+O}!> z&r+m7Go!W6axK4YE@nE~#&?Za12Y;J3`x;so-fxXpJ7l{HswdjWj8617Ig?p4*DIG z*0~d@$Xf9!X7R=7a1plhPGvUmEu`otrNcV9Zsf#2OBK+aS)RE2m-XU&bN7JrAqX4U z38fuZL$gLznJOntAA!xNY-wzucI>qMXBA+Ty?>7!LCH;^2y%=o+cpf2crBZ!p9y|Z;j^ah@Z ziP)5tU4M$&I4XRB0!A`$?*S?g(g*^$Pbyp~v?$_VqQ3VIDJ={*l0DugrOv`!aok2% zUq6|wdwo(}0t6F+N?qmTZVcXN3+`U7q&iOQMiV?MEi7^^4JWTLHb;1jQn+!pc~>2p zMd;7@TrFAgd|Aq{6)$(W4-!Cn`!VTE$Rr*F82fr!*;iA_`oR7|)ONaz?Cj@@yn&;- z?F2l|hBbhH(tX9`kUH)KW>Yh3)W8OaK%Qh&7=bO~8De+^WJgMjb0Io8jwaUr2 zu?iLaF*7|qFZBZ!9C7VuQ1$$9!Lt!6#zqZ?#Afl?Ll7HnD|$C#mqM%jY#E8(UOSfh z;YurA8o8Um1ZnhlvvNWhI01YD;_ZaQmuo7EbU5|#2T9Es*~6czNVjvq{V49^BDE9@ zOc6YOYYdd}ghn-s^LgX~?^%7h&S37r1gJR~Gzidol5$Ha+w@e9AN%$rh(e3%j=miAW4 zIFN(IOyDfwnno`SrhheI$gmUBlN4bBa5v0#_gR8OgP*`AWeH8gl%iCj!;W3O=A=@w zA7%c_^jxH|Td36R{QDiV(uM~<#>z^ZdP-d^Mh-Yz2&4PdAJMG8)Ivig2+J>IkRpYd zLqlFPB5ubea@Wqob_v&<3V=W7-c=qNRFah zI)@f?{+kos>vDF6BH@dODPRNs1?f4Ea#D658EyC2x4B_Y&3s{jkOA}c_|03>Wv#rz2PPJP4RiB4JY}EA8P`vF@@q5|A?c0Wu^;V) zR4KY^zMHrc0lw!opp215rMiDR&({{)Fbb4#gFm%>*Yy~X3gm=czy1PKG; zGI4QcydT&-&Y$rztVO!0yBX2<>QCJBX;dHMX~tYyt*W>jt3hw9g?B&SzsvPp6YyxO za2hV$LlB$(<}Du%7Gv#QFx08Y!9;*}pf-j3zkmhIX@{%bL5;o$VvVLC@n*ty{(5$T z6w>o7#p|eUX}=dV_}w$*`h!3ZlVVeHt4_MVY|3)`PceWp^nsi#$9 zrn2wm1gwhH*#*Y5kR3OCrrik+Xrxq0CR)OGL--e}oPom9Yh_k8N*2jTc8l_G+%U6A z-wabA2oByuc487Hjoi<8Gu`b@G>-4cj%Fiqy&nwe?zzMUfD;JD0Qa0ecfGucl15Gj zsou7+=s)gX&*zS?vVwk8MldcS3&W--PpNZK9aDzJvV63)vXPRD&5`)Bhqd_&HJM*> zAp`+REYVO`7iToa`})v2C%ylaP>-p)*-{DzOZ1douv9oc?I-{|~bWDFka^<q=c4(enYU|q$DoetAig5fUmKpG#s=5&hu zA8MM_g@MPcW!8?HUP6T6g@z;vqYe=XKO`9VSiMfrOnSzxqh;S7E#LCe0thSJmf!8`IwuS<@&?Yw8Z>073fGF9X-7{U(tO}ripYh z{ozjWwB759<~#Qbd2CxQ0!3M?#RSd_`DaQ=1#F9V&i&xV(e#B6sy}%~}~h&jxl)T(zJ14HZ-0oXfk`C1czMmd-}yLA2IBDLEN?0 zqKnr>TmJFlMB30o3xZFg? z25X<0sOF=_8q0=X)UE1VmfIir)3gSk>76jAonLdezQH!HO-3T%KczFQf!Y<2Oh#cV zOyj)QO5I0DipRq8qCvdoPqq0$7b5i3Ve1vhdHYpUCi;O95U_NriQ-xZ!s_sD{uI>%QIj zXrQ7{6t{6hX^n)H`pi^%#l_g{9~{(QaDdC^g*ggN)eDu))jR>ZH|n``e6j_eIN2nT zWr)c=(Jtz{g2KTvoDr#pxz>U{KnK2Du&)*>oP#%>L5ak@?Y#f~I?^*6TpYS68F?u2 ziCjyfFa1H*Wi#tnr|U!p3Ap}?B0veGcRHNHvlJ00 zsmHxwW>LrFz*a@1*Mp2Zqh8jQ$R(8XrU9;OP;#S*mMgAFl`JQL=OBzjmPJg0$q@zc zNd)t4PLTYed!2%UOZ5&eE^6jK(JUF_W;YW!N&@i&`rQ8y=LAeDc0p-qYWk|61`}lA z0Pz4b#>~4()<09QX}N%4m|Am`$Oo(jTVi$1dLAip1-D!8y2qNx0hiEgs8wSAruAPY zwe-f`?%xobmOaDb&nHe&muj88uQ15_xiegiEG!7qA^G)vVSj7idshp~hTfo|Ob~aW zT_T1i3=#na?~MNL!_J4c_G2oBq4rgtA532PbnS)r+~lq30UfUrznwF{fC7P3)_JpC z_`#td>0dOZRy~Q86coA{R4l=u&z2fYAG!tHNnKyGZmVk{4Ru+>`1rV5tyz_LA}NT) zubvN#hPlq6=E7(FkscW>0yka(yZWQp!)XZ?@-3xj=QR9(L=``XKb+8rqiiKw8dfEU zCKl;=WHNtCWvj>r+*j#Q;r+x8{sP5S@TK$XxngTHvRLPTA z4B7!BXKzTwF6MtXC(tA-vX6eiC2J*{ix?f%K^W!uVN6&Z3S(aLsjE0U?F_Oe*`{eC z+6aoqLc6{5`+om1o-sq;JWTFyYG}|JV)?PEQ;Q&?HypB*Fq!I<1uV97He3X*8TpLS zSXU6xldt%QZG`2O73aREbyI1zHs}%dUr|*vY;d_O&cO%uWN;Xyn!^CWXrczVx3&m> z@S2QkqMdRUDnp$t*)N&4H}@J6SG`FpIi2~kq4cC`*Q^iHnDEh16~8kYnFOTloi6rp zMeQ^4Du~|wls;#{h6Qc*!<5|i16pJ+e8*t=N`;?;qApH4+H0c7S42}uTyvOQfPxwA zbVj01FE8tlYCLC>%b=M>1|FZ=3v0)sVtrvmv@O*hH~J;}i{-~kDr#kShy|a>7PYJn zL+VPqSJOo$ei-6kFT>N)L1~Jd_2>;X%G56+@oYc$=gm@@tL1>bB^;K2C>N?dDB+^0 zCZ?x*!=zSDN%+B8?@9&`U_Ad3U?7KN)~W*~Ey3n5W%Bjbfvrk2Mz4PO^}ZyA2vx27 zrzLOz4x5(Z(&>2es0&tR2Zqb5%yeIV^gUOQp$xN;a}dj8)$=JPZd&riz?BT8LRs}7 zL+kC@ki|6qgLt2Zc+|ab-(kNTGn_xB0mG5L`zK3vTA2(ZzvkfK*~sI*RZ;L<4($mK z*fmg#b%v%Hs<`l{{Fi?C=_$PgrIW;lQRiV@CpZdNcmmPuiTFE9RW#vQvdO8({v7Sr z-lYNFU=qgR#?10^_Q#*KDZ^~wCiFg2e@W!38c}6gKZ!)%xHPznSvb(V61Mm81G8`5 z4>_MbXk(cw{L7M_b7fGx{S}$7quH8P(~vZuZI`A84L&`;_UN|0Pd3PcQ>C%iA>kpY z(;Y5p3*xe2EA5M%L1T==!952`H|}Si?g^;^>byIL^~wRYqp|$w$7L&-f01XNC*D9U zT)<8jYm51a(Y4>x*2;)+x}_QTugHkWTdB^DKh<4W#%BHuCl{U zR@eA>zIPkkU;4gj4hNQTi)o>)Zl08Xh0lj3w%Vs`E}vT6&#oG^wGz#iJYjw@xG7+A zZKpSYnPA$jcg%4A!>NFua|2a(mVryr7u;*Hop-i6yT!oB6Tl?nSyCE=G7YRfTocNzvvH>z}xVjjj)bmwh|? zb+w5cx?y4pc_)HAHRwQyI3`}} z_rAiiR`@SpgL}fa@eZk)3J#w~!$#D}$!Sl(-f7@gDE2eBgKo?7&TrV8{-0!f5jGS} z^ivo3*eBKY6D=o-Dt0@s=^2JBluvmqFfe1Q&**ZoarEgll&jej zxhrvuy0R<{>Mr_9rUmy+IEn8`3jZ@4L#-n1X_(qNVGxsg<9;%rb&e6@Sn*_5ewNZ( z?D4N0r&Jji8PvuZzN~{Ye-*N*fUGzM&82qefFml= z(zc=-U6?NPnfm?>Nbm-Toabd!~)d501pC;H& zerKW}@r_7q;?CQ{S3n34wD&P4=3xPBQ4bQz$Ld;UA|I*W(=S$a7*y&W8i}i~{JhEC zmVIG6cqU@Kj(l;yI0(XMz_mS`n%0x=O758ZJQw?boB${sAi-x^Uh+e*wa$Ccmmo z)qgX5qNG-qi^Y0{_rcc|)JXjiK7u*x&PMJum9w%OH!~h?+%(K4QKGg2VZ*W?lgrY# zz%>D%I_NqJm+XIgPT@9_)kO1<*D@bFCj<2_$r`ET9_l7AXEE&0x}dwDS8k_Nbj|AT zh;7O9lw9hX-4LbYj0UAbD(e(O0-B)+qczu$M|Z$o-tSoHHa2Vxbw7LEMBcJ^;hvlk zJxUlPwV^-1Ts1TGVRh7XB@866XC2}67)0Fm5|WZHIj2zdRzZVc;bq@r*!!aiA@_D{ zlV+66K9Om{@(rHP`A}NPxpeH{%c?X2@?|h3u>c91|3@;Mh(b%f_a7bZUPZ*HNEko> z%)^9@oI0q`#=mPrznGeBIQy{Zafd!g>*+>5ZEpcJ zRAT@#YRPU`3@gaYx>DzrG!p3AG1>Wu%?X++P$R)$$YQ@5N|2qVs|DpTuTBIsgG%y} zDXg;n*3?M^77dqQC>bfmk7#goweYb+$Cx^sj=&hx0`D;J)SP5!2zB4?w=1{jD~_Eo z@gyS*QEk6%us}U>PE8A^z*a~_+kYYd5VYa$=a+JmI_EInu;+@d1-tvn19W7j&GBY; zOhI^H*d=>CYp30vyNsbLTZf3RPb8u-aWGd$n?zSb%Az{e)v}F4Ny!XC_F6obU8>t5 z>trCkspAce4b;QBUY+Rdfo}K3lDXE(&eVg@X&(+43{oQ*lJxu!{?}*$No(URRT`>x z9z?HC5F0SSnlz!B!^U(NpL+YllnQRs-wSq|q1j_@T;PWE7hu?mg4_EnRk_S02W=3l zB6pufo%#W0bjJTjEBETasgwIwbit{}igF8vx;659H3jf92%3_x+XUI(J10zC_8tF$K7YFa6mrEV{aad}JK}|tj1=K+jpKPJpupDh zy|hrDfSSXK&mVY2BD zfLi*G>jn1#lpK_A_k3_D_m?rjt|2sbwu^Y3^1cM*uFD;-o}K?O#$BxSdW|qt?RtRp zUU_O?IQel$Tm5)zz5N9!Nl3V}E9BX66H6hY?&Je@b?)wjP2(9e-FrS!D zBrSNO_RZODi}e3;M_`fH$xybHrw@3STb@r+$FT#tDRlL%i^LQvziD3%*sm({@2W%4 zDoDxxs=7D8p7S+$)dL|jFFV)bC!JeBn#s%!<3Cfqv^_i&nA=cL>;~m;cR6og@6iD? zxTGzH=@kcb_m}|4yKl?-$gzmdMJolsCa9M~URbylCxHdJADE>|BB%7b3i;2h%!kRP z+>Sd8Avg#i#+Zs`JaTF#lz8lCxwc&#=`4!WosvtNMgFdzmX8DcJtjPUres<@p-Gzb zNXxT5z2+!AVc72+o|T3MC6GyZag7*bw49Xha!T9uuXcyueK1Mmj`k;Jx*|Wi#sbHlBsr)aSx!UJx zvLNs{ql>Br1M6}iC~-i1<_Q%<;YpjAa?4+mk%jb7&*Stbwk$J+fH1(3a2|9+x2jwo13ZCx${ zw}uP{0}Xz041jTC3X~q$-7Ochf52WUD5za_k!~>pp)b7f1^8Wk=57A&$NK_tT3>7E zrD2~@GV=>@8O%Rs0&IL%F>ZOq+T0BP^8%C~%K=5n9P$4gSI_KMZ{T(ha$mk-AC@%H zr%x&%w{j@iko$6L^3T`ecwhoW?tTA~=(zc1!oXZ*@Ic7G0Xdq{inO`IL}*oRc{xe? zUI@r(2NDc1hr5igr(M1+0SC>gkx)XerJT!HI_7V~&cJR%nj!nKjyr z=nOwpB|H~II?$CPxP0k_W{`8S7nVsN=a$kNac(tNg&z5LD#>L3WoWp%az<~?kL1k4 zOAEMJaz=G#vZ_0pnwqNic7O|f4glJ3#J~YSUPAfwDEB;*nU<86O#u66>h7~es&Izi z;Tzb9Bsx$K5aOgu7bN5yN8(;_PBtHpcKM)8lPlaK; z$nH&gJf+5C1{D1F*1OTwiC-$5HXS&ordxEUrT@PiHw$3EfU4y>^gCg`;JG=Z{Bgqd zNDJ?0V01s!DP+FPqPuakG!}BLx;dGJN~h@Ki`7gBr^*=usHhr?O9?8dD1;3IUVPOF z!?v4gp)+5V?g^^Z8g5xIU8Q%q-D^-Q9u~L_%eRz_{jGmq$<=Jr?sYKOX*%_ZAZQn z-e0;r=s7+4`HPYJ)@8YL0#NNFUyI$kIR6$}66vLdK}tZsvRY7D3lAjVj>ujpe{kJG zi~8*()kBiU5p97p^V$DdBnEm=IfBIXejF6hx9s0v*A^29rc~wcENUrF^6`5>KYKfDi~Ngp3x0YHK6``}%`l!xTKw>KB2ggu&)!U7wgN z(qVRQmU(rT^bJNaw1*cy(i4+(u-|!Y)+^9hizTjQe))hVZs!^w$Cy`MX#b0*^G-;$P4=zd4g0T8q|O@MR9dv1j|QzoK9 z^*O)9Ybw=LYt9EhW(V`ds0glmABb;wN=#ieDrY0Y*Bv+R$puCY8I&1{jRdAw#r15V z%_l^BiX3{XM+?yhRd+%9I$~3!Tr(3TNhKREL@RlH#o)g6oOA&D&Pi)|b^` zFDa;~@ZmC{WKBL9U}JB_^~D&|x_bAyQOVUX4>o?C-s*r{#_qAWG*Ru>$4s9+c;7oV zwMZA}BwdX) zb~SfFK5so(p&V4`PGX@P=ql;3A!QY}J?|eI$9}~xw6hnMj=_9ilU8g>!yZn=w|ILJ zF0h<}V}}h;Fm~ULth{%$^?Rq}s=7jC;K004-%bLLJPMS*;&e;{(!P3_-MQpKIfyL1 zFTP5c&m9K%Z3jXNTkR{pB2(((9El27F3qKxQr4WH!qvC)SwCbb3nkYIbL#oHf=PBB z)VEP2h&QtLq*fx_=8QR`dnHO38bjM;BdM+J#a@C%IgV-&EcDJ0-QY?>#vVylunYqE zga9kMV8D$>rZlT_z0Q6s#WqItXiUu5a{I)d&`$_ukh|P}sd%U{QLNOtnUrtCBEVWO zer)pd3%&P7m@E}`cQ1ypK>q09b`P3$=%GndF&M_;vHo&6hnpAz5s6n-r^;9LQ5JAv z?|6vS?^oCG!-Q-H%oOwQe5D zL?0L4n5c=J&jb|gINk&@dipR!opfX9M8+=HqM46oNz)OE`1&Hj-*^n5!egObubp`s z?0SCMOMr8X6Gey8@jFzvu;7Vwr!)+M8ze|1orLQ8GBolgYt~w@KbFx`pwxng|GN5z z{Gr@K+fx~Ttl?!Q^_UBH&)Qpoa~TccS6U6zu;q|$B;Z6N9|j%%qiztAjSX3q_4KkG zACYst+g_yd;uRiWSx_PPKxLrDifF2G#jG?<{mG%eB;2oTZhpzAQR(dYIT|#e%0Rv9 zwK(TrGw{34BiHNk15&yd+fgUZ2L;fF;)n z_-5IaQeJ-Q>HJNX!C4^yA`?fAe79V>f7yOWigmwAt3AF<_U8;yuPvAGILnI2fx7QZ z9g)>-`L}O-atP|S;gsJK+6z)%yQUu0vPR87{gA_T5sYIVR0&`3VnF#1~|L z@F$0x-(BB?3r>sQms}3q;PiI{H3Tgnl#W%AxBfo!6M~Xs>e?JsU2`RNcRow}Wg8Ld z6#v3IHiPPHmK}Q}q)w4XMMb4M8;QW9>gUg&wZteX#`hsnHY|%xwejAuG|EnfADq$u z7Iq!!8q;!@Y1E;xGnUXZG7cdL@WNX;M~C>*g0s~YLng?#{@-cZGU0A*cCcNdZ>#ab zpiyE7G~5NEZ$u^vkEd_-epNe4-8PeCKRj|eL_`&@Bmzw`nL^g}+o_#Gfs@}zh@a~V zU#{ruZl%T-1`dM$?>^7Oc5kIcD6>u;&#a$$IBvLTYz&O!fZi(*v#YB3ua$A;JVnGG z&1S4`Mvk+)^_Rv4OL$urZYhO}+CL%4+dd%O)p9jBpYQt~9%2dTH50=p;v0AlGPwgy zuEA}!xgIaqC?2^}?WzPFyY4X^qsuOHC_p4f6x{%kYvYtEOH~Eogp5s%@bT&ko^DVr zk?U+Y@PU=d=gdo~|z00a@KEeE-YVC@a(fXE^=s|H-$KAdC^at_lTW=ed;cBy4 zMLuBf#vl=hA{Rc98_wWI_0^w0Cszmo9fA8guBw!h{9ukrS`C!c-^r=f-u||M^}n^+ zkLmdnct*VU-4>CBXtv%>V)csX!PC*t{STB8lHu+7T#zrb+x-RY>0idG*JP31@~PFK zrz2Gr{Luzov0P8*x?n3@Nbk86dej-QugKiLgC@t>K9HmNP9ntnkwemP69YsFTen%a zYW&fTYVsuQSTRS1emuEl#GKJcWmbRxl`!M}veRvC(Ah+qza}EX$QglCFw>?|jvT4l z4?QzU11$P&^hU~eus7Q55yvX(mE;SA{^%d6e;0{YXw{kZC~CKBD)(;p7>|M=V(7HC z2L8M;Bca!_z&n>EWQ)Q;_CT7Rq0Q>{3k~jG5AGgNt7PAF5O5^}=-y$Dxpl3bd9WEz z!_>QF`!mVKNuUOfF_-_bdZBHc6dLh0^z0%Wrt5_b(8c|(#_eK#DoaUO#M^hzj`=ks z3ywoo*v~!%i`^h0c!Q0CQjR05!V z>}AOGS#zoqvK0IkYxeZZv^6jV754UrhQ2TYU5NW*5|O*}%Nq|NXT8tjADlOL2n>JL zV_QH_bZ1swTUSrN9gdCeeDCetofk+)^?FUpM6SqTH4>5t+lM{~J(UGj?YJTJB{^Af zmLUV3_^rW6I@g>&*|8{kasGLQ4He?QSUh&htWmDmks=3`+tx+`#znmTn;nyum|{|J zRX!E+5$amn=*h@wEr2-MmzLxJgZwQ4StpH^Y4n-4$PAajU04bQl8ECeE*y*yToCm2 zgOIxb%WitI%>d)M7s^<_#$wdf9?s`}oWZ0F|0WLNF0=dORL^FQXkmS4sf9Oo{QgzG z`vgnrFLLay4r{R20R+%n_Qmb=@f7~Z2cucNON5JyONo2_n(*W`CgHuc*6BFL+KAvC zXr9}uj*2eDvl!v`aP;7QUh^|5*X*Y}@29PZf!d%d_L7%SJDWx;-7U~Q#;VpZ4vhJjAYD)ba>4VavtIJ}fOrF+pOCpI;|L@AyG_&@mJ9^c6&3zS< z&(?wKTnSSiJC?sV@myj>gN>lXu)26HdGQ)wsY`xV!Fd7ljEEwyrsv z{L`sD@mXEp3v*$m8YAW++K>uPSa04Jd$hv=Nxdq&FS06AVr%*}&s0edL3*rYWM%gk z`tL~aumA|e6QEFHp7d_KoVQlNgO9hE)H7S)M0_Z+_vt|4_Bn8JKUo`jsjq)8YWOsw zUri(U^!bLsmkRN#4eQGJ{I3nowrig;7jy_@eLB_$>FFeie;f#myaED$n@PZ# z43deYYz;jAETuDMDhKx$OR!@rLU7RdcQDHH7ltj^ceWQ>mX=NU`PDX`%yRtxw~r^Y zI;n;VidoJ&LVhv2qHzSDdZN{o3mDiXWQivCq=CCioXJX~Ak)%f(1K-gLbiWF{liFf zPIS`nM~1eP+mBw-a90m^_|Gt*#z%w8?qRk<^Q@t2ZSTqlQ)ME^;L&+FvjOuaB+;uP z_~b^w0sXYWB0WNeDndfw1xG_*;?xsL1}q}RhH6ebYS~c>qRVlXYX=#UtmdP~X@Jig z?MTLHns3e{U2t*JIh&2Kx^q?b5PP(O260KcFBmC?A`@ydwNWBnVfNk+asY`Ticf$K zhXNOEHc88ue9?{|+xJMx{a8)orKvRItcC?HVmKJty43N-wOQMs&iCOqQ;!+~P*j;l z)sMehLv>F^t<=rBmk#6^tU*KwADbIiw)Qxa>;gu97s@%+V%N5`5ET{{YIgeiT)MWC zx;7a9z%}|X9mNcg#p8b`@^{V2-{${=+SUCI;v}2wQ<33z{06kuE0s| zHe+9O z#+M4#25&~Q3 z;ITmipw|B1yQ`CPpx5lo_E$_4E{tsv>OiU&c|wfRP|q1fEw-~+pVR)ni!|IX^Sx23 zE_pp2nWqVd(8b&v!y%40UF0W*BQu@BKYpCCI#=|+L(-kerTdv%Bvhyku!mxfWp%Nr z7XFyD`p8D77g5gbf$nt~|51;9wmZ#6V<`t``8T;m_Y-yN_uuE?{6t?UQoNjog0jCHn-) zHE*znqR*z0qudCR2;%n5q{EkD6?G$@lQ5 ztGoAl=6E^QBQnMCf_uiL>}%Rb5|O!`J`MX@^3#{255p#^^Lbj)n-R!%DWQL3bzR2! zb4u*G&K6?U9**Ax&1}dX9V0j^gyaukO4Mlg{;{rXfoWawCN8qfFD&FL7>%RT5|%i6 zJ4TXhJR}3=pz(!w&N>#pbaj!Hdbqj0*jIV~o}ESRNHvL5P~O8TKvyAAZX)}~Pye)$ zkh@0`5X}nbu4sD5T`#GwbZJQ|N=v)JT?X&W@(t zcU$D>b)#2vIy^ho|2-qbf4y=cC~68J5Qey~YpC2%6ab~wvaW*8fDsMhd+mOWW-GT> z)50mqf3O5?=Z>bQK?XNqw%E3Q<_`J9*x_{i^!=#mk~ax6pN@txh?S}8`@0KBfUf2< z){T8SvRw2TZBK1-e`%+8>LGA_Viv5uz}`{i-a<}(Yrnpn(XrUlW>x25{C!@6OW0NX zJaGX=%;9p;1C*vd z|2*|HO=VQ5@w@QV)|#U~r5sr&az>|GHp4$D+!`26LDV-W>q{#uf3&#lXTD1>7lndS zqY@@3{^U-+q4tFhe;OnPg>0h5rC%9d%~05WoJ?lcW;OnS26Bi`@=AsZ6@Tl!Z~h(} zBF;{67Vfscv#Re&XUJ}OzVH1QZwY=0{*`!P9ms+chC$ks@cVri^4n0_#cE+ztV}@y zQE78$8Cp`MM~c&S{^MRHGyQ0L@&(Isq1vHIzTZUnp#0m_-rH;=gYeG#g5=}0Q zWZCFAfSoZ~7~(&1kespFtF@|7>eEu^n5WGc85x-~-FkS4Qm2NEc9AI?dl&L{f4V~4 z!-Mxjm7X9aZ0#c)XHrtqY`vxGrCQ=aoEa&N6g_`&KH93FQEn2>-I7FRQ7yFVn-m3^ zG7CsaiHM{mJlNOJBu0(=wl;5xqMM6`3NWcW7(ktD80W4^+>dkpZ%HCmKhuYUeeH~Y zu~4L#HGgQ0wm>+Iw)fQ-i2MwGcZB#XI!Iax2at3_&xH9F)iGSGW0H!Y$3iZ;!Ks7} zpB;AZ3kL&T+6h0KuXZ^}o>ReJMxY_)5zkbHM zv#$pGV9A8ZV%t}mDGT{)q(|En2hR{;cIh}HP`+83uaJ2LlEsPssMir`sw=m4JL!~r zs@Gxs9(}Fc3WYz{H?g){WQ&zr2iF$7G?sJbodxC>{<|@<_NH^L{XJMbK z$dUY`MbR@lt@KxAPj>^et4dfPnf4M4%gy_>Oh+=+klLJ&L=~F1_OE8x|p=jvN6ZB2!Dp7SIx3Ln#&`Gbwno!{+s0Gb2Rq%Pyk^Fv zNOeszrmz)scmDZ{{OS`@ie2Z4_x*=9)l?2qR^cms$2k>!`ZyHG9dp${>y{?7^vlIJ% zbe?RGP6XC5?CBym6fyr z05Ns0v^cuBd;)L8+V!*EGceev%Ovo6VIC1|AX%z7wX;Dr5_d9#Tz-~Q>>w6+G@Rku zbG_!(PXB;^b9eVEdFLi@`ao5dP+D5LF~)|Tj<)Iz((-~Uwax^7B!*~TBblFeI;RWE zQq*5$FTqdzz1k^I?lb?i`0U3=37?-6T-VmV`exiC$}G!6liz+^>48pOWN?;XO8h!D?B*Ip@waAVz_KDaLUUhfLV<)~%@TTdtC!Kvv(-DV584 zZlbTcPeeNr=SxlugP;3EXH9cg6ft(hn@xooCpR<^+}XQJd~HhnE){LbfTopHPVt>BN7_a(v=#&-KwK2KZ7R_~d2J zF@99nyMhfpe_Nn_OSUn=vV#TGrDT#Veez1zk*pc}Ij2QGOwt>k7$faRE*FtI)t+*T z=#N2C^cP!7#l5n9jBjWRKPPk;;L``(Wu>>50t$6+oLQ|YB9JA!#XYa~WOQ8sYd?Qt zzIjmB)JP$FI4P`i))g%mYr|WSMt-d$VE_IT=S~-%0=yuV&aGXeY&Rz@;ZE#GCfh0F z8x}PKyL6nvwspn}`w3Lml3LH&g1eD$=fRKS&TJAD%1c&)6bH8lmiFHKpJ-)oX&B9vqpHOlzV8Vp=dg2O%@p5)C zhpp)uO1~=k{E38RG8jS$eI>jbN#Y|(*}AANspDJjGELk$YslBx|LU;xL|LG*uU8I% zK!}wSojhfzJ+!hgZX$C%kW)oQ( z6k0HhxQ;W($j;Gq z+a_oz0ag2wE=`aps7SlMf_}@&0U!e?np5RrmQZ|+?GwuP-qvOlW zZ^?8MAMHu#K_zYzSuc;WtL}4iR%;W7^nDqRf@SBF0?diQqy;qnA$QHo{tL@m*oviD zp4IO#iz3Rr6g2$Z`?Lv1&@?MPm%GRyloH+uT$zZ(%z(EvQNG=~ zlW%3HczS`n{Pbr-v&0_P6gL{&Zup~3DER*lNrL}M-+o7<7MmxMV~2D7VagM!K!m`f zL^N!%!Nv9(PzN*3UBky)`VXXCjDES*l7qZAZQpOsc`*ULOh_@j)CnMuNZ3NUV&_ms zNN=bE+pSz7)BQ66w@y2m+m*`iT?bKp;GA$?Tcce{Hiu z=_5a_(R0C{_A^=>uin%=zokE> z9axMoF7WAJ5x#ne*EX-u>%J+iYmeu(7L z1CaooUm{87zHrjt+nJwl0ysA~pdD>-x_7njREz-wWBGsULEw)llj`TjY<$c}LAzP1 z11J&{q_SstkKedR~lYFflTg-WuOD`O4?` zwqLF&UmfR1Ha2RwPP#sdL+t?15T$sb_&>nOR32MFTW%L{C?myoGKnC?hHqHL+83}I zWYgYjYE5-Sa-!ZhR+PBnP!?#IRmX0J$aOaQmmF5ymgun{0jJh^{gZ>vtJziam+mQ5 zwn1;ed#_IT*HXnA;zQWFfTf;0#*zPR4Dmp#RGd*r-7JOHT^^;&-rMXqvcnDX>AqEI z7DKMde#Ssok||W)oIM^Mji2`}DsX-cW7Q&rd)ZJNP3{~4TFPh3?rPS@UnyqNNv3h; zNo$4LgZB6hSJFKV*-unQW5 zWCs|M%x#P$Dv2r@iiuu=5dWZSfwqt2XUP5*_)xb^hiJ3QEUK7^S53_PYz8u#ee|6w z#OQd6DGAR_<7oelMNcRk&ZXicWJ)^{E?s~J8P;f4EBzWjT9y~tz+huUG8a3N2$u4j zQuG?G_I$w2wA!D+<+xq>=xHWE=O%B@IQG{4$(FRPg?BUdXOcFNNy zHT&PkphvEHlvID7D6=}|F<|?Z*a-;IP(#9QyjVerM9Ryvx6uKj0#3X$L^-GtpeRo_ zK)$(qBoNoiYC`$m)>UED{ms(x8-5$62Pg7h(#1QMi)gRZWG|a?Xr}ovcvivfrA)|- z=(D-hB-y$H~zLy6(vmrD;~T@LNXk}MjZ2eUj5;e zFsJJY0{(kD#K(WhWLX?=;kgMdR~(aa8e{$hMTFYp%V!YP2(=BMDW$fF`bN+P+a7NQ zr&z;b(=9J~Nn;Rr6`XSF`U3@#iyHpAHa8yRzMl%a#a!a#_LhgfCBIJfVhi|GZF_Vzvtkk{lj)r<6 zm7pB3Jb|4F>;;cT!%8#sN&LL%lB5pp%{5XYD*J~U8clVP4P&+XM017nPq0A2fy)Cs zm~pI`2CW4v8-V+RoP|!id2hY?eTPr%?N<<|hnfd77(FCMM3*D9yeQa<6W#o>p&lRc z7n|Qia9&8@syiGYaabf7$S0x$p(Y_qvTmU!G#s1DS4b~@^en5V4MtcJoS#)7tR}Jzo``x^N<&}-DkuoHh`m7s^y5x zjv@1MEA)9t2S)R>gkvP}o z8UnfqZtd}b*V!@if>F4M1ga50YaSRR)l>d6(e?kDYz*cvBk5yb0kv ztJB!y0i&g}xDdaE8yoTedZknv2Jf?M;I)^q5Cgp;@vCztFLmmDqud8u9A2|6?Lv94 zK&OW`bvtGpaUQLWT3;p1cr~WOb+71)zY{5V8OE)shsZb32>jr`c1QA^8`!QZ7j>sG z4-Lj%WDk^`x}S;3zW&JYshCejB|B(G`Y5+Dx1Qpvrf2oMA1>!#C&E)!pRMl%SKAn2 zEh&!VYc|WmO_I|;l}2)cu)Z|sdD;SI-H_q{Wod`a7gr8~O{U%*^n4wD!KLwcllDr& z=`WUI&}Z#0(ERMN*tNhso69luTPxFc0|O(YvW4#{YTV;VjQZp)X+=pbMFiKTc~=n z-;B5BSPeUlwJj86Z%m$HG&HbbL|-yPqDDE3JO<8xjBn>1x>X9jn$+{hBJiS+={}wo zRI%fFA z_cHD^y?fZX+Nw=Q6=IpSu0Y-pofgWk9&@Q#rF3m%idCr<{~HNSJ-<9Hp;SRENC3n# z^s|dkyFw2oibZvcp+a~khL0sV-0tkUS9!dd=1FsMsBCQQRsAb?v9ra;hVe2JGSlvO zo9XM#IEMh*0^N+O73b}~m0s;^s@d-~DoWJ`;3R06B*EJIrJG4>Uwh0PaRTaQyerP6 zBcT%HDEB8{>?4WE@W0)l$?vb?_&Lg^2D8Q!5hID?m7`Vyb#{>D1W@EUrY{cR2hVRQ z*5p=&QRjt%tA>5V6cpE79$oc#@NaIE>lnu#|MyTio21gSJ@YdREczCb8|2ACiaMMJ zGo+BrZ3TRK!AkE>gG^D1dP+ah!!g#Ph%OdgiFA z@%8Gdi)LEy0Otq2nODz7k$vMQgg$c*({~wM)Gm>;KuWF%!|Gq9^^9AI{)aUScL^rb3+OZYuVHn zb$NxLvA5rldutJ%Hzog;ml$iQr6ehQckULgoorl=wP8H{#OM3t7_A|;JQ*7&$Edj~ zf4j8RuXjc&#X^QN{%temrescP1Bw8Y|In<36N0+Ev`TDs+zB>#k%9^aoN|zMYo+H2 z%-YjdQPO5y{@rRvYz>Wk51_Qb$n>x(dJ8<_8ZFoUD*FKU)1>zYXoz zw)#oEXiZ~C7GyIO8`Y^jchi~}Tf~mg!3wZ(N~P&=;<;C(VpGzN5}?*#y1{A8;HjN) ztkgo-`tj<#w}6+2Wv{vW^c5!vF}_Am2&6f;D`)_fn~46#nR9*>(ndM*@NC!(ai3(# zDOkbka8j};_07MP^|WX5k5lL|= zHRJ=;P>JlA+n@U|7;rRcIu$r{#PZR1Q4nMeF70*@2#Elti%A}U;^NcVnfx$_#QZA{ zrqts;ZRxraD22oE?1*n`(VdT^nwVR|Cs4KPylepC{Z2g4WDH^CVq%`(z)x8f`wUxDW8jBiA3RiGQ) zQ=N|~(LGkJ-(kJk#y9$o+kZvxWl&*ma_P20U9FzL)wf!s?CfCH#~Oc=&3MR%(&fOAI%UMgZgXYbsD zJ=Jcw0>8ga*2y$-q2)-nI@+;>Tp`*^yNf$KBo(60xl4F^Y4-!YwuXc$((n_Vl5&JJ zy1d(*d0id$c6fLSVue^o$UQUE#)nut^rkVthBow=^orCTQCrw(UwAPI)9&uj5gT72 z@Z~sl7mJ5M(kU_=V7neJyIIUM3-IF|Aws;esilf_h6UhPlsr=8M1Qt7pRoX)`!-s$ zD!QkxM2Fp>pKtP#aSBL$b3s7x)_hmHfL!t0W-;*svJqXQ=|Z-B_hk0~Km*w7G@!M( z8_mC=wt%MDp(7%pS?!Ym`HWyBz-fc^Nvw)cB`Vk5lGeUDg9Q#cr(RnZ?u*qF!M+4Ini#8ndZsx! zKjYX=2l8nH%VAfUcNp9Myx+N%33^&RiOrv7w+J+qZh{q^9aVtU9x7$t9j_`G>|By}Z2ehuHX`cR?FKh9Pt;r{j`Y=EgquW_g2zm*@s2f{PM zbm?MA9||F8m=Y?RD6Hu%(qqa>V z9rPVHk$rr5%Rfx)0u9la2tEJno86fR_Nu?#5ipYG(qXZe-byFB4R9Y*&HD=Oe4vAm zAE!+0;9{wKb=$bI3lPo4MJ3?G?wd?A2Kd01Di_Lypmd*uClL6MLd2AyN!U-7g3qNm zu!|Cwk(5Z1NKSba;&XP286Uf=ICPvJ59d{wfzS^QxsRu3OU5hM5*u=Vo(IAYqM9&u zBo~6?eL$W{P}Y+Ai8_MoD9!z*tL<-#;{5+WbVB%EM%Hg1&RQHyV-O*KBlC#{ttQRN zOhDF@MNkR02#Yb4|JNsmcrq*GB(IEkzFbrwm-knmMEnVn=bZ(bX9SkF^7mdwR_W7O z{$ly333y$he0Oqz>Dguwrnwcp7=LT*+w9k;Ur!vy+0rjM_5as@_b=y{A&ROf literal 58510 zcmeFYWmjC`vNhVcySqEV-5r9v6Wm>b1b2rJf;$QB?ry=|9TGHHaDNxcK4+g_a6jEK zR@2a{mpwIWR@JN`Qdv3t=`0?SPR zkz$xfNPw*PLFJR0QIa5S77(U|Tt6>p=^cpWy_SUxsJaQ%J%Nf)3xY)iv8Y6Z(t#ko zK}J6)C_F(SX&_9gKUxA843((+^uS7`)e5vw@=6Bk!M<~b(b8ffrk!|?!+^Gpc7bB8jJ%^*-3@@}hl>`K0XaPkXWh{@Vsy!2BO!s`>! zEP4NXlNN1y%v}|9=QxSyN9+t5DrrG2P}p$*-8YMNt8B494t;+=p9*)3?zCqCFyVk zrV6=S0;deCYLq&uh78dkK^Jh|aDA!P1pXf&wxFl5c4^kHfwd}vbBGP%EydjUAyWAW zQ)X_g>G9aP8B;Fx_<}K9dHYjkRwyg+LgGU#-3PcZ?EQ8uOoM%5H9U-PiKe49B#>ApB+Va|pOESfzgp?d;D{$O!5FskPG~|iJ za`n`$X!rfNCTy(X+A@q33+V9}%&6WG;{Du|=#k=VG%cUO-`9LspFy9InsHF2IAkoz z;E=(mNE}`^}*9lKs(x&oU8l{(h&nL#sMsBa8P7^%uu4 zX!BGyQH^ius_Vsh>S&ztx?&Z1jjB~D;l&snAJciqgR$Ss6;$LW&Ei|(SlwDz9k{ik zttSyHrc7zgj2=oKq#Qt8c_1Q%VFeFGSkmHU;KJZq;(6d!rOFrL%|_!5sk3mi9;fc7 zp`r6`x5jX{eUKEv6tB*ck<1pUEbL<- zXFqk#__B{XeOu}?QCqZNX-OWhIJ+#nR-NkQR|{d7-BjnhOgBZiecGawOTVZM%rm+j zI)XwD`4(1lecRIHlw|EPnKG3!>EjNr%9En3!VbwcoyS0A(IHtHeHv-Y_z9@2eYIt^ z^&q@3l+X8~THVKa|hoaNe?9LAX+47D>8(tmz4}`wV&+5-fJGBy@B zHk-e%{i$21bK2PM5UR_oQ=qM(YfvXukySyp&{ok_gjUp|n5bBmy!ly646WTewCU=99~ z@Yz|cluRM9(elW0&%%AQ+&r}QWxyf2iJ3SFX4tmwb2*gGJNQPi!UJ_(+C_SpT1#^+ zi>~p=5#HpoY=-fZvAU7f&)k`3Ij<+^z3AIt8VkbYwB8YE?{$>h@YV`Ad#%FnVnH#4 zX+oC^G)Fbk+s`YNooJ<0`gKr$Qm_sD&@&R$(*S0BjGzJkE7bRRZSllFNt;<`v%&Zw zEQ>%0D>AAQa}_5A%YTV>&GQ#QxZ_Ay+S=FplCu65vq_5?i^IK*ciDQ#$)zcKDaZ~; z%PaLro0|0}*Ef=@%qiovt8KxJ;w|601e)8;i-sr0`GwWLt6!-qc)d15_n75cWe|-N~cPm^OS$cSv{Ah1bp=j@XG6XRL@eD(O z+_=~>H%~MpsID5nz;G;$JVes@l6B_s4v7m%BQ|qzhr&t1>*wJu+~zGY65on@jCc7q z%q)pJktGqcjad4hbg2xr^hZ4ty;h|$q3MOAjZaU~t0X9y90EFCvX|<^)+>iWvx$~} zCS$UavV8rR?$?Y~^BcYQO(!;OP#n)%QQfv@BwwTV`P=y?^#3%w{i$93g`w4~m0rbX zXn*8(B=C|rt2ES>*_K|}qHo)B`l+MA+v4_+Ae(z){i?(30{eAgKATr?z2owe2|7bY9Az zl*BH3pMvM3?qj^F)xq9D;?7}DcGeG9nvW+v9%~*%XWuqalz#e<`qREz-Pc^JO%**R z;w2`&LPDfoKAEz=TLtn>Qd1dK1rX>H6$lg%3IeIkcFp_=o~Qlx!gr2TL-qcVcMTrFhO zii{1IM-U{-Z9E<#jQD3r5f>IvwxduKm;-3Teq*0|^3oemLqil5^1mAoL~?onDQwXH zY`wgnwZ;F>7q&@d%E|t_JID!@a^e5%7Uh9OxBWl6NeLk%Iseb;QIUiC@&EVaz%MYO zCP@FiI%-HTX-(MwTpQTkEBgOm{=duj|M}vd&q4mZIwLn?Up>eQ8WkKBG-dYS&K!Un zQ2bJ*rZLacfdAKg8f_k`8cDi=Z?=ml*fe92hZSK6zy`g5`}=ZpOcw_0e)+qrbX7Xx zAE5fbI56QB;)(DF*vp;oe*&v7DP4L0PVNm%67#={{sS|UVJ>V$4CwRb>*LtiSlCdp zw+%WN_&In>o&dXZ!2|pRw$`>b*XKhfP(^9!U~v-M8^nt51hM`DKE_YtJuF}#G&ApM zqt!-xD_dJ}IXSrve+K;6{EiNyHusZSKU-Ll=+aU&8|T09r2ph7`5$CPugL#IwZSJK zn4X?aND@_}tm4Bt&Y z_j{=Z;^gE67BbnQ`JxVZQpbE1(rY6y|7esKshNkQTTY<|=t$zS&=wdlj^ znZfPE?!zc84J*N+chQ$LMz_iK`SZTMq2ZlxKYA=VCcF+*27!h~tSU1qWR3i>9;o{l zdS|G2$?rtcQk=hDoNu$F*}GU6(8>mp=AIgoUXMTs7`ne!3cQv#(B zFj=zsA>FU&L}hW*c_bwT5mZPOEYXIV?W@OUD~As+&z4$RDfiZ2KjrNVs>60qj9n7( zyXQ7EeDwN8ZBX5EF-Q=*jhG7|f^ZEhiaIJndzo8PgVtl@q+`fP9G{`ocU-tf6LA#w z=J^pvxy$(HOf=J0bA-w$35E)iGud8Kb8{haame1#F0Slv&**cRu{q{r&ELmD)(WA3 z+VAp#e|2Rg`JF3^I1eFLG^dJ(Y#qShWXRwg4Fgr42WUIy2kYW zwQ|L*Fbb=U<{HV^d!`<`I!HBlwji5 znv+!VJp;_T6NYNai(Nx4)gi)YE0Oe0B&=8xSTJxg*K$^OeePlXR8m;b-?*Ue!)Uu7 z#*Q6rY-RPsv$E9YYioeE;?9NVf|?Cl5b4Ot%KN=uNYS7F&P%`Sl$^WQ8J(V^?O;-a z3&jg5%DCE)I1&yQM2q$Jd{beGT$vJ`{d)(;UyO&}B9S(x8IwF}h}z${kzZRgub-;g z!T|V%Y1$CrWDDiVUqnpfNF%5Qo3+3EeTI;~ID{Vn+v~;T(V`B!FOa0I$?_D4J3V#4 zcLgpc=ll}H$19+f6C;OL=x`bzp#!!KP~HOr z>%=aw|T5;BF|4x2Nq?28hI+9p)CsoXuZ>Y#W^vU zO%*OE73D8a(fjo21f_eo@@qpCm#)AWdFR1?w_wO2W3_Cu4el+;dv7tVc!zuvOHCE+&y=G%ds1hZA zmn)y`&9YpX^N}Br=fM?tz;fZZurD_yF_6PA4!*_fhNge^W5W_f?N>F@XCxpXKm*A3 zdZ5@Rl{6yspJ{q&Lt9uPIp?F9QbUZ;?Xo?0;5fLs^@b#SB*@W1#K;6L`Y`}X1}L!K z1$9J#^vxSGLI1o#x-N@A5peueNCJwjgVWQ&SniF8g#}I9w6!4M==#RSoel3Z$v2kN z>mbbo*6J6T)&4)^YQtu~WmNe)EbeK58J9v!OI5;*#irt>H`4AODh=AIZ5Nq1xVZ8Q z3ZTZv$ARFcW6F*mNs1j!21KX2y4g>R9$`hGkn0%+fIekg-K9$I;ex8g-lCHPlq1jK zDXB3{8bt?Q*kK6u3hu^xfO-}vnl1|v(q%C6u&H59@T_YuSKhyUA3@Yy8F1$$7{K~e zk{a_7rKMz`+g`VJkHFv1PF28P=r({$DPEO}fd0CbYwp+^X^*Tpaaj^4wy>z!T!AR_ zNg@emUO6FPyWMaIbno@`NrcfrX!IWBu)R_-wlr_Z+Q$5gtX!o1{OY@Ci4p`@hSu|$ zl2?FNi;rw+l29-p6HxwP`ygx7q+*= z*Q!s4V|{%c%ftBXpo5hb{m(7uL4ke*0T8rV%Hq$p%`djot5sjc6b2&fzv=*~wMyU{ z#RGRp3cGK>U*h9AK`IP-mtm0sv4MOo=lqNVU7h&gc=!**;M|!%wf^SgUZQ)w1t?eI zw6SpZ12B(V*M{u%wMEoNT?L^dxxmTjVg8*oqE`YOu%&*l5bQTM>Uu1a->Xyp6;lET z8H87j^cEjfEZ%TruFsgk9{-oPgGm4MHoGTpQA;}}o#_Ql)mo<$-YkTvYs5~^X;;63 z10OW;5+|1x|mG*+Zb^7Tmp*~hy+N2mEU{o36nUs+9s zIO?Chm%{2Gp(DlL-~}l<)lipfYdIeYj@So@8+HgfXUx>NF{2inHQ5kC{B~9Jah!jg zzNDk%Z>aC(`rn6Fkp<`&9g5IOOb@H)1^vblG{IX@X7dpL*9i2%s_6*Q#ekcTMzA3W ziKh(}u;5fus{|OQa`8-tG|~q>Q)M!+@)#DEGz-%GHTTz7$@>48yXFmsZ|2s4cipz3 zC2?E* z+}2lmH5Df<;QRxK=>GsDqMg)ry8z`61T6~2N-5k;^g)+9h`;zX6YSqJ2>)AD)O(nM zyfIV$u~4wwwzjtXU|Aq1QWm1F*TDFVGvz=2knJFi=N}Wbn)u}{6<2w|(faG{z%6;1 z=<~~MX{kAP;EPAOPX6eH$A7uVK0 zbYQZCjiA~9h}6j&!#AO$@nsJPIF!p1!0y9^@`kM8AW-B#Mb=fUJILRoc{30y8xE6S z7m0{h#+0T;wXifv7?{8ZV-g0{NM9gxR~dsuIRrCd56FGl6tht$X?hX_9fTI1O0mBJ zNLIDQ=nwijqo2A*3&2(-?os>AMI=@^#raQ4YQV##>fs0dP<5AQxsJO21oY!SQcHhV zhtSfPZ=}*r)yp*>VO;#_F!yTD-f@b`kyMW%V#&Kmc7%LyPxa}(xwQ11hR`ctE?jhh z+3;iDTdI`UUyS676F}!J5$D1m zPy~dG8Jsj1OoyFfd8{`5y&MeHk2$orNHoc zwC?~C!R?-aKkbUbT9qlU3ExBSbpPHBw+{af!fXPedXu7OcgOiTnc#d%`m`^-L2GLv zBjY^L(r=>u;)vMV<$w?}oAV`706}!>b+m4a7n^Z3z&1FRXZF2C6PVeOfY<@&^(;D2cu&|i@Xt>6! zadB979q-Wu)hq_nDy>a7DRqsL@1trT7DEhc2Dy8cLVu+MZzBh5KsYo3-8BSH%Q5k25JXwZ>33HsD*@*j={+O1sG! zj@_HW&%TsQSs}Des9x9tlR^8qu&pwEHHf%jV5Sc-T9sG0@n94(%>$BJ9h7->0f}PFD zi&yf#bm4E)zi{md9QkL zH9!Enj}2#`2ZDo5kgspoeB21m%81Q}1npg?8jE8MPMux}sbbH|bx}M0{UCA#-hmh& z-UV(mzm*QiHb7b2y9H|v%ecN2qBKQmpO zKl$)7DYT{f!_HhG4A{%PM4B>fh!IP~-rioOsL_T>Coyj7>n~XR=d=Kku0#UOdGq74 z+AhE8J>LB#Ci6#$D4#BS1UL?Wl7TFC&}VFH*ks>SN;y`7JVNSQgao;c&L_TAFuS4_ zt4CbqBU{&FAMkP2csNs3Y4V5xlR-Yg@CZ|4f|FJ!48sso^9Xq}CV~Cb1BqXjrt06d zzioz`iTolQHqdyMHc_xpES z&bwCY4ArP_fZ!j87~&BZzePdviijy0xbuZ<6x~x4G+GP76|eR(d$hdRQR43|2uI8d zGtYgM-$pv{h;Dw}<9vJZ`0SvQ!%P_auvsHFXODe9=5Iz8O2$LzGgb4t{&g|iSA048 z84~+TZc^ff#kOsBqg`>*_%E~ZhtQ^Uu3WOZROdw`hJAb6CPvIBe88`fvztTe;XSdy zzWg9dligi9Od~bp3v_(v@%cdA)!4TWXOJ*k6i~XXtgS_9F~#KMM!$3}ijW2<(|%3b zA&wtHiel0NvBBhfdKbnc1Stj{Eq51YN)U2T`_{ zH6MM@{XrfdIEn%IBbdBG(YUB&2$0!@JDldHxs%{$rE7r>4m)CohcSH|z`k^gVmH{~ z2j-yJVB-_iex8pHq|fvlq;@a}w%m{}l3$UVs zf&tVppppfY!a$&g-iOCr%}uJ3R4&jy6ItCpF@hT#F}^fM+=)3q_@1kTW8K@rb?Vo( znTjI!E_rX>X?)zu(NWsln;#G~0WsjMFielWiQqU%-K~6#BjT6kq8gx@Mx!)D^dE2vuhj5?Y*c-h(%3eV+mTq@V$CS zDzAren>hkCKEeYL0RfZJNjt{t8!y)e(SpLNfu7a`-Ha9Z6z$*)u}}Rkg!B9M^G||K zZ4G1iWNP3^;yj+^*B%%ByV5;zITGo{-K?R}O0KMYtg&T_57jl(@g5sp5fIYMsmxnS z?1&hu8V&Qtres{Rg{EQLN@0M(4hV4jLYP2z0{PT)GK7?Y!L-#|I^(xAIjXf`_*xUr z+LEF`{>KSoNs`}9Mq)rCQP{HDM5QiD1Q>5>%LDJy#LWbM_B|FxB&E7WD5cYRt7L=# z!uvVMhS5H_%r+K>r}apcrvxI~=)|$m>v1sxMvq_V>FHAo3tiV&jjYj8w|vdq%P-xi_iY z4#)UwcT%I*&BtLHyrJp9%JI0^pmQEO^f$r?!{tmObZx?PO(MkW1u_DH`NogZVe=oK zb}Kwc(_|?sN|iN4G*SvxnBx_8vP^?LU*}N8xIY9=u6J$N5Vi#yrz-k_xn^o(kjQOy zZ1A+k8}`54GH=ZX@1yZ^gF}CUPX5tD<*aOm%#;vJd8EUcsi~M8ACOWN=p2eZA+S{2 z>Aq2(o>xQnU6mdeT3hXGaPXtwn%g-1nv1<)*iaEFzi?C?&LAtFQ0$=@{Fm{evmdvy?FBw%TYzCWg+F1C;4VVDt{g|oD zzPRN1bfGg0v5KHwG_3D=vlO9TqpY^j)9L5Rra2%MOR*1h63knd$A^@$L2GRBc?l&! zP;qL{b#*qlVQ?&nvv6vWPGQ#-F~_{}{~&bwU1@yJxOGC@6DF$B6_>&{$py&mSVDr{ zV}Q&O0K(ft8*r&%(<3ZTmu#OZPsgwfLpzU3*Qv8+lp_z%;9l4jUtF{`K^{FwQ*3v* zVeF$Lq!X8qVSaQTYD`C%+}HZ%c!QkE?G1z`&xgYzAPm{Hc(1tJLxKBV5&59^=-V#C z=gsTqUV`VIYv-TVofzOLbwWtbb>B}2fCo+P4@4!|IvtvkXeWb}F>N#zIvOD|Jh5p* zf2KqxSa|5JK`bnsJs%-=J-?#@((193O}G=>kuUkBHu}Q!ODG`VJJZ}O@R5rnnSU#q zb_;68bKOIXq4CV`Nz59VIv8&bCe);IGnSbq!d74dy~GHS9J(>FeBueD5e*nQ=Q!hra#h+g}OpTxfaZyt+ilmJ1tFnSnq4lrf0k* z4FKsm`dwy7sfjbxAYxnF=qxNUw?brj7n>1l2MwC{xX3Eb)e^gPyIA_X(Vn9tGeMvQ z;4`sOXn@4pkD;W!Z<33s7|3W&x(FVvQVv8uZ|^qKIzj|8Ttxh1H$dl3krEtdk*_N) zwZ9x+wqZJPYlb`dJia5oe4q6*>Womr5pGaJGMe+NPF;KAFe+CF_KP**AvGVK`sM-B;2sT$^AYWp&QW4lhP|}Lj#yRRbehI)143T z9OP4RS%v6x6yID`v7~_kh!c#YotWE9C#X0FR6s-|*cDLia?J-2)m**iTtljAG1)U_ z!I<+_`Ncy_He3yO*JDDN3dxTNdSl$eKC-@EZRMHLDE`KB$Wb<#f?0Md&WDRNAb;4h zkj+^lk8|Zt2}>I?4V`RhueAZv7N1NiWIS6aHIe$*YK?YvJcPlp+)zFVu__ zjKh>qy)_ht)nHXT3CqcO{^)z|Dfro3huMEbsTeF^-gbdB?Vb_uxN zh<{N6omb$d#?4PE-0UQpwyOWFdW2(luxHV4!fH!57)K9_ZjWU1L%;bvR|(}u z4wvDv$LX3*!X-=Hg5gU$bum?k}zyJ8H*a?t0tl56KqJPEH`T3bG#tR3Vhf#-9 zXRZCCVCT$3+h;1cFSG%>UM%Iwf=1Y2T#hRdRsO#`(2h^UNw-f%S7APn$uQsUZv*RqgOY7Zb`a=74M)DHw|sHlpuIh5b4x>HByRrztcXKF^BqRtaRDCnZzM&QPBmx{tJ7Y=~ZImp5^g; z1CO%>rQFppYyn}sgbBD|MZL(%CoRLxHC(Wr-FV>-(}6%$E<%Yuj0Ic&MO*DcG?ro@ zT_4Pg%DiD*vha#(NZ*bkFAVO;w$7Qz&c;TJ1OH=rUcJI`y_cP`b&*gK;njy$5Tu62 z31b4PN?cE8;>~3T$blp2`^vgMnmP~V!cu*LUP@6*by_`qu@^jHa$+tbmw`cimG7>t zGA52b6J8K)fax-?oQ;nV^J(iYHX+{?G4Lr<3gUZUumGGVPH*ZPHI5#a0fNRkmRoI7 zlG^h(T)dp1e3&JHp`##Z_Nw?%9FjFqyc~;v$sCOYUi3(J0b@KFhkfzD#jN-zh7zLy z$;ZdLburHICS=pG@dOQa4zAEmiOC+SKcA__+jYjiW|;SP3HSt7+9Ga^ZQ)WEeOY?V zcD57XAwJEL@iiSu@R<(m;v=>ri!FP#)WR#TpCjeJN7A;}%zlnS)}RYWka)aFO6W9_3zj0>i1 zCR>RIm=#x!5F$Dtq0zC!bK!7(E4?RK_bv6eYgDf5BsP5bgY3EH;JF45tV=#^(0+^8 zK=OD7m&r-G29LiVC?@+?PjyU!f|}L)iS6ufGVw@O8~l z>7g5ulrDEa*L4=ZOQp)1%LX`$*23F;`O$NN&XS$F5YkG;bF}tFXI8%iLt*nf0g?T9 z>3jJCr>!NZtNxvHZ;3t(8FU_lrwp{0KX@B{O%Z_hc+=$%1}7*Uwad!M8QE29ttR9; z(736OYmrwt^Mn!w3(0rY-CKgXrtd%D> zGeG;0_7htuTRZ|M4p%?^?kjeoBWW9=&1{&)-4jk%ghrX>Aq_9xXLC5QY;$>!buWew ztB+i&!;~}DAL$g=m>i&iTJ~-UQy>cTehj&?1?owXt=!&ew{@^Q_VQOyUSFWvuQy2NIJT0#PVVdzW}yg9BxsV& zd)PwF*}Sm89B&ZuC%w>&Gq_9}p(BA9K2JTD3K0lY`E}DJBr7HOSYFTMuG^ve+b%GE zHQHl7Gvi~ScKZ{hNI%9!Tg;iO)oe{cPNE5&iNVs?KK2=PBW_=BxrjhB{GszCQaVQ@L7b^AME)etX z>~Fok&ju_G*qSqt!@QpxGg=0h)dX!NdT{H8M&F>vA@!ATrYwFYN07nylZ^Hz|?T*wb$2u_3m*5nu~0c_>Q;U(OKnwN#JwL@$2!n}dA8U8Zo#aFFM- zld#lK(bjE*uF*SdX`NAW%e#3esITQ_&8H&)_lJ$>#O6e!0@ayYP#|rM{oc^KpI-<+ zZhU@+`M}^-iAQkxE~8S^pbra>e2G&)I#YvjT%i zMWpP8HDDE%r=R)XIyybwy?@%VoHQ}pg7nM|?XE$+OY;{P5}6W=Zzdm_xvaV!A47xk zO*E~W6-`0Y8xI{Pm;hK_nZ<~l5cwO>($Dmo3moD3Sg}k9o2bohyAIbYs_+u!mpBjs{V#=QT-@0_hYi>GI9uNEqFA zBr<)-$^=7)2;RT2Ba06qSXKFDggi5utyV;xi>$P`5ayq`+$ig?60< z*2|V{S2z~K(bfg+d2>?Q9~zykicgOI(n8t(%WT+vZMIz z&yozaHk8#koY-)1ly{@>b%r12sp*g-0Hyi7C37x(jY+L?Y%{YX}?Ru1R} zdK!;}}b#J*Q5KvXG8c3;VK=7J>6l;%>P+aOTx^rLZ)vR49VQ*&%1 zus^*ZcqBL3i=S&esk932iA3&7H?|jLs$!_|B&*E)aPff zJFg<9ySf~6i}{s}(41hxe=C*8G#~j;*X>I$=hG+9mtoAALpRY-<-R@&Tq1(WdmT{4 z*paZnh5t@x{4+B6M+Qz|w8(IiuVt`r)rG zc;CxYeVoM4wTj>FSBJl_X4b46;lrcT7QHN=_nCfJ;O;W^al9;yy)!vJ>(juQlN4TH zDcoZZGDH(Y0=2tWZ+bG)9W?T?G;zv|K}G>3x~3tl7!5FQ_(-l#_f|D_oD1lmb~6*l zn_5}Ht~NQ+)VSp-T00B^5+%I_(*&!qo)Ozff!)v(pklKw9MUtN*fk!0WTUP>LH2Pp zCpcK8wZb!30)8L#E{FbGi)bSvjFtjQ42^{IxZO_SqlD}?XRih>C}| zS6jcl$P*7E(KzOYX`!ibe~g3p;b&3J_K}ff6z7iw4O5G^wL72@Mvy9qLbBw#ww-ZR!9brWD$6NT@YRgMs0&I}a3 zB7YoDqF{BZ9j1l`s{ls~h4e-^=^q~7JpsiW-jr+hv_BZ2=F6Lmi?vJUygXwleJ0+j zNf$tSH>GEd)XIKD3z_>&J(W755io$j8Ib(_O>1e7G7@qw8)yg9bpSf*X^*B>b|XAC zE2y^Gq!rjSGn9p@2fT^B9Vouw#7zWWXw@^d*$p*gKk9e9ZangMA3zoB@{Q+vdcErG z*6a+pn)WrOv@Ky%T5x3&5fL@QiT{#i!R-ec5jEbor(-EA%AJEAb_Y->Kt3sS&< zWr9pd|BL{j@BzcTeHcmt3HPc?6UUEivta8C!)Lc@jiz~tnv?B7)?{u9E3!&1i$b@= z>`U;LRX58&RO#PpQ!en>v;DGV%1o6t)~@Jl}%L%{H9Eyo-Oy#qxh#?~)#+ zAr7L2-MZc!PCY*gwC^F=3N@5;YToy4@AMGPhlw{bF{!OaO56+|&*0u^)Js3F2|t@W zt_x{g4^5=;cHaBJV3c;4FW`=iDLa!6Tk98#vOdIQ(YdA?G_H>$Pu-_kW8p%Nr19?Q zsp73WUxRF-IIPW{V+!1fui12@!u9MQ&A#)vTPW?Gj%6fUIG z5?1bWcp1M_l*MfHNJGrgpJpunaO?W>*c@l{RI;=2fP!T|Y>sJ@w(SS3(b~+u8P0pZbtoqc#j?VON_C-XzX~QpGbtHa zi4Cah_NJFr4y|(=B1k&-96R@&-S(W__8x!S?ekc-cxf1fPqi2XT6`;=LXMgWVKBt7 z+unlSJq6);wemgqTedKh1Uj4olkzmwb5*LJwbuqa>gq563jv#ffkEgQbb2}}d?>IH z;#31e^4m2-#0+{hgAT6So&pX+H|_ddhU0r8b^4J~-*jB3XHe=`%u8`?l`W9@YKVXa z-nhv)a+vYTNIwc4&IwDBFz#@^rSOt|%$H;T=PJ*tGs@5CDq30y$>z#!mBd<{<&cw?K(QRP1Ela_oBN@Q z$AOE-VL&G%D*vMj(%0j{`Am)7%TMRmRZk8dx|)C_j+V2G)z=vIupT~4e8ZK2H3^t; zFks29Yk%NAa(lm0#TeWVOkV>X0bp)eTdglTzpae`n4$DMDgi2@$LX1wASa&R5pB;v z)6D{ye>YCulPnC?y27o4X{b;dfYaY>hft-Fme3_srch!V~q}TSW~6=nMxBeG^HhJO?+dwv_9O;-f4qB#i)ss?ZS^UUt$U5+!du)xhTNuDPhBV6do=N&3Sfw^FfGJMh~a7fVr%1Eacvo|40&;f9q z=5=_?6hsxwo=bt#yD5dG$3gx*R(!hId#nk8yOpz72gMr4iF@Y(zD)cBuqtq6&bmXN z!FgbYg`Bm*T-aHFos+ZV1q&(?0V8?<6*+v|qiH|(S^l!OeGgLJ)`nf)+z7 z%qEZdX?;UMud+Lgp$c-yHC2|axT36YWeb|3+r16z&R16|c8_@{Z1h(U?O5ss_6|S0 zfI!e@=MDC0=OuQtKAUIuUj0qp0!k>jH!BTCw{sb+8o$ZAgJ@}1nA!9tm}o!>?5v#@GYI{ z{g+hPyvIAqa=pVqy*;vM!Y_1Kug_@|dn9cm6ZL#y#xcXUXPw1KpE^A(@N@isqg30N z3mzTWY+ulRmYC-86P%5Iy=;387#wUUu7Ky&9G85SPV#_d=lRDv z?aKr7f{GQvCS5RrEIe+wjcxlQZ>wa z%`A6yzneDxI}iefu+4g0{QKbob3GbpT5ON7j|I%2-e&qWLt|kdaCM+bwXm@8#rFju z16Oy2rqa@JbB-aCG_LhJ%gNf!aR;EDK+u!>64xLmC<4d!>AUvQQfOtV)G9BK)1RNiUo24vcb$PKG0t82 zDQ~@AZ|oskXV)^xUijs17ibwPo^fB!;=_0?FuM2X!egxb`s5huqS2Bx&4P#_id@q! zlKhL$HqO^RpK&+U#Q6OO9haD%m78&4$@7N{r}4d2r(JuGo%G|T?)^)0)bqSXu7I~{ z1?W|gZcU=wERl!ci(CS-4av})8+wU4pDo3*#{;9#bxV~c9A^}{@o0Ejp3>7rzp1xJ zk>=B=zv9cX3oIvS>SBL*Y4Szyh@IqK_D^Y{cNz*}ptTuhh2bLxB1*j3^Ak`jku@KG zR@HcRWpp4qi!>OF3P2zn#icPHMh_4Bt;*RJOuy?s2eg&}Vi*`Ca0sQ1LWO+CA7!qs zE!Pc5>n~S&{t*Z`Og$%>G0y53=eF6>QQ+?}Ek?F+jYjuk3!XCNLTkhsILe?wL0Sf} z>h!U0V5Ub6!OvmJ6f(`)VtLdNY%Dm);eE|VrDZPmKfS?x)HX>u%Su-YOKmLkGN?2( zb|20fN=xw}&uJ}cUEX|uh}gUQyGg9%memZzXO|^d=&_#v$JJj)RrS4JpfG)C5b2bZ zPU&utmhJ{=kVYB-X#wdJMY=l=B^@H&aTG+l8}8!s{k`uP_x`109Ea!Zz1Du#eCC|b z+|MYMasU&Y{f~?QSe~#WCzqGA9VoHk=L=mt5P=)6xRg{!oA-@^L`kW=mi7#`wzeVM zG@Ore9;{!)|7=oTNeG9>a{iD%RRzk{IH}4H`FC&oknbA`$ zTRh&sj(vJ;$;G%}W3O_$niv(bh5tG`JLg(UmAV`q%XTDvYkzA&6{+Lw4ur2F&pZxs1OSqN`u#$ex8!_#TYp`N&*lVF!p_5 z7_=Gd1%|>Tlv%>#dZR6iSzeB0E&0QXGmmfX@HcVX{zJ{3B zx@nfkS%6Or1mj6RxzW|=m)@sM`cIX{k6av9&Q(#-5QzANA~-Y|*-4V@$=l2H%DUkXatK?rCMz2S&v4(rRbCaq4beZK5_JJ$Tr>fn03Y3^L6xy~VI~Om>E)y$+b5LrMK|(pduO8*eSjxa zmfUm%e!8QlL9xR|G35M(17)-T9z1TBzj=QDm;iuGH7I9FgRAk4zl4gk{n>jL^0uWR zQFF_jl~CH3B)zfLizr_yXu2o^7%rtUJFYF`)2* zQh`$R#rk84ztJD2Sph|SMvv~{Gxxrb!wS-{d#R;jGSEH9BVU*zX`=uP`N|C7^i=(| zcEG*3?YtpRex8i$Ly0gEwk_BzlByp{n*g9XcZSs3j{DVJ+9R)=$Cv&;cy3c+d#2uC zkN*9JA{->Oa`3JW7QAJ8y8jZIe6G)(!E2zS@{lz4a~^;7FG*K)Fs9<~7ii=%CL`tM z3mMW`_qpYk6p=A!wZBS~K8|O;%i9m_%QGgF2HbXOLD@ zR5Y#yG@NO;XDEXaJH3|c8G%WD=Hzp;A^Y7>hSUh9>(#TaMO;U({YCAhH(lUsr$sg-;)1O5~vi)U%}<<*JeVx7Nq^X*Yfw9XT>Mv-7LOKp6eV|r$^Cn zYu5Mgg7q}xfh#yJjFT{%?K?LkPu^pW+U7a`Y6jDxr1^k5M_+hM8kdMrbWYGoGxaoF z*ZCzK$W@6c5qCET16KF#4`*{c6OY#!d1MQ`_xFf?3$M+k#=2o?Mdcfh1L@#KnL%p&ck=6otTb>@!V*29zuD%FW6npY(U8Bp2v1u27tzV)<394r7$f4ER zZ9Q`|$bWB(iG{TgcscV+KClBPhgAm2jGL&Dwk~2coMXREK8{>GC`sP&0koo%l!3IuN6r=w?4dblf+t4<6S1VTA3+7x3TS zFn!Zzt2Qa6mEbUH{{RJ(7OrRWXfTJlh=uZL_?XAJuB$_KPG?Qi>XqG;esCdZd`3H)5=L$bu zJ-m%q@~-{d*<*E=OnKNR-dlta@!+Q5-L4yZ%?Nr-LrcGzfNiocDMmzDb*R#rjXT zvj4{h5`)SDAB^^@37>8pH%g}5+lWU>W!WxF9*W_(9Do(MA7{x{h%ttp=RhqyKn#T0 z7WL=vUe8L$`x4I~ISBN zZ+f9Md`&EQU87DWT3BlD9wig|X=SRM6d482A=RAQY1Mx}RDAE9VzbW4K zb#Oz+@PWnE#ud|+Bi7P`Cdz}%tdh%v?s$lQ#?Khtcm2*}40-2^Up)NI*&_O?EYCuO=`A?-+LK$)& z>B}etcjW1eEVg}YLK?>TytcU5+9KwEP}EY~r+e461q#c_Bt+Wheb9GD`ZyzNmhh9D z$NPkIWo1M-q(nt5EDNQUSGkl&Bn|tv6Ic@X4Xmi~*4@D&Yui8OC~xr|pwNc|RM9)T z>2f_}ptWLTO2ATmn~fKPjPW3fX4F<_ye-JMF+;9HgtOg@>ZxsV{48T;ot^<91&6_))u3OyzE|Gix zu9bev%O1>UJM*E}yiD-G()tBLKRhwIN=aPA$f%KzZpj?ZiSSUCdVH0qq;N&qRETBF zO+p{(Ky7}@5o==C;gA2iHNQ!zv7oDnN|MR-s`6Xx!O+*LeO22ZH?DH^S*$GerVU}`WZLR?!?2&W#C(p zZ6A@H57#h!_YGHp*vd-w)3n85Muk1rA2zGMbR{Dv__>M3-K7nf*NTB4;J~S;>du*L zJhdC#CA9w2cRopf_dXEH8{_87Z<6?NCUTFdD0SvfmrvePbU_BHp4?UsNl=*%q+Py^ z|5b_wdp1{6LYj}G^*)6oO*HcdAcB%q_aAssyPNaCV9(QYbK4vXKjoiM1zyD-FKdSJ z%d?k>GxmOeRp|vd%5XZn?gob0(T9!wJRelkhx@8t=sQF!E|^-c)NesBAQeBduL0w< z;JFuQ{VSfT{9Sp|^dfjMd%qCh)itZ$nvaMwfvm#xA*W!g@9}DU4R#Ypw(iyjc#8Ya zZ1CkJNX)(7&_`*^-J+sn1i(5)K?`1JQ03R8d(uM7V);(=vwVd+xL><1A6WcXg_h+C zag~LIg)54xL^W*Oe_k*=tn1MxC75miR~*6cG{U3h@h-r2OAF>&Bsn`=QKIe zn+6`TJfhHkg=I+a2>Oxau?)!7uhO@JQYDbfPl7n1WBlX+Ob|IibA6q|@UuD-%dgyt5w2axlH~#{dOJQpyJSA+8P|9f&Gh zgixyn5Ub{}nl?j$it2+MD|@#q9Kgp?7V&5LCB^3yonJ4W{T$9yKFho&B=-Z7$>*Q(xlY_h9k${AkC-}sh;nyUI_LjThq&5YqG?Z$yPb2whmg&R>q=L zB(G+zWMwLl#P5Rv+m)Zgxir}1hK&PCGXT~qwXgrSWOqcT*_LKxl8D(U7tuH;czUh4!#xbf^c!n`9zLX9JVx=a z`B6kMjcKLBGWxELVxvqmELi1k*91}HEp3tKTCVQ4#lk*&cdVu|7PC2U?o4g>KdmU_x9ef;TRnAcK~d0$i#3`=1+$r9 zzpDTf;E)Dh8rL=gg(Rfh?K9R2lDeJFDcj?TiM!=1!zncXn9#mGiOIeLZYKQdJuck=#0z!Jxb_tx7eyWY@w z1WDFewuh>5H4JAph#<1yy{nxSx~@?T;qI(ST;e1exJSek6upH?Zh)azH+RAS= zijOFubN?rAfPq1D5xo_?`DMzWF}z|0V;J$h=gBd9KKt6H=>bCg9a@NWOOTc} zuT5vA{8x=7S?%W?e-`5jDF5nKtVL10hqPS+)E>nfLsi|c`W4sR9L~q#5&mg>1X{rE zl4zqgS4RBzFkC6SKcg<~lLFlrG_rKpPfY)kIJvn~b%BVc7I;TZK0k-d?uKFq{J>T- zvVs}u`W{RXm1_q-8g@mCv3%A~Ti272kNnYH+V4eHMg35sSwmp@m{`WI9u0n)V?e1< zL_7|4s##-H$mS&6qSk$}!-(XMvpP6>_l@IfCQ(okaH{6QgfxO#N7V62fYfhNMP`x! zp=?4(Q;J}WUKhYFmu-0;uoege-wc&m4}`ogFWkl@j-$BO{`PI7F2C(9m)`2ZXi8jx z&o4ycGNQI*N57AVHGX-ix79wycSn6nj)}70!fH5sxysF3KE8+|Nz}0?_N){W`K^51 zd-SuB_lIWBwb=j%RQCa~5jZ2}F83n``;YHVC)QJR?W+Ut{fz3&5i5v01i?92w_Ixkjf>|^h9aCT4ap)Wb zjMJAYw^+>*6D9V_NAr+~`8Sj8>bR|6?`c*$L#+`HnmrLsR=n{$ia5+x{uyK+@<%BC zlyv%u(px6iQ^8*`CywD@)mvVEx#8Wo7A-9r;Pcq0;kf#a1z$D-7%VmFgPfZXnjZ9$ z=srqq6+2gKPKl&;-v9QF9Y_j27~&d0j;BY%3jw%NnsM#SM;d?dtb^Y0Bi|_B6DDLA zN8&QN*%udZA{aqK|Z2AILuCIe8#ZLP}j@gGQ7sH5!gXDe}>w zvQ*xw3oAiBf|cIwxM%6=Af|{@m{cOHg zYt&VEZ)0qofiRPrQZ~0a`iy|NNXnYGcZD0*F?PZzw)+St@Jtg)WC|CV2q_nFpw?`D zkGs0f1xh%FS=rM~8?a@HCA86NSZ6QgZ35BwZ7UY@=jyy7Yt}r#5@7<`jzbmDj0}!? zXW5Ygw;?<_#Se`33j*a#U;9N;pG33wYx6E5YO%ua>Oi6#mwf)bs@+OT5OBwu+tzD* z&jChzr&n4mIJ->hUY|ew(OY|;^VjZ;6=xF`V~d`2fkenIGxF_p(6td^Sxe23nGYJo zv2&~EshId|up)&Z+FIejvFrw1P4^leFQ_?oYgwS$+mk2JbyX-F`C45CpxfUqLIYeV z_#Y@=uFI#8C=*oH8g+<$e03;kFt%3jyMAJ&y3xlzTEvkprkUP-l$d+zcCg1a$c>@# zyBni5$~peRQ2(&ENZhULT1hg}!-MRsoHW6(pQ6baQ6KNcM-hQ|lRhbvB4fXdF zlADAHpyd6a*V-0`sCcC8pozwJ)8O@!pUr~Zu_uJgOquZHLcA+p+gIUo&$(T^7k%UKtvq;X# z{VuN526G0$VE_x@HRuSA_4}(ltWDooVEb|Oe{-<2t8oh8&x6y3H{qoZ9}OR&`S}vq zg_ecqW$H7RvimZBVCli6vZ`8+c5g2%=>ky-x*VQzF&>TJcUGuc2@R`EV=##1){h5| z{D5MLx9YRqH~QYpEy8}GPUHMBWfz5;DtTjL-USREM{Hm%mj}HkSBn45b0)V`^DLsK)6dpVaT>s1}`sS6BUhUWc~0OTQrHFLx|>lc>-mtH%s90 zE@OW`UjH01VdPXvblW^>Ta%RP+%WL8G5&%f1{*E^WH;}qapNMXMOCdjTuSi9ChrRG zc3;oXXeM|@*@T%lfSh0$F;bW!;U?0Qe*}S}Y_bX@W&9D@U^64)AEc>~N8dXPIJFX?=Lq;-TwOQi5AiET=)iQO*adNdZ}y+C_-R%c zLo3UxbiS=G6c%yTltLwuo&A+pEL6D z@j;sKct9vp>%eHrW*2%+BOSLEmv?{C6x1ZtNA=&M*_u5jpj0L&vcy6j_IWlRM{5rKRlB*R5oEdfus19EhSer%iZ=bVvSbpSs|cju!?BIGECM!j z@w=WU4o=CM-Em>iTW9^mtbEREWr#fuiwDf?L#Z1^_)A}h+I=-f4Cy*C)uUj{{C7Gr zd(Ie1yoP3>@u|gCAL#}-U1HxuvOem(Z%fwGybI>7tOV@r6PW+S0+n>xn@_TmvTD95 zO%VvVcvA?`>XyZN8!`aBxv|vK(2dExjNe!RWMNC4EvH)xPRR}v-S^~AAU%Peq}*va zx=fd$X4{FDr?pIr%0PQ2UGVwu`PI%xKBeauYezw#92TroY!+CZFcNX+)pI7HwRb7w6;STTC*S*}bi; zI@YjH?CBRnzOh!U8>^DHAOpu!61<@yY=B4no`l`FGj=46!`$NxPrwGRc927nK_&v6 z&^+Q5)A)9lbKItO`s`20fkkIJ)LM{}HD<}B4UMYvR@zLTP(0|tnq26FQiPIUMpAf7 zKZ%Z9)T>7^uLGlt4^Ds|ci?=kJfc7$qNZMn@WqG$S`I7P&5m06PWnH8*|>-|CIax9 zIx5B$5S-Pv!oL$1Hr5?Dw+Y$(6(ye!40}V&Umc(2pS>GXUYHspuCS!PJepw;)v)BW%P_jmo zDvE1IL4uvP2x`$!EZ@=u6)rx*r_dwEE zC4gO6alQMLRUnt8)M!w%XOz@IXKdR*3`ucpggma#-y4Fd_LIRlAr$0KMmpt*TykBV;d|@$2;OdY&}REL{cQ!dKrF2 zoCpE0GZBTb##8kgC``=ERs|`+@b7oycDZ;{J}Au&=iG@Q!$Zjc#JCGYHmSlmV9m?i ztc6^31hkW)9JPx{xE}-RU&7cJ3&6w|s9;#tWjiD9_`3b=Ex+lHf7&cuyg4#XDsXnZ zIhw3Y&-!JpJw!qh0=|)GYARNR*vCNfpYUP^X!x&-OyjQ!WYa9!H0-qqt>9(tif!DX9VSnh%|*c|zeAeJDs4o1O>K9J+pQ$>SanQR#6BC|g}v4%Qk;!T@|Yx3^G< zGYwfQ>e@}*@Ukmp-HJ_+3j5UoKqi~dUrp_XO_b`+Ph?joBCTz}_alRIVJ2(LW~CTP zhraGZ8A}ak#eXqC)j}1RN0i1s@GRzB&`#r{H6{7K$Tc>%Q0h4(@hX4Nkz%UP|uDuSepRk%yKyS@CNYRLX!G#y4)oZM4 z$bX_bUP|r0(ZlxyqxA3WX^NFIl^EX$STB=MwU?9(rLulvJ}$Fk-I#xZ;V3u7eR?H* zx0-lMuR(t16Nq~Kw6~fJd%&?kc|SiU;CCk?;*93p^o?q=A_I+s@BNGC?G7S(8jFe}{`eZHHWG_=2tMw{XHAVQa(* z1RN$`UvTkjZ>E^<+N^E_@_xZX83h}#FeLB#yM(3XS2hxQy4g`YSR3;z#6G>X{njDL z|9Zq}4iKgQPlCzubS1O4knVG_?8D2=p2BD4`r3%Z3lzZkZ)u5^y+=`A8vJiu*g5xq z$j=zM5*asFF5h(+zP23(yr4>{AU<)3=>zz;1i)N^PB%@LD!PF?LzDvAU z*D)F&4mz=yX;hVm^RH*K`4YDOP-+6i1e!pab=^JeUts1$P*R-@{wvg zyD?b57ioUq%iOHFnng-IMbxje@4U(o)_{3i3S~$M6|&#dSrJp_^hsYX2S-ZJZB%$; zlbWU(`FxMvQf5NjDb+K4?J6&>DkXF6m`h>CPF`^)sP|*5N3PmZ*Gr?pT6K{+7#>{U z@7C8Kbg4E%^Kk*gdKN#@&DJWwQhJ<9uc;Mf8zrLpBHF;U96bVF%kwypx7e|8a=yiJ zwSMx-qF>@F49CJu4+ZPo^To~bg=JsXRSX03E-dQ6ZedQL6tP*ulO;mvitP$swR=^{!G~! zclV|^2HY7AcJFp#cP*=l{~ufwj@Y*t*-x;3+9hSurhoQD&98(#K68gQV8N4j764RZ zHI`iz2*vgcTXfuRc5iK|Hg=z|pfo+UEJH#7U92$A|N64LMWpjQ>?q!F96cC%7T{N( zzWpf1RKBKk6(`=Zi?v`HCUYp!fN5fBdp92Pq-gOoFg*W+^eoyi0{C%(v-QcQX@Ybo zc6BcXj4RDnBn4ezf0~g;^0>(b-%T?o352BU2Xoem%?F$A3&zfrAt)4SSF=V_YT8uM zvs&7yRF;!UjO+n6Hqwe2OL9iUflw2@VY^ARIS^(~_dV%qXJU=7bNt=EwPMXbY1*0v zt+xt=RSR)(7t8Z?NNZd#FF;b{DQi@V=zs_}e z*;7&Zvl0YCgn+kF7IQva+2CHRVp@7#u(&)kE%;N6v}jQobw0Yy*e@mdM4bS=4nCkP zg02{xf+O_3yT?yd!R~n%Ok@3B3kWqE(=3LskEMW^xYEK=LV`oO7x;u&M_#1-8qU{h zxZ?U@lNQCcO+!8|GH5kp<;sHhYvqQ`s+Rd|gk4#=o6Qd3!rF}}>YKXsL0%|^H@i~&-Y z&gG3Et~PYPnn^!?Z;gqGNuBbVHi!LSnoJ2z%LSPwPSRcd@#x;r^&W+!$N~(3`U5NK z(@{tIPZ#`wWp956bV2oKe0#X{!8ZAvX3jny9MwJ)+W<|ss7&>`L86@l!4zJtG`lcL znRqbhVQHD%`lxzbH3>H8*O%kHhwk7TH|C7xV-MZh7foz-hQP43;GO+uN@wHGnw8(W zup_?G>EK0BJ_=emdpXB494gihU)I>zU6P>C<07zlj=!l^`@IbI;TX$vQ1vakGe)Xx zE} z#$XnyVqd_Do@x$ElV>?NR-p9qh$FJfRa_s}-Kv{zhq0o$JNu-5jL&GP_DXFoVdVa0 zH#3OGd?p>Gn6fzm-edcQQLEE3taSw$x}U^G^rhtLgui$Mk7=EL1i|(=i}qAe?G)QbuRt z^%wH;P&~$6dUMLBRpG|muh_|M|DIv z`EJG(__BRg7U=KSgq;0@?D2w(#xg(bM-QIQ&IExZQ7 z&crb-+DPtg^hk3beNM;bx@y1-;G7z+MKm7RR0Z%wtu`$pYD<2=Lf@1M`uyde~ z#|}rW*3B#2__81vMi6n4o+jJ(6H6J&PwvQHcG{QaLytJnt6}PP_hO;R3P<;49_8&5dYulO}IAJL&1m+=-r8%IUOn6Q~bMgE(Olc zyHT>Q);ZZtT7x}=DuNoWU}4x)Zr@DD%Hy=5Lsrc}uEc5V34|gMxsRUsiF2#60Xg6t z!%Q#?l~1hRY2_8<7m1$&=E1>Y)A@fq9`0RjmlR5ZxYF zqD@@R^BQ=g=!DI8{^WsG7`%bVwX8j`)b;b`b+3_iLH6wkTQt;((d1=)UI_NN#aJn+ z$K{z+c(lixADJxnHDb+pWoMeR*ym^FZ+#G?X<^OeRFQMEma-M{x8%{^Fx0pMkIOO+ zlJPy%;!_HX6(u*hU4HM-F_Kz_8^KEOYebf&zijpNFV`tmmnJ1Yz)i({0pLXIU0bSq zHB}+tq<|u3Y6s%029a$6oK~)8ik6b(;N(k{nA<(H@L)A3mo!p=pOGR_LJ*G{>cbZR zBxkLCU!i4T5zb_FH2)V1xnb4jo^%2RMc}?zeDNbIAHiTMghOESS+k zEUDXMT*+ey8YmSC;PeUe88ZhryhdDyXfhkidwC{0c*}57%`}BvbgD4xs7UVDA|sT#wWfo;#m~2udjUNxz8Y$CqsTkK&HT9 z??j!4%1vZqwvXPUm8Rys!(!pI`@KcCrIUG!EC3L5gf&lgwWi89)uyWs0{^>oPIPV} z%65TJM9~4a6Ay8JXMQPfV@TCN%Bd~R`5CpBJ+3ru^3i)^)GGp`_ypaNbzZ{IaPHJb zlO}XyV~&mP1QE;-FqNLUrYI@zbxH2|P9kMy?&bT2(aeW#^D7bV`PeVqa6jnHZ+n}9 z&Fk^LSiZc`(A`Cua09|sXqRNF&tNTT8fzDQ44@(hli&a;U!$-6DTLnda3iC0G)H?% zTmU>82op6ttI}|d&aHU-j-7asemAbwML0=x68JtJX>jy^g_EYGp9?-5o0{-EIJel$ zfc;!+A+tQQXDcBkV?vDBhpk5SG{}p~T%hWJhzCov9Zy1)3kgI4x2v-wY=sN=3xM4Z zh_iaG=v+jBXJ3}$ls^)ghDk#m(^`1ZhM6gnoI-s1Y+-Zr-4*3| zK`O3o^hQFf2|HGJ7R<=;mKc1*V%bjYd(B?j+PuyW%J*TtJ|(-HjYd!%B7U2^V9(3_ zxy}BHxT^(>Lo@3zs%WJTbZD-M2z2^S{5u^8$PC-ocNK~J&Ze7b+GvNirGV;dHPb%r zNt{(>2o#{McYEcQb4|_$k>_z(SJzbqM?O|QE9{Ky&%po0I^iF`wiiHEqe&FIrWE_N zaG9USdrKH8R_yasIID?|}q^0bxfnw|x7vfr=Cmr|_U>v%}Kf#z0 z02W-QV^ICu0b^38It+a;Dee1-C_6BgsM?VcCcwi;o;3LNdp}|VO)qGXFI#X?8H>@H z-73XRJc*Xi<1m_>m<6LsO6>}dwNMWQ2BD35Q z0V%=s>Wtz)Uc0Y%+p~Myy?fiO`)it56b%lz)*8%ml(C!1&KPPD+UDQmrM~BJU-k44 zBN^Xjh>Hx2U+arq{*81!-eg3|4cFzD^J2j>s!b(1o5*n?7UyU=QYL{27vkeH3(AVY zW$~nD`#JIhbH$Dme#KA0w~WsLey^<|D2eEx1%BAnPk_q`dEg%7YO|!W06wO}Ki@xtowFIv~2Q~7pmN2AhJW#(hmZh%mlFPJ}E)UK?e=3t@1 zjmBF+(NIX3KQFLsJ(|)j24B)oI9>+!}Z6 zq5eclDy#ibvn6|GiHc!0ll>9P>}V79PRx4i9!#c*eZ-Q$75G@7Lr8x_9tcvyW)+zFYYtIKCW%t{Acfy-5IK2!tX))k4;Gd%D&Y z8j!U5rK&xr7TW{6VOv~)J-1>G_Yg8xw;4;^!z-qS6fKRNwV;2ERb2P2I8$0e8NVK( z>NrFp7nsHXzP8#-2_8-7S3HEG3ol*RY=}tsF28F36nRG0m35dc5m3=oJW(r7I>q1F zb=8L-cs6PRyDf-?vY+CGzHt_yovCirs;Mb?{sDEsrSpuH73S#8tX=Z4p(1M$PMRQB z&~7)I-hCtVmJ#(txFnr7`=-j{lU}m8+-#Ts0@X_kL;=4||J4j!=cW()IBOqZFK@qC z#cEnVOWXW9gQre{Au0CK(OQ8)Q=#yepPb5r{n&=1yZK8F^#1o`?E(n0lOk_@Oo&Y8 zuLJzRj*WBJ{kg>d7S$l5#+EWHm5;+-!-TPHtLuwN`|&thxxr3k!N(r~sh*wy7sS?= zT3m9hbdT+Od|g0|7{GP)x$Je}@UjO(;3WHpps{`*|i zYCF53{4iyYOM5&-44A87tKA3lk?A1m;tr*}W_{F_U4$>Siv=1$RHi$N|5&FMUQcCwe2h|J`TjdU5{FXO%+_6hqiz`6}U4%k@br6VI;6x65ay5Ba zJhOIWn`o6os6V|OWEedB)_$AN*C?1jm5)wd?Vf>=2X_47(m;)QfV9Q};%*q$)eFAh z$r?q}P%^}@Ua(SsQfjRF|Gbmbwmq>snQ;ibI5pjv<47COC@C$KDf|oYgn?+ zHrz(^F8aa#q^Td}PN|QTYr;pqcoxkA2$75$*7~*u$!?i|rw6lkC7x+M7yqdFC^hZ<9bdQ7hQ4*>#0^Gi8lL)O{IA_M-}WR{QCXZ03YD6k<0xhwSQBW z_lT}`7YXg_eoMq|Ldske4&pFH12;u{;(1lwhA-<+o{*>5dy*-?g>>Gw{<^EtOhC3Z z=vMWxKm|kZSGbNuo-l}yNTvb|m`sibGe0t&$OfeXVv{FVO(nkq;oq?|Jtkh821uDy z9QdLcp+OA`_y0Ovr;?LCX+1uCaeH5g>tHcc-LL6O0#tjBkv8xA2Nt%cD+{$8mzlqv zb;}mvMa>&1ME~yNbjB16O_5 z?t%g{H@z>`x&EJGWZ4E_(ag#WJ@Pfa8B!5>(ngZjbz~T$zg=@hWjNOLh>zd|p0pn8 zeR6fPf3u&D_0s8g&I@&cJAtz;yvA@CKL|QAN_5E5Jot&S?}!MeDwgQXwC%`qHmV3+ zb5UCI^7Pd1%{CquLl3=E9nNu@4jKnOXatC(zYjS_=Q{-!6}KoojolE|m?K6!5lXf) z8?FUJ?^Q3X&br-{QjFqC=W?L4UwG&B&5rG=lO1(|bB-QEfez%=^-s9nS&3U|zzx)a z4JtArh3E4hMs&UEnEKPmD8pmP;q?#Upml#SG@KL;7nyQuis{?8S6B7u)v@LR*)Duj zpHIu~$76q?kSd);n}`E! z_KC$eW8M527(@)r04tr;I}l8CZ{dx1yzZRT9rgm16ybJ|eelpq@e+HC-beo*R4|{r z7y+f6@d*d@0~`boMF6^E)c`V7?RI-+M1(L|6ue54JzfVOM44*;vG55jOv&|@a@t8z zN;L0)zXtVz8|PCn~2)|@qX3zuxoyvuHA$LG#aG$#C}!s5li%ToAeWQHcQGI z?wf5v(1EA493Ab3x+Fdx@NSpN-wlPI`@bjJQ#|pJM|wNp;;ilF-txw?#Sohgg@#U6 zen#8AyN^==5+>;IqMS}8EL7kTy<~GB?D{{4fe&Az3rGQ;c4EB1v9GDDjt)orHCYhx z;&l%zdNTnNhx-PRso;kO-DxTOR~-39@&)22i8jlDr!&NP3QrOOz(+?+NAC&9#U~G^ zgYC!fZNa66e<^qz1az6}#j^E1s*C3=ngI)0^l+u!gh=yYu1fQt{<6vThG*N5h;=t5 z5cqxaGL6-qgjYn@HB~z;pEiw1bvJw;NQk44 zz7c-u2dQ>esnB`&EM4(LB%fR*17iI4VBtf``9sFnt}rZODmCH(hhAK^n5^dn3_?%|oCLcXv#QCVxPcoFi^)a2WZQ^(loH5i_FKAZP3}5c@ zDQ-0On%e;|5LZL)E}PNXyy`}Hzm@f|21umFjP@(gAe!Lktq;Dp9rg@!L<#~|6McjD z72_iqJ{@iu%Kdt1FxIWYP}V;hcIo^>IMM>l2&zxjiM~VMfIM0Zm%43aygv433_mWj zrlpcbE~sK#dQI#vU*-#rbXQ6YA<#vPG-Fs%&TD`ydaaMzKwnF7bp)S2*i*WJ<-DwfLf-!=iQ&5dg2w=hlz*xoY z>U4dz5>ZF`h~V-$Q(q~++@G!yau$-`$Tw-D= zr?-UA{4Rtb_H19*$4HO}K01B1LQI+aq>_Rw({(iYClm@l<+l4DcIM{+LcR!@Z`&Q9 zOZ7~P0cZNNX0x7lH4P6eO32F$dY=j{jqZ8zH)x+vFZuZ-|F2DAgQHl<*$y_Q$`&+O z+)w@4+=vYtJ-xP8lz(`g>+?PZr3j@)^y)yO@#uJ9Qb#j=3ai+>9Dj%C zd)Lv%#}sBiDs$hIY=*o9%>(v%l7y3w!|?p^!{~1N@?Mvf}m;Y4X%&@|@nn2e^-Zz1{?~^fm1~e(f7L8^Wy!b!GHyb{R z*B5*KJgzPw)uib=Atk9j3%0b&T$NV!vC)c#jrZ+wA(R}_Kl5-jbp1^3@5SJO}DS39`wzG^HBDi{`E>6 z5Lg;G(umvDySBLygt!em##+csBy5Z4z2PMhW z5dNE;SJUt-U!@ra$-f1I8qc@U55wy)6?aSFflpTj`Fn-Av7m$Hp zK`QnQ{^Zg$Rvj}~5HKPjnV(1jd3$>zs0}PFeU9z_fC@4wH3{$zd_=i3!Q^4!l?OYY zOUX*u53k_b23tq#jnnU(ZRl^cia@70y zye5Q|DxdqbEW}wt(CR_t8nA+08{TR_vyj9WKt+IniL2iNk`4jY@SePsr&SO(_b&t( z*>EW_5q{z~<0ewC^T@81695Oc_wYwqXKWg2k@@F`DLunIZlYXEBW={||(f(e~%~KE9o+S*g)0x4(#g8X>!a(?FZvtQ7tK7oI;)+ZR)>uBDnFcbii* zMTR*=PN*WsPqW;{y|_s2ku1g6jWbQ6g#Zm_EeO#7>WU*b@cfgE;1D3~KG4WO{p7+%*9l!3TTQShV z3PPPuvwI3tQqIMcW7T%_ZLgy;#IJ@%|MQ%bmU?NOELzM_=#I#BZmMR~t$H?$RQ1!T zV+uOWEBQE7_M zc#-|4(-&^N^wT6rZmUPb!hF*)AFmby-WFdGxkg zHi)~qFjCp;B76&fg0Y3d2w>K=!YX=j$EKV8&1D9f*(^ndDTTt53Ch7A6G5JE4mxv$ z9u}$@#-PLSvdRszTG}c<4BAR=+Ehz1C&~bx1f9)265CjMhWVEB2w5$%qWd>8@S1{$BNboU88`#NPZQh2hFy0D zYZK%%Z2v>Tzkn+5L!!l8(T|vQ(3;e8lJT=A%va7GZWf zh`CWyvFbB+SUY0dI_{~RZC#Z69;ZnE^}81>Hz5d~D5LPq9a6^&53RL_6AfPi$y7Su zp8o#Cb1^V{Ke<@|*jB&Cz=GU~pfv-npuRsjW$}CCn&%35yEko^3wT(RwLn?>V>idl zPt=%$PDB`cZyEJEA8enlYZs#5-nHM}N4{}XD0w^yjz2Il>l(LZ$9y}$#1gHgbv-q& zhmZqZP9TMXsR+y_8XZR)EY9ctO(O3!$Eg5~`|XV#W`Z)%1ZPEb2;InA3z16eVwc!+ zeSxXNokj~BcNg+%fbGL`mX$K@`j0Qz9mr{u0WBMQf@VGIrapronr&zG{YDnxV~{&` zMx?(A_?@s?DfUDga8Pz0Mr4c60SW8zxFaog`)Q1z=7c+x-G)b#*?r@+iD9GDl1?-p zC{|{AP8CK;Mb-QUP3F!tE=}zfZ z0RidmMnbwvx*i1=|o1oY~vskCb0_EXAQbJXt{LQ-A4jz>ElRu21LuNKa^lFqPIB< zace4(j4XFC^EcHhlWGzyn})sFnVIDC>J-@SpYn3PJ|~8MPTycq706IhV+ro%-nTAu z|9q-U|2HT+!?h+LDnCs|M`&BQykUz^>ptMsi`e5W)noG$vM(SsAJPq`vj?ww`^$)9 zw==oWzQJO4mfB0Zxh8|)ZWWHOot^OkRB5RKZcv9r#>EjKKi_ehxy?KiCsY{1!dU66 zaRwcB&TokXv(F@nOhm-o;%e7p9XV5~QRjR!)+UF7$?nI!A-12jhbj8&7=5ZPy^FCX z_sWJlUZP(elTY~@5|R1TVWD{5WYOX7?lrlsY)Et;y@d%G>iya2rWOe7d?&*?af$%M z$m$MC8I#cyo3a1MeNoD`~jr388HzY3#gWNU4&=yiQm7P4fPJiwV#x|GCEi z@e}>n7C~CavpOB-v+v_0Vv-1v!l63R>Xra^^F?gWa+hIbwHn<1Szr81Rj3aAIvMr;^L`m z0EZj~Ar7~WM`c_N8JReBTrG4Wwx}{&N9ejfVI(uYTpS%W>et=ZU@|+W+$rA70=rr0 zPw!~(UlV>LcYQj%%|USa!DJGsIu(5RP@uMLH#?*vfBoM=MuIwU1qpfI zYqJC8X(mS{ifyB%5FoNKUv&oZ8tyrZr5(h8cA&Z5$sG@DN41@jsZHJjY!3iL$G5r# zsC37-_*a4{t)WzZCzajd@kbK=Wjk#;r;OYfAiPg+2{qw}a%!kw8@X&zW%uE(;<@^% zLNeCN1=a%Kg^_$R(e=t?1@7ODa#*=VGhV$Y(dk%+5#GXOnYO=GWi}r_oh+T?@I};P zg^qG|4;-1!kx#9h*F_T#>rx2n_R{*Ot*HwHbfPVh1rJy|^O4nVI5WRgTzHQb4&oS_ z5=`D8Ul$l3Nn=zq237(iMRWac{2!B1UJk%`fACwax{5ptUk}OORhTIP3(+v{Q@~0Y z2pUdn{Kn~N07$QyzGp68XAh)o!t6-?TVT-vP^Q5T&J9VtkEe>n= zcsx1$Pbpufo(oEE z)Tu=eW24ERCzYBBViL`*-{Z`~P|Yd86?GANr0jno&5+K*P?UF?c$AcRSomeIqM{%w zN?{e0>VerSVEa%Rn~%kOMmbweWO5*7&Q=R6k%>mXW^;l!dbK0S5X^f4()i-~u}SJCQojC`TV+?fX#MHNkY#z;2nfe;rW^eA~v?i06p4-;2&ZdmD_*QSse zLv8E;wzZAIE@NN{EeUa;bq4!kTwDJXL5!de+axY`3`2)OFfa0!Pu`%LVL{rC1aGL* zD9PY^IP4ou>B12cWlsrlm}$$4NW6Ze3CjL*w+^B&+T7D`smoeY$X9JfWQfy@YqhE> z`}-~nGFXKSu`k%-f|N+^EXOLpDY6umQkvm18_3`WTbX%L)_)%s)R*|eAN&o+Cq2M5 z`Av*7O4rjz5Cf~zF%c}HW&W9AAX&@)9-{ga>6}}1>++5BAQ=T{_f?gAANT-2vDbFy z21I6(&3L)yW~4!XjX?;iTs?~@EHG7O2SrLb(CG=^-2VV(j`)=#KOP(@00**a=l8}6 zJBTTJb`(&2Mg;JzN!R?DT&I4--pCAndy99c9jM z8_8>NfBa4GPy(RrOn#*Bu7bjZdwRv>K?^m-X_10>cka>>=(WG$_fdw?h5}VN9$dfT z;?jeddBh|5d>7MAD1e7wv*?gX>6VaENR2i*n!x3>na7(XFWuTIUnnz#`Klsma@V48 zsWEhoiMB?x3Z2da=2_c`j7uDL>{1PkEN4CchkSPCfP!vn*lkni>;xJjj91b61u7^oAS8P`WG2-M-uK0Z_=3E8SxconyD^0d`H?wo_tuj$2n> zSP#|%xZ|)ozTJ#A6gL%Jo2O~ebUw|{xT2Q5h*kzwB_ysViCoN)7@1o-_n&?RmcWA1 zw3g;L_L4wGQ;i&Eul=tM`zBuptHU~z$9<7RWn9g%G>qFTQqU>+#v?AH)4|7k?{12< z-f@tiNNR8F<^&G{6V7j~m>+tSHf_C&V#Y^-91I)41qvyzaZ&$nfV?Ww`gCPHamTL_*mOWL&k?f{k*Fwq|vE6DGbK zf&hFNtQ}iXC??<`kh$;wtNygs`X4&w!O$L;vXR-v(V{~?vZLr6{Mbp*)7BAW!-lyg z=P@lC-}TLa$Dzf#=vdfeXXUQ!&p zazTn#Sc7*i$REDSnPWEm^oazhCk5#7sM&lPoN}Fy08_SNZRLnr60AF6!kiF=m9rVy zV-KV!f?%_jS{D3M{Va~XvnMN`Hm~d#puMkO0bGoD^EZSI-oIAHuj_psk}^r+?9qxUY-+Vh%gH4czu}47G8O^4UJ!OI} z_W>h-9T}0}R}ZU8h6nLe*@mXiCv|^OU}6>l?-eEGHJRc|E*9PJ)JGK=*s&p8U||g3 zm@&kU3i*snHUDRIYxP+?Uou$oD`-id6KQx#F*i}b zs1IejSf9G~p}z{=UJfeMB# zFALba!4QL$9$0E7YD*_hgnII$A_q=d%N0SVE+Jgm-FA9*2R!&JQxzN4|4#whWq~}7 zScS6R7ff>1#L_%3;Xn0!OLbG}iC#=3KpcNMkyu54po3h2-E1zAg>JS;V^9M@5sSs; z?HACz4*I*gd|-otR!`K_QqVN~qYZO4@K(FZcX8`<6=rn+1V}zp-i=jtPr1X{sa%O0 zc-20%C-0|JoFD?^^aDL}RUUS>8F(KlMH;cVed-c~@+f6lpQITEq_-a=lv}P@kqP`M z8-x=&rC)3OR#XON9$({8za97Bkt9aXq zu8*%A}R-<+n1dD!=txI)()BpiCbyg(HM)*~Dp8i&Y_zwcew z2wS~o+%nR-^;NI0F-Dj{!IsR3g$ zZfFvP6sU0cn|zYCNGr4RJ!a(u76Vurr5)o&+JT)x<=>O+jEVBP{KX8~8e;A2?CP5D zX<|cbzWoS(8{>Thr}JW z?Cxn#lANB6DI}Uax5KG*;n%R>vdVmCkhb?9j5wv#hWV3 z4yG%hlVkUr0iex4?EetS_z$XB%K_*UWT^{-sij><-X~Rj3&YeUg??zzI%E3JB#?SM z$}vfjfy|Iq7uhS{mKt(fsXj0I=hM~GO>`*d0@TADD*8~0kscICs`oR zGou3779tJ(H|dD^G5zJkBR-sh{FmmLb9W()Iu~xpaxkf__+xV2AH^PjRaJY8tU}r6 z)17O^?vMKb(s(##yj&A`yxbaWpQyrs3L;T8rNNc!{lyFZxsD0}kdDX$G^7GleVKP& zl%+b2@Om+6Q%m!J&~v8$X1QK!D>HXf5#@h3owNEZ&CH9 zI5>Kzqg1{C5aS9M4d>F%6`4Koemb~O-3DCrhKU-F&Eynrk=MSzC_(R|TVRN*9Ji~l zy7JcuM0YHIQ3%lbaRchoQFSQH(7YPFfE0>1pW*hoe4ACoG1>MGoe~3MQg}hAuO$7i zChwni+|Sbo3)SvRxKYv{hYR<&w?*mj`jT?Z5w8`0cGE!FKC-3nZoSCPOY2sPkKkT+ z{jdnMOtoVb&7w@(Y+;*QtLb%1kq;3}Uwc(1=>;NwfWkd6F$4sy%e5MB8Fn{A6FtK< z*uq8rux_`xusf+FyR7~kDish&aHEg07~c!iK9@_0Q_J4G(B{cnYxN~}oo|3UqTK6Q za20}AYayvP<_u%iHB4b+J%0C-rN((M_!?qIk7^t^P=iE($2R8f0P z=A*pmaG?Wqu7Pi>BWs6rMvFirL-@q~Yi91{#+-Df#@~KMt`Y@mOw+Z%#IwE>rmRNe8 zjaYhR@O7RqC4)ldhK-}>IySzp0Mk5RR}G>xu+DSCtJh%(g~v)*+r+o>+b<@0D|WfA z(d9Xjv@^-82cu1nG@ZW#52Ht@$??(?V*ejKeA~-ML`@%O1d;gdNTrntXt%^+IIR9R z-ncpD=kcIe|Es7vv0X)ZXVR?#W43uo-p^U-Yp2mpLbUi*1Sxue$YL?+O?hGA0S}_5 z;hx9X`1of~0%`ZTZ{|c+TD@bIl(e8w%hy2Y+wOZ+KCi}5f$T@^B62ZpoXpA%&)3E_ z@C-7W$j>OSb+b4<9SZ(xk1iH{RUX~%qtBfrFr&MD%5rVFWNyguN~2v*>zyN3>9253 zj;AX45ez}EdD%YWJL??_qDLFs-!hZJp3BI_2Ca4GmIcru02&Ks|A6p@&6O>kPy@Mr zyvmg+Zxslp6sjUzd#1c8(#-5=I3BpEH}Gvxk|~qdz+*r2=pMiv1ap)v#IExx&K&r# z&5$)`ZR$|afFkr)8nhWK%uZa*8tZrb=e85D#s~aYfdO?6*f?Pf)>w031n#w9RXYov zdmmnWFvj;U*+omml3NB~QpHE98wWa{{H2rhpW?9Iv=9JeX87-H@&_ud44wwR3H{B#2@mF5 z@19xr8IDo>KrI_AC$)Q% zGqm@AXk}vlM}A$+1Pma-=Di{@Qm+XKe5Um4!R=&}u9IC-nF_Fz%G|c;Y=?b+mibbV<+w`Chpxs9N?8Z{4_Mf<`nbV_!iWc-=iz>O6aSbJ3 zE`Q}r7A>AD z>2cIt#GUlP^fLA8IDHC;yF3Y?MNX-*td6jMu%0sbPeD1-hCAJFLCXXAl$R@qp$TWFWjOYJ4#Oa#miB7v^!*~LZ5)a9#e z)YHcEOHz6xZ>(Kw&wib~TQJ1~?(T=vqyzx^tpERql3;|FXGnT@Kog_FTpu zis=f%6ZKnM#0*|jGn3O38HzSmpKFfp?(Pb+K?dM31KYrTA-?R+{9KZC$&f%1&E?@O z5%hWP>@t4R&Y%9>ts?tr64J2ti05BSadK8PIek&bo?~%k@146k43ke;_KdQ&$DV-( z=;yZtO~>OmluuhdXRj7J=^jFPIEMhv9|2AdIDZ4hSH}d*EYsQjkw?=_3tFQXZI-rV zOi_sYe2v4`HwYFv@$^Y>ev_Ywja9-qUQr9o6_5-D9VE4GBxvRqTBR8h<(ly0hJ70d zWaPZy+ykC5jhf$3xLPX@^3Iz{If1)3YjmiTm)c)K(D`#lrt6A}rCy2XFqDSR^BtzY<{M*I22 z3HJFuYJ&0kCqg2z3s6*nmI9Va;rz%+WtSz7AdtcR8hw$?>w#bWpm~ROW2%edx?g#r zl|JS1Gi=+qbx! z!`e#R+L{sEZ1VW0nW!dmf~v?ypV`a7?O)-^`IP>n-Ag@zqha&qR~+lt0s^uOlP18D zMRTuFDvc*$M_e*lXU`K@(Hcu$!Ef1cA8DqM?JH;L!^))@1aAP1E&=+iy@SE+nP2rO*oMci{=l5OIbh^YWn(B4C$~C z_m#o=*PBGu*Je=>lgW;Wpj%+M+ejp@0xA9O?~g$6zi`H9;9p8h_s~{#C-4Ix;2Q^ z?AzLajY@^XWLc+V#%DQ65KkMyY;R;pP=@3~k&q0e6j|G@UWyatq8`X~%qq>LnXxos zXI@PsxaFO?ds*E=m)$9t67|J2_of~?>3nftvT%FwpNrN!)evf)b)%r9=mFCMqLusM4{n2}6YcJr;L65o2jglC1= zsDj9-BB`P`s%K4;M!~Gg)@R=dT)PtgciPF{%E0tM5H>+5`}pk-_fgI3J~lZ!GYnuR zl~qzQs7d}4eW%-phJtst@x$b05XgoBQxl4)dI>kCmedtdohg+@?;Te#c2wXQRC33k z>w}f^a=S;A0?c~QMF370%G2)htqE70hFcFhmOmGAPx3?98}) z3A~}*R~#4{#MrQ;FN-v{k8Zv6#>+JP!J4O=-ZOWV1O&D^$OYl!!<=Tm$o1}+FW+v(J59m>G9k#st(|S{9VNn3Rbh+mQ zatlODvT`ctsER;VEVEib?`HT-YxOP^~k_Bt!Q z#pGMk>ttZPRlN5e_<_X!u57^q@Z9`e9r|AZ&CzBFdRm_IB^rYwuQn3&A)U<*$noYw zAQ*Ww*=6T~reyfRNMv#P5pLH?5FjBy7Y*z_Roy#8D#)$8YFy%~wEBBpWvxE7Jl}@> z^=Gw%WMv@%yLR-M;qYP{-;zkBfpk9@D8TxSKi z9IEx$sE`vw`~W%wBMyK4mgDdf=R_2RvEZIXstWMRf%cAzF&rlRtnAc z0hZLYS+zw&aJq#|9hr{CJ>xvUpwp#kOs6V5IP&`|P)bB^B!cTqy3gGkrg! zBLv#_ro73n2m8|$D&nDr(-=89;S#b8eG3=77SV6h7kT*A5wK^1 z20tujIuRh7f;$Fp1<#F*NKv=s@(fQ3dH59Ob{BeQ#>%G}@ZybzI}(->9(6dpiDqyP zP<#{wnAra|T!Tg3bo~tb&7ogIC5_NnG{yh5&VFw3ggf(Qekv-HP?BfCP2z9xp^&Qk z%~%s*S=`M}{h-y$hU3R7*R2zWG}DYWUuX66g_8aXX6+Vo%8E3EPyVL3rHG;R!EO8R zO$gvQq1ef6=mKV;(DwvpynaHQWYpBL)a^h+la~7P=NBC7_L1qoPjHqJn7V4$B|hx! z7g;?P9Db-{(#(J9ymxu;b?0!NZR^}@r#^AJ?Via?pg?7q0%b^Bm?3TlKAH3NZnQbE zbQC^kZ@1Xr*79^3+vJ{*y~tzX+1uNzFoXuTD0^(f2l~sW^88q;VJFQKk^d?xiFx^F z9Agqfm)z!^QZlB(^F4rDyUwYmt&m93?>sv@T7&pLwdzoY3@7p$0qtIP- zv_V@;aQCy&o@Z?GoW?XN42p3Kbj6mq;jA^n4SO%EGW0b=9NTBd$tX@DI@j`q@CQk5 zR0>3z>anJDPW6a_qhHkuMnp#snPf?aN_rj(@eFn%T0tgCgp4hrsYR!yWZnK*d??*QfW;Q^1P55#xr$TF{D)Qr_Ir} zAJ$JhrJ0LqPbqGgJ}9d{E~E4%i69&dDP&MwS0M=7Hlf5DPK&mCI8}G(ia!b}bO(oZ ze5>;WS?9@(?y)K=?4Md25LMEDiM*-0e6DyK><&Y@4)S6a}wfrejC8oA&Lpn@jS=3 zuoVt;qcKE5{Ysf zMA_FPU_cYhK4Ex6MBlq2`5=vTc@5OF};EsWHmJj-j{knHZ&)D(%?Ar zb1*1Wh+@Pj+g3Z)u#128uHLI9Kj<1oRh-t;*5>5obo`{|8PeotVi6oTdgRkPnzXMo zx4UK6fEo-Y>Fi&q>s(EI6rk?K<&qD~E9{+RGHPuPi&?ybYuak&rM4wGe#vRwL_uzw zh^DKY#efUgl!tYs!?!a-rB47U#r)3#L1NuuWNcOS_9A(T4u)ns*&zy&*<5cYZsY1+=c zkd?m!l5|8Qf6W0ZQ2&s^=x{uuR8R=7TD3Rz=7ZfA^J;$*Ncq6x@ZY^9;V6Yg51*{f z<)^3UcPUSQ6SFyEP)|>(%W6-3$(RD+$#_CQiR~k*S>ixo&ziK1Ibp_IZ8!#k|ZmVf|CM=^#gbY`({iCWS2Ias=)fH|iB9ruS0BUX<(wY&=PSxUJ&L5juf8nuL@T z64t=Bh$1IN4-Wz1PiLA%|gggzsW|B>066c%ux37Qh9G{?bf@+LgU}+%;2Q`I29@b>AR;`z=qMsG~ zaYm9YBYcSSPsz^gjE>IwZO?^E`hoK19zCRdztS3~uV13=C=htBJl?h|q-_--?=tll z8aYNjQdQV>OIT4U>gx~ti)P-BD`c6;6XdHam{8GZUugEaVcy)#$ozWUmh{^v@qA3> z>x|lvzk7cI^K`i(^qVjb7G6QJA1qJ@8Z^hvzYeatuS5yt-ezWLa$SPEyK2!l`qSZl zd^?)=Xu$vgE~=*#->)REWlk4Xi5H1&`l|U*zYE}Td&x(^Gh_BJ0Bq-@TBYF zTs3!p5uT?65^F@_NkX0vp(6byzNhSW*BetfF*FyW0hx_5Q2riJ(2I!$T=GFbFr{euSHC^dUy-imm zU;*HuA%lX^{^||+qMKvP>751dWH~*pndsPX*XGTcKV)yJP1F5Hrl`CsfiLWK%!i}S z%W(hlu`!0&n0#d%;vdVNEY%e-%`efTQ-rQ|kZo7#b`u1CoUvwLT{zI$AIjiHNTqmS z*3zCUw{Isyyn~5}xiwSN>mcJv`P03_W^?m{Zk-wpHND*47e>=epb8^ETxl|h8gozG z9T;u0{B-&&PZG`m#Rjr$?faO0s&{K6`${L+qy2KJxeP8OI!KA*^22BG>nF%w&mWi| zieD%@lB<}L)KtKdgkcyi#Cm*&TR}}Ea7pmQQ!=M{hWuJ8Se37JtBO^`R=B%t^Z02% zx~>rPrXAco47wn@qUE)fL#f z{gGlYNLMJG>49gY*S=}2p1FVaOIo2lDa2;J9_Po8AId!aX3$_vjYgM`ON~{p6`?=r zBxYqy*Z8*)zScmug-Oy8MJe%IgMZ3td^T#tnbF#O6p_`O3DBeoH{bq2CUJDcs7o8?lp>O2M3 zfTbe|x^34y4-)soMJ;vQ2a3&MWfB`0v zWdFs5vBL^Gjl%g;&z)7ICqeN4PrBk}D!PKi&o{R&Mrx1Ef5tV8XO=ehOv zzOmi9tjXv2m%m?r2@;Z7tTuzVM5XxNJ`l{8ThVVO$n^|TKD-D=JFW2UVIh0%g^`Gd zpYuj3+Y-%5KG1L**xuPUf-=q4ZJq@L74Ul;;lnwW3^cKH7`lP<{n%2B&L=$IGnX2V zAP?t!OB3ILTd)Npp;X^PZ+BNwPjAt+!9e@( zw9bMn8at;o?FRR&y>vDW=tU|-S-8q<=Lfoy#+R78CaM525iemZ9ZP=_htK^gtlE{> zQTa4)lFVAcJyxz^S@gKVbTr$+TxD!1<-2SxcLrXt+iG_w*CmR0NR-#$R>iX}1xH`J zCki$*AI12BYPkIxPt~6*eE5UlDQ+5{1KNojd>;RqIJC@mE==IC37vQG?AIwgw`}RN z6x(YOH4a5iqxhfC^5=f6*oinFWQmM`+C-L>va7Fu3A1jlJn@sd>TjTlSB-_Fe|?L# z?|ut`oFU&jVHV~YL46>6mCu&cv23h-NQEMH z5A+WAp0@ILr;)hOWJkO($&)sT)RVAbFC6U*ABqP*EbG=AsWf5lr@^JvtazeyMCI1w zSKb$W3Ju6oO43~C^b4avM(eLil@MlMr9m0FaCwie6&VfT;BO#oI40a7Z6c;ka5HaUoQU;#{McEJeba9_To_vSr8A{87 zS;%kn#q+Z>7v|+5zXV!(Rc7)_)i=5jHLZji>Yke<$M%7rbO?vfw<3C0I|$9oyCKt3 z=+k^sIy@g|KaY-%7VFV7ID6^AGTho}w@DUlb8LTik#9x)ap*Vsw#%I0M(_QYC5%Zn zjW|`pM%y~{2!f#l-ARwg08;v|`vIn!(^ahw)9IJLLU1n^uY$Il@QZ?;rz{F+AFTg0 z^D+jeoTKr)%cYO^-c4ouB;a;XLC_C{ld-=tNs#3Z8?^_Rrnr>nQF>mIx4{w-0keKk zUs;(5Yn+*x1H?2FU;o#NZ|BzCLyP3gR*&2dVe8?jwu!-ivGg6|pI-OH` z%K`ZlmBpVRuK7#8mkX}B-@m_LjKIq0uv9D4TUfH9v1aaP82r=W@{HfW%weUyHH3-9 z%i%_ENdN3rjQWo^{a=gA%6fJuij?&9d={&xdMUosGcxWSUFl#o{`NE#S&DPGUHtU_ zq^c42D57HG1-Mu1m!hip1u1B%s#I@{3FjexZcNDi9p#+XX>h#_Zn_5%n|G;??Kg>| z96~wy%ySEii~cASvyr5BH)I*bJ_LJ*H?7mxo3^i>iQ{#dm|=IXS83$w(#-^1tGa;oCOtIMzS54?d}LHM!F{u@PCkTsoDJsI4;l1)(71Z-ZFkZlB#B{ao;a)>Y3yse|sshz^ z`qko-p|q&1B7qbi4vGu7?USRPR&9rvySQuh)zpwW(s&ftBrSEV_SI%PN<@gwk|9b? zna|Wa9nZmdjam~NGkk>}@Wj&R{D;2B>f485%lTM>kL*J4fh=PN!6D{d} z6|S9(R7XD+Y9%iB+V2-De8k(lwO=dzu_d%(q06}_JRVv9)9|n<-o6n7)XsQTI zNv!oV3zqP~wTtfS>vJIfg85QZ8I(wcg@rB3@;AtqlM1ma7hxEZ<0f;$MxuDB8y=c!l_U*=UR^C$-^3HwGyo$5L!Vkrr=g_H9M=NqN(s zg9=gn4^&7Xn(aL{nk3nFtl-~wGVHIML=R9z61dtr`+0NY0c!zBd2Ktx@fkt3jt1qY z1MwNW3pNO}-Pf-E5P9uSR8@xInCoii3+jYpiutqh9E{zLl=3yJ9Uu_zyRJPe|B0ZO zP~2WEeBam#Tt96V8t?%$*v?!xze<+h!`P;$tQ@$zyNixw@nNcQFXSo&G2eGEL+Cc= zl9hlk;|#&OwtqUB9NwUuxp8i!viRquWjfZ~kH4~iCULs*85oorD)CYh(g-+^XSiVn zjDoE@;-EZv!NNh>0FPFJD!*Fhk5!6a#f{{3ycFeqQ0K9z+>oKHqAK(G3)9{^FE5~Y?^ez^>Lpvx!O=P()K`YPZ4{kfwY@eSg~8-N+|ypxqx($Y$C zD5m%cx{LA2$(-6+0#L;TB8|Wagz7~Zn!Tpw6go=rTbMP|RP$43i!1YH>c_3$s=8^a zPeIBdE+#(jD$RRSzh;*JU^^OWuT$-%V8iV%`0gr2qCf6-E3BCG8;IidThVVXC|kAz z7s5T)IUuHk=aVuW<_=O*JCLDtu0MD02^+@p45L2Jm2_-y<1pZ@q!NdK_Q8w=PcQgh zy^~}zoA@#Cz|QAfS;uuS9vcx3;4X*07Mywb1Z`{1&=F^%;HQcGesc!_bEa(@Fcd~b zD)_})Y($5Vto0Pu#W3^Cx|o_2bWEZ2Nv7J)?_@7Z$s=Af_xdTj|6O91xc{YEJM z1nxE|x!6)I!m_t54<3+$dKngZAD{sI^ZNl8LCCOHL(UBSYJPOO?*V>;q+cO|8N7TL z-+O#Kbn*sL0Y6DbKP{?1eK(xX%)G7Ec3n$4Yt%1GAseUe4qN1P#;3*;q0&Z4?A|2v zES{!p5>r{0R(3q{pWfcA!+EvwdOFX&f2D{3iDkJXPhAdp$L4@E(Y-E^-1NH?UUA}; zy>c+R$^02+i_J`>X?1HK%dJ}=iCjBIN;@*)v$K{O%}QUbUok9wK@XAy5ycx*!fSlG zC;}SF$#)1QyL>{+xSzo;C2`LV4&uOVp#nu_&n}TJub6jnxRBLv@6u?`fCHIlP81nN z$=sxnc{s=vGi!%)qC5hG%x+h-OZ@@gaTCbmI{&WZD#XwBx&FOI$C43!aq)isA!f;b zC{eENJI=-2Cx}9xtq2f?bC{`=y*=ThjPhle390%sLwt@eBIcXUC6Mz zdzmky(tjxZyqdb^q&H^iv#Bg0)>Y^5!yxlj@32qU-xzP~a3+b^4U=OfvQNx;>U9TT z9}R-NBmOegz)H5%NPTX2nTnh4jYv20jbO>sWZ#LOYhA)Pf08Kktw$8{OgW}Ad~{g$ zwtB1$&GUY=65!y-m;WRvc6jJhB;w$pc$0+M`3x1v%V;Dt2VXuByxWe`_+>=zqznCH z+%rbG8Wt1GteM}@YjAZ#UME5H;@eeSUBX%z^Ki~xE@kyC=VwFw!Irh5#n+!$STlAp zPIID=$;ig3IkBNF;*wmyqQ`2y4_6&hLg{2fl`TfJQ)VaeP<`rBPUp|mOMf9f~E|qe97c3RvkKAu+NNBea6Zy`)|?Z0{AF0C$dhkEOG2VxuyV)GW=fo6VLo+sNdgO(kJn{sb2%pWZQ-9( zDgHgJFt+#r?q*Qf%LQ8Pgc^*o@)~Yc;)4o~7(gpg2ra)Cy-~_P-lp=omSR5m5nj7f zyUl|o8{vPTOn?(bBcR0OJ!)V!l3=QS3L?+%J6d@3cy;HqRG#=%zrh5D;(qXm4H)xg z6{X$px9d~SFB($fnNX7)RJ8OYQ2p~Wq!207XvFF15b=pbTOanzEA*pUR#E1J_hH3=ww=kMW$%&9+Y-V|eT7*;t3S^|sG{1{avw6f zl#`$v6>R8jWxN?FYrmH-@_JuXK7475+$p{alv~7_++VFqV!D!4CH_5jrM!1$@|IS) z^&@d7Z6|q8uXMtNx)9-2=%bTc5IZ%5X0>vsuP(xU*fp>RoU2w}qYIC$yQF;Yi%vk8w^H!#qt5AJzXN!5o(6SUmKmWD26Na& z-RsG8vF)L`dY$SQ3j4A!l8^t>24R- zi-*J!%gQ$J|J~}u)Ake-Ol%p$5z!YZKpd&#iH9m(H;<2Pc5sRnUrlis#z z%gneJGI<-!)c?g4^=ndJUqzyn&v{Myj)+>nt+d2;KK>hYhDFWmA4;ueeDhDGDBSYa zz$aK%XV+NXUs+62i}-I)=uAZe@>f5jR>iSWv>Pf;mx&Pt8MveZ3g0N23RKamnp}ya z1tQme^nNNom~W6`W>t_*VvhjH7yDEGBmi4fOKlL?e4Zu&G3|zruLxL@=8lYgJyuxV z4$c>|jJ~7C|4(%5HDtgeVv+wCn&W5uY7XbPU__NTg+WaDMWb0%QePzT9TIE$K^IgN zYbCSwniV`#0Nrb`4D~uwlZo-FdNxe4mwq!tQNA|;RUTs&Y5}k9mmxkCJ7UF6ckl4l zx5Q#itE;PXe*Hp?x!c4VI^`m?@_3#6KktdAT#$jhrWLfL|F{8fyAVSnE~^MvMe1`J z150E!R-|@nboMJ;XR8T~97Hk5#i78LhiWYs_J%L(T+Mys2fkOMONKWurOJ!5k)EQA zf`+>X>!91l7sFG}7p~+0kj-@E8h3Pf8ya$l9G~lIK>sH^2m2jgc7pGD5~ac3Fm!oiX;1rLkn+$r6P;p+{w3vsOyhjO>((_hz2=o;C@bsU1Nu%X+v*1my{(VPb1z)NFEp??~f@yf$AV`ymnw$49$%KNCl@CP2nV z_!Af`O1&;$zF+TyyRon>&$4a0H4MYyQJP=rmABUi34!^|7K-UPZUZaZvz#U~p z(O0CZ@nrqpc`VaKj4U{pC8%kF5FT5lk^^CS6iFDA^11$JTeB@(Yv-q|GT-+}BB?5n zGV8h~{2%;1rS%|$o&_K_$S#v30YtqoxKxj~|6sjuFqmD~z$9f8muX4LKCo0YC5XG1 zfIMwmpdxF*V){BYzTcQ*Ttl+rmgr5{-!M6woTi9 z9hBIGAgg$<@Kn^P!nxj)e3yp3It*-DOn}ss^VN9mE=MojGK8}Mkvix@YFmeg7hV4{ z9t7%o$&2R8KHA%j{i};(&K3!Oz|bZ-C->NtKK?_F+3o^|P#!=sqpe#qcO+;PMaIv! zZw7;!U+C6S4;eVcj!2L0eII-@QC(?wSac(#k+M^>axO-l>bkY`$9oTkj{ze1X08`Y z#aB4@n!CT?Jnnr7ED#=1nd>LXp;8a=p?eUD(*1?_m3_R4G z)?7;y;;b+OJKJ(i8c&>Ngi}{<^z`9D;a;r!Ooy<5!hUaWg$N_;2PtV6u$Q3EhMF9a z#F#1Dnm2V^PaTfCW43-qp85j(h0FH|J+Jk82Ag8m!^(38D)D6*q8@1?z^@01fgI{o^c!$zmJ^49|y)l)4Y$(w_Igw$V+0LmZFct|9??4<1nP)hH-DVYw z32#t0d({%NNjXGOyQRnnh$4ED!ek^`nZhiy;3h9rOOM74QPDWjew6O$GEri#7`#*~ zxDoyl!T^R@Yh(-B`i zw^q-HVD{@iP1qh>-hOv#Tj^7_CVQ%Urln-yB>-iy>!kvxC8<)NXnb0!Y;Ao~BUc`;ps;n7&DUjDb z8os$i>+f#VV2i$Xb;A204G3_Bqm%4f@2b^P5$&q<8jS@$CwWa$*n(!hcpnR>7SCmV zV9&m^On;w-$M#c5pJkPmIrF;P+xZ5|xHAQ8)EuI9XV6pxb(Z!Ma#;2H8!fzVo%4zn zV?h-(v|iNWd2I>%>930{!1u&7*;=~sEsl$$Qi01l!P4x=3t(XN2}PK`H17DK@Nn&z z`Pw32Y$?aiP4lbMt-$2&{>YQkxHCBP&);c zU{;Q0hjs<11AB@{>10Wr;kAg88^W{;W90*hWz^%*9UrbC&6}5#qFk7FTyfG7BW>3Z%AG~ialB!6+pPJ)x^8v$`0Y5m~ithrk!m~x=xhfBr zNkWlSNoZ?O=9SoadWTPpSNM(8?x%!GdsceAj=E^GF&Q{h^6w-x2ll&gM{0A8;^=zh z@1mz0u8~$*dEIsK{Ay|7T-UGA3hPnZj^bl~oL20gSP(GoI@gww68*H4dcpa;_%Jp;K0Fe+@gfo-0m}w3MdJ zTpfqFINfl<_@RwaDV_H*Jx>BNHN~I=&%{A^ZgBx#p-wyZ`Yw2064Kv8)0Qji4m8BykOs z4eq4;TuK2YOS#uprFtd;gh-W+r%Ehu)5Gfg{_^`3-BdfcoHdjCFZe>`QF3ch8?v4% zK`VNfS%-Ft)iP*8QdP8v3p>PUbXfA7(gMsP>vTq-qwJU`3ONmqhmyF;8jH+XVHlRd z)PNH&svzeK*NUd5*v^hicTu{q=f*5>p%KJJ1nWS#<9Hn{!^@^AV5p73p)^FWTzP7Z zzMu{-OIb{n7gr7rQDs-2D>Vy+HG3!LIO~ZNuzz}EzPQD&k_9{@K-E`6_V^&yF zQB`(bEri1#6)o5gRMgTi!!=EqFaCjyfM=(p1CoU(E z-9X`a$Gr+~&ldSVWcwnVuw+{AT(HnRSo_h(odIeYE*!7sixTdqgjz<|-3$j2dK2kH z6bf3R4pEbJRU2Uc&j zJ^IvX+xV2w8gutpOv@3M@(a1DEG#y>lJ5K2RiJ^S=qGcCU|nwkLp z9_RBSh7IjjSr#2HpdsN-vpgIrg`H0Cm@zJzfGKlPyNUBbrh?z~+IQRgelu}<2q-1m zT3-9ta1C$Z8>&+yFkd^}6CoGe>#2mMQe(#4W(9*JQ8dw?#DP1Gg?Rpif!Lzd2A5U2 z)cJvStRiJLbrHKCCIfx~VK2DzpsfSZXisY`-cN}+nIGlS?Z?;C zP8s_WP26C*q69vM$5Eskr`?mex_^XfKaFzG4tI_?u2$u?@MZ(I)8}Cloh*Gg7)-Um zAR+m$9(+xkiIW$t)<<$DPn;!LjyvpB*C)u~&r+l{Wb#qzHH|w=fE5tlTF2R8%pwJK znM)V3GmWzD{$`O%8B7!uk=cC&=G;ngDwL%w_S?-@mi^x!?dJldXFODc5+CI!*zj*3 z%9{HaFFG>8lxp96zf8%zRVmN$UEOZPCsG#lMqDj@XTY5ai~YV49McrN!f}u@@3?#B zZ!aw2a|de_0_r&YB)2t8#xrl}*ZrM7Bts$LDdO$YcFVUWq_B;55cpWxu>?qUJRyq7 zlhC6Vls+TVMkHy*(+QYi6dhVUDb!Q&noOSAtiCpn=A~IspJVcb>Q`13R{mbwjp5tm zlu_+u0%YY;;{<-H zR4o(1RyDENxKZg*ZK|r1bw|@#ywq)?#31ArjoWh5+L@9Oe03$ACLfhu+xZRiH^VjZ z7*mn<7CI~KaByc0&MxIkKp&%m{t5m4X2*i%?*5j;DNi6X1wyA4iC-!(0K*`AcN=7w zLjgI35DtuMm7hL$FMzFAQSKWv&2;MORufOM%Vags9>e)fb$s`u&zg*N8>MD?9wSHj z6?!UzBr!&q{KUvL=T{TSf65zv{^Vo)cac#%th7FkF}vmYOsDsaVoS&q5eD{$0(K)#bFmA3J=9S&9=xOAb8;>F93@6j3X4zT z9=1UOH_*+JqkYa~%R+?t0{co(8PY88YfkL2=#G4h9W7)U{-pn1)l8q)qkSOWZc0$r zJh#KtlaT;9mC_#{M4d0)%l&m;?%5YZ2ew$)>`@;6`S05biYpAeyH>}1+jKdxXEVpT z^=4pznM*Rj_=X5*L(&!FP9P5Ri{+-H05zFBcR}qY{tO0y>Xw~bZ`kM*`R9JPY^@5_ zqzHn47jXJyR7wAL~yOK5@dL7o;|Ct-l+H`Ek9o z3aGSrJNc1~6A)3Fe)z-<^xT?sm}*u~fbLBENy(t=Q(u7oK_C7NDDf(`P?8OfQzDPwXIVg3c^m#I zFww^k>m4WTA94chFVPJ}L7`MtZxLU_3wt*%fBQ`x#EN4zXOhK|QCrpU z`_ + issues are entered in the changelog (clear the label when done). + - ``git add`` and ``git commit``, ``git push``. **Ensure CI passes**. (If it + fails due to a known flake issue, either ignore or restart CI.) +- Add a release branch if this is a new minor version, or update the existing release branch if it is a patch version + - New branch: ``git checkout -b vX.Y``, ``git push -u origin vX.Y`` + - Update branch: ``git checkout vX.Y``, ``git merge ``, ``git push`` - Update tags (optional; if you skip this, the GitHub release makes a - non-annotated tag for you) - - ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``. - - ``git push --tags``. + non-annotated tag for you) + - ``git tag -a vX.Y.Z -m 'vX.Y.Z release'``. + - ``git push --tags``. - Update stable - ``git checkout stable`` - ``git merge master`` @@ -42,25 +53,36 @@ To release a new version of pybind11: - Make a GitHub release (this shows up in the UI, sends new release notifications to users watching releases, and also uploads PyPI packages). (Note: if you do not use an existing tag, this creates a new lightweight tag - for you, so you could skip the above step). - - GUI method: click "Create a new release" on the far right, fill in the tag - name (if you didn't tag above, it will be made here), fill in a release - name like "Version X.Y.Z", and optionally copy-and-paste the changelog into - the description (processed as markdown by Pandoc). Check "pre-release" if - this is a beta/RC. - - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` - If this is a pre-release, add ``-p``. + for you, so you could skip the above step.) + - GUI method: Under `releases `_ + click "Draft a new release" on the far right, fill in the tag name + (if you didn't tag above, it will be made here), fill in a release name + like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog + into the description (usually ``cat docs/changelog.rst | pandoc -f rst -t gfm``). + Check "pre-release" if this is a beta/RC. + - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` + If this is a pre-release, add ``-p``. - Get back to work - - Make sure you are on master, not somewhere else: ``git checkout master`` - - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to - ``0.dev1`` and increment MINOR). - - Update ``_version.py`` to match - - Add a plot for in-development updates in ``docs/changelog.rst``. - - ``git add``, ``git commit``, ``git push`` + - Make sure you are on master, not somewhere else: ``git checkout master`` + - Update version macros in ``include/pybind11/detail/common.h`` (set PATCH to + ``0.dev1`` and increment MINOR). + - Update ``_version.py`` to match + - Run ``nox -s tests_packaging`` to ensure this was done correctly. + - Add a spot for in-development updates in ``docs/changelog.rst``. + - ``git add``, ``git commit``, ``git push`` If a version branch is updated, remember to set PATCH to ``1.dev1``. +If you'd like to bump homebrew, run: + +.. code-block:: console + + brew bump-formula-pr --url https://github.com/pybind/pybind11/archive/vX.Y.Z.tar.gz + +Conda-forge should automatically make a PR in a few hours, and automatically +merge it if there are no issues. + Manual packaging ^^^^^^^^^^^^^^^^ @@ -69,9 +91,7 @@ If you need to manually upload releases, you can download the releases from the .. code-block:: bash - python3 -m pip install build - python3 -m build - PYBIND11_SDIST_GLOBAL=1 python3 -m build + nox -s build twine upload dist/* This makes SDists and wheels, and the final line uploads them. diff --git a/3rdparty/pybind11/docs/requirements.txt b/3rdparty/pybind11/docs/requirements.txt index 35366e3c8a..d2a9ae1645 100644 --- a/3rdparty/pybind11/docs/requirements.txt +++ b/3rdparty/pybind11/docs/requirements.txt @@ -1,7 +1,6 @@ -breathe==4.20.0 -commonmark==0.9.1 -recommonmark==0.6.0 -sphinx==3.2.1 -sphinx_rtd_theme==0.5.0 -sphinxcontrib-moderncmakedomain==3.13 -sphinxcontrib-svg2pdfconverter==1.1.0 +breathe==4.34.0 +furo==2022.6.21 +sphinx==5.0.2 +sphinx-copybutton==0.5.0 +sphinxcontrib-moderncmakedomain==3.21.4 +sphinxcontrib-svg2pdfconverter==1.2.0 diff --git a/3rdparty/pybind11/docs/upgrade.rst b/3rdparty/pybind11/docs/upgrade.rst index 87bcebee2c..6a9db2d08f 100644 --- a/3rdparty/pybind11/docs/upgrade.rst +++ b/3rdparty/pybind11/docs/upgrade.rst @@ -8,6 +8,46 @@ to a new version. But it goes into more detail. This includes things like deprecated APIs and their replacements, build system changes, general code modernization and other useful information. +.. _upgrade-guide-2.9: + +v2.9 +==== + +* Any usage of the recently added ``py::make_simple_namespace`` should be + converted to using ``py::module_::import("types").attr("SimpleNamespace")`` + instead. + +* The use of ``_`` in custom type casters can now be replaced with the more + readable ``const_name`` instead. The old ``_`` shortcut has been retained + unless it is being used as a macro (like for gettext). + + +.. _upgrade-guide-2.7: + +v2.7 +==== + +*Before* v2.7, ``py::str`` can hold ``PyUnicodeObject`` or ``PyBytesObject``, +and ``py::isinstance()`` is ``true`` for both ``py::str`` and +``py::bytes``. Starting with v2.7, ``py::str`` exclusively holds +``PyUnicodeObject`` (`#2409 `_), +and ``py::isinstance()`` is ``true`` only for ``py::str``. To help in +the transition of user code, the ``PYBIND11_STR_LEGACY_PERMISSIVE`` macro +is provided as an escape hatch to go back to the legacy behavior. This macro +will be removed in future releases. Two types of required fixes are expected +to be common: + +* Accidental use of ``py::str`` instead of ``py::bytes``, masked by the legacy + behavior. These are probably very easy to fix, by changing from + ``py::str`` to ``py::bytes``. + +* Reliance on py::isinstance(obj) being ``true`` for + ``py::bytes``. This is likely to be easy to fix in most cases by adding + ``|| py::isinstance(obj)``, but a fix may be more involved, e.g. if + ``py::isinstance`` appears in a template. Such situations will require + careful review and custom fixes. + + .. _upgrade-guide-2.6: v2.6 @@ -192,7 +232,7 @@ way to get and set object state. See :ref:`pickling` for details. ... .def(py::pickle( [](const Foo &self) { // __getstate__ - return py::make_tuple(f.value1(), f.value2(), ...); // unchanged + return py::make_tuple(self.value1(), self.value2(), ...); // unchanged }, [](py::tuple t) { // __setstate__, note: no `self` argument return new Foo(t[0].cast(), ...); @@ -256,7 +296,7 @@ Within pybind11's CMake build system, ``pybind11_add_module`` has always been setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's being applied unconditionally, even in debug mode and it can no longer be opted out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also -adds this flag to it's interface. The ``pybind11::embed`` target is unchanged. +adds this flag to its interface. The ``pybind11::embed`` target is unchanged. The most significant change here is for the ``pybind11::module`` target. If you were previously relying on default visibility, i.e. if your Python module was @@ -484,7 +524,7 @@ include a declaration of the form: PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) -Continuing to do so won’t cause an error or even a deprecation warning, +Continuing to do so won't cause an error or even a deprecation warning, but it's completely redundant. diff --git a/3rdparty/pybind11/include/pybind11/attr.h b/3rdparty/pybind11/include/pybind11/attr.h index 0c41670926..db7cd8efff 100644 --- a/3rdparty/pybind11/include/pybind11/attr.h +++ b/3rdparty/pybind11/include/pybind11/attr.h @@ -10,72 +10,113 @@ #pragma once +#include "detail/common.h" #include "cast.h" +#include + PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// \addtogroup annotations /// @{ /// Annotation for methods -struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; +struct is_method { + handle class_; + explicit is_method(const handle &c) : class_(c) {} +}; /// Annotation for operators -struct is_operator { }; +struct is_operator {}; /// Annotation for classes that cannot be subclassed -struct is_final { }; +struct is_final {}; /// Annotation for parent scope -struct scope { handle value; scope(const handle &s) : value(s) { } }; +struct scope { + handle value; + explicit scope(const handle &s) : value(s) {} +}; /// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; +struct doc { + const char *value; + explicit doc(const char *value) : value(value) {} +}; /// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; +struct name { + const char *value; + explicit name(const char *value) : value(value) {} +}; /// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; +struct sibling { + handle value; + explicit sibling(const handle &value) : value(value.ptr()) {} +}; /// Annotation indicating that a class derives from another given type -template struct base { +template +struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + PYBIND11_DEPRECATED( + "base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() = default; }; /// Keep patient alive while nurse lives -template struct keep_alive { }; +template +struct keep_alive {}; /// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; +struct multiple_inheritance {}; /// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; +struct dynamic_attr {}; /// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; +struct buffer_protocol {}; /// Annotation which requests that a special metaclass is created for a type struct metaclass { handle value; PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + metaclass() = default; /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } + explicit metaclass(handle value) : value(value) {} +}; + +/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that +/// may be used to customize the Python type. +/// +/// The callback is invoked immediately before `PyType_Ready`. +/// +/// Note: This is an advanced interface, and uses of it may require changes to +/// work with later versions of pybind11. You may wish to consult the +/// implementation of `make_new_python_type` in `detail/classes.h` to understand +/// the context in which the callback will be run. +struct custom_type_setup { + using callback = std::function; + + explicit custom_type_setup(callback value) : value(std::move(value)) {} + + callback value; }; /// Annotation that marks a class as local to the module: -struct module_local { const bool value; constexpr module_local(bool v = true) : value(v) { } }; +struct module_local { + const bool value; + constexpr explicit module_local(bool v = true) : value(v) {} +}; /// Annotation to mark enums as an arithmetic type -struct arithmetic { }; +struct arithmetic {}; /// Mark a function for addition at the beginning of the existing overload chain instead of the end -struct prepend { }; +struct prepend {}; /** \rst A call policy which places one or more guard variables (``Ts...``) around the function call. @@ -95,9 +136,13 @@ struct prepend { }; return foo(args...); // forwarded arguments }); \endrst */ -template struct call_guard; +template +struct call_guard; -template <> struct call_guard<> { using type = detail::void_type; }; +template <> +struct call_guard<> { + using type = detail::void_type; +}; template struct call_guard { @@ -122,8 +167,9 @@ PYBIND11_NAMESPACE_BEGIN(detail) enum op_id : int; enum op_type : int; struct undefined_t; -template struct op_; -inline void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); +template +struct op_; +void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); /// Internal data structure which holds metadata about a keyword argument struct argument_record { @@ -134,15 +180,16 @@ struct argument_record { bool none : 1; ///< True if None is allowed when loading argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } + : name(name), descr(descr), value(value), convert(convert), none(none) {} }; -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +/// Internal data structure which holds metadata about a bound function (signature, overloads, +/// etc.) struct function_record { function_record() : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), is_method(false), has_args(false), - has_kwargs(false), has_kw_only_args(false), prepend(false) { } + is_operator(false), is_method(false), has_args(false), has_kwargs(false), + prepend(false) {} /// Function name char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ @@ -157,13 +204,13 @@ struct function_record { std::vector args; /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; + handle (*impl)(function_call &) = nullptr; /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; + void *data[3] = {}; /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; + void (*free_data)(function_record *ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; @@ -189,17 +236,15 @@ struct function_record { /// True if the function has a '**kwargs' argument bool has_kwargs : 1; - /// True once a 'py::kw_only' is encountered (any following args are keyword-only) - bool has_kw_only_args : 1; - /// True if this function is to be inserted at the beginning of the overload resolution chain bool prepend : 1; /// Number of arguments (including py::args and/or py::kwargs, if present) std::uint16_t nargs; - /// Number of trailing arguments (counted in `nargs`) that are keyword-only - std::uint16_t nargs_kw_only = 0; + /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs + /// argument or by a py::kw_only annotation. + std::uint16_t nargs_pos = 0; /// Number of leading arguments (counted in `nargs`) that are positional-only std::uint16_t nargs_pos_only = 0; @@ -221,7 +266,7 @@ struct function_record { struct type_record { PYBIND11_NOINLINE type_record() : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - default_holder(true), module_local(false), is_final(false) { } + default_holder(true), module_local(false), is_final(false) {} /// Handle to the parent scope handle scope; @@ -259,6 +304,9 @@ struct type_record { /// Custom metaclass (optional) handle metaclass; + /// Custom type setup. + custom_type_setup::callback custom_type_setup_callback; + /// Multiple inheritance marker bool multiple_inheritance : 1; @@ -277,42 +325,45 @@ struct type_record { /// Is the class inheritable from python classes? bool is_final : 1; - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { + auto *base_info = detail::get_type_info(base, false); if (!base_info) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); } if (default_holder != base_info->default_holder) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); } bases.append((PyObject *) base_info->type); - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; +#if PY_VERSION_HEX < 0x030B0000 + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif - if (caster) + if (caster) { base_info->implicit_casts.emplace_back(type, caster); + } } }; -inline function_call::function_call(const function_record &f, handle p) : - func(f), parent(p) { +inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { args.reserve(f.nargs); args_convert.reserve(f.nargs); } /// Tag for a new-style `__init__` defined in `detail/init.h` -struct is_new_style_constructor { }; +struct is_new_style_constructor {}; /** * Partial template specializations to process custom attributes provided to @@ -320,129 +371,177 @@ struct is_new_style_constructor { }; * fields in the type_record and function_record data structures or executed at * runtime to deal with custom call policies (e.g. keep_alive). */ -template struct process_attribute; +template +struct process_attribute; -template struct process_attribute_default { +template +struct process_attribute_default { /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } + static void init(const T &, function_record *) {} + static void init(const T &, type_record *) {} + static void precall(function_call &) {} + static void postcall(function_call &, handle) {} }; /// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const char *d, function_record *r) { r->doc = const_cast(d); } static void init(const char *d, type_record *r) { r->doc = const_cast(d); } }; -template <> struct process_attribute : process_attribute { }; +template <> +struct process_attribute : process_attribute {}; /// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const return_value_policy &p, function_record *r) { r->policy = p; } }; -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { +/// Process an attribute which indicates that this is an overloaded function associated with a +/// given sibling +template <> +struct process_attribute : process_attribute_default { static void init(const sibling &s, function_record *r) { r->sibling = s.value; } }; /// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +template <> +struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { + r->is_method = true; + r->scope = s.class_; + } }; /// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const scope &s, function_record *r) { r->scope = s.value; } }; /// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const is_operator &, function_record *r) { r->is_operator = true; } }; -template <> struct process_attribute : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +template <> +struct process_attribute + : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { + r->is_new_style_constructor = true; + } }; -inline void process_kw_only_arg(const arg &a, function_record *r) { - if (!a.name || strlen(a.name) == 0) - pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); - ++r->nargs_kw_only; +inline void check_kw_only_arg(const arg &a, function_record *r) { + if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { + pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); + } +} + +inline void append_self_arg_if_needed(function_record *r) { + if (r->is_method && r->args.empty()) { + r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); + } } /// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + append_self_arg_if_needed(r); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kw_only_arg(a, r); + check_kw_only_arg(a, r); } }; /// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + if (r->is_method && r->args.empty()) { + r->args.emplace_back( + "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); + } if (!a.value) { -#if !defined(NDEBUG) +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; + if (a.name) { + descr += std::string(a.name) + ": "; + } descr += a.type + "'"; if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else + if (r->name) { + descr += " in method '" + (std::string) str(r->scope) + "." + + (std::string) r->name + "'"; + } else { descr += " in method of '" + (std::string) str(r->scope) + "'"; + } } else if (r->name) { descr += " in function '" + (std::string) r->name + "'"; } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); + pybind11_fail("arg(): could not convert default argument " + descr + + " into a Python object (type not registered yet?)"); #else pybind11_fail("arg(): could not convert default argument " "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " + "more information."); #endif } r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kw_only_arg(a, r); + check_kw_only_arg(a, r); } }; /// Process a keyword-only-arguments-follow pseudo argument -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const kw_only &, function_record *r) { - r->has_kw_only_args = true; + append_self_arg_if_needed(r); + if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { + pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " + "argument location (or omit kw_only() entirely)"); + } + r->nargs_pos = static_cast(r->args.size()); } }; /// Process a positional-only-argument maker -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const pos_only &, function_record *r) { + append_self_arg_if_needed(r); r->nargs_pos_only = static_cast(r->args.size()); + if (r->nargs_pos_only > r->nargs_pos) { + pybind11_fail("pos_only(): cannot follow a py::args() argument"); + } + // It also can't follow a kw_only, but a static_assert in pybind11.h checks that } }; -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees +/// that) template -struct process_attribute::value>> : process_attribute_default { +struct process_attribute::value>> + : process_attribute_default { static void init(const handle &h, type_record *r) { r->bases.append(h); } }; @@ -455,7 +554,9 @@ struct process_attribute> : process_attribute_default> { /// Process a multiple inheritance attribute template <> struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } + static void init(const multiple_inheritance &, type_record *r) { + r->multiple_inheritance = true; + } }; template <> @@ -463,6 +564,13 @@ struct process_attribute : process_attribute_default static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } }; +template <> +struct process_attribute { + static void init(const custom_type_setup &value, type_record *r) { + r->custom_type_setup_callback = value.value; + } +}; + template <> struct process_attribute : process_attribute_default { static void init(const is_final &, type_record *r) { r->is_final = true; } @@ -494,41 +602,59 @@ template <> struct process_attribute : process_attribute_default {}; template -struct process_attribute> : process_attribute_default> { }; +struct process_attribute> : process_attribute_default> {}; /** * Process a keep_alive call policy -- invokes keep_alive_impl during the * pre-call handler if both Nurse, Patient != 0 and use the post-call handler * otherwise */ -template struct process_attribute> : public process_attribute_default> { +template +struct process_attribute> + : public process_attribute_default> { template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + static void precall(function_call &call) { + keep_alive_impl(Nurse, Patient, call, handle()); + } template = 0> - static void postcall(function_call &, handle) { } + static void postcall(function_call &, handle) {} template = 0> - static void precall(function_call &) { } + static void precall(function_call &) {} template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } + static void postcall(function_call &call, handle ret) { + keep_alive_impl(Nurse, Patient, call, ret); + } }; /// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); +template +struct process_attributes { + static void init(const Args &...args, function_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{ + 0, ((void) process_attribute::type>::init(args, r), 0)...}; } - static void init(const Args&... args, type_record *r) { - int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; - ignore_unused(unused); + static void init(const Args &...args, type_record *r) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::init(args, r), 0)...}; } static void precall(function_call &call) { - int unused[] = { 0, (process_attribute::type>::precall(call), 0) ... }; - ignore_unused(unused); + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); + using expander = int[]; + (void) expander{0, + (process_attribute::type>::precall(call), 0)...}; } static void postcall(function_call &call, handle fn_ret) { - int unused[] = { 0, (process_attribute::type>::postcall(call, fn_ret), 0) ... }; - ignore_unused(unused); + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); + using expander = int[]; + (void) expander{ + 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; } }; @@ -542,9 +668,10 @@ using extract_guard_t = typename exactly_one_t, Extr /// Check the number of named arguments at compile time template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> + size_t self = constexpr_sum(std::is_same::value...)> constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - return named == 0 || (self + named + has_args + has_kwargs) == nargs; + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); + return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; } PYBIND11_NAMESPACE_END(detail) diff --git a/3rdparty/pybind11/include/pybind11/buffer_info.h b/3rdparty/pybind11/include/pybind11/buffer_info.h index d803004a10..06120d5563 100644 --- a/3rdparty/pybind11/include/pybind11/buffer_info.h +++ b/3rdparty/pybind11/include/pybind11/buffer_info.h @@ -19,9 +19,11 @@ PYBIND11_NAMESPACE_BEGIN(detail) inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { auto ndim = shape.size(); std::vector strides(ndim, itemsize); - if (ndim > 0) - for (size_t i = ndim - 1; i > 0; --i) + if (ndim > 0) { + for (size_t i = ndim - 1; i > 0; --i) { strides[i - 1] = strides[i] * shape[i]; + } + } return strides; } @@ -29,8 +31,9 @@ inline std::vector c_strides(const std::vector &shape, ssize_t inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { auto ndim = shape.size(); std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) + for (size_t i = 1; i < ndim; ++i) { strides[i] = strides[i - 1] * shape[i - 1]; + } return strides; } @@ -41,61 +44,89 @@ struct buffer_info { void *ptr = nullptr; // Pointer to the underlying storage ssize_t itemsize = 0; // Size of individual items in bytes ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + std::string format; // For homogeneous buffers, this should be set to + // format_descriptor::format() ssize_t ndim = 0; // Number of dimensions std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of bytes between adjacent entries (for each per dimension) + std::vector strides; // Number of bytes between adjacent entries + // (for each per dimension) bool readonly = false; // flag to indicate if the underlying storage may be written to buffer_info() = default; - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) + } + for (size_t i = 0; i < (size_t) ndim; ++i) { size *= shape[i]; + } } template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } - - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } + buffer_info(T *ptr, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : buffer_info(private_ctr_tag(), + ptr, + sizeof(T), + format_descriptor::format(), + static_cast(shape_in->size()), + std::move(shape_in), + std::move(strides_in), + readonly) {} + + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t size, + bool readonly = false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} template - buffer_info(T *ptr, ssize_t size, bool readonly=false) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } + buffer_info(T *ptr, ssize_t size, bool readonly = false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) {} template - buffer_info(const T *ptr, ssize_t size, bool readonly=true) - : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) { } + buffer_info(const T *ptr, ssize_t size, bool readonly = true) + : buffer_info( + const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) {} explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + : buffer_info( + view->buf, + view->itemsize, + view->format, + view->ndim, {view->shape, view->shape + view->ndim}, /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects * ignore this flag and return a view with NULL strides. * When strides are NULL, build them manually. */ view->strides - ? std::vector(view->strides, view->strides + view->ndim) - : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), - view->readonly) { + ? std::vector(view->strides, view->strides + view->ndim) + : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), + (view->readonly != 0)) { + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->m_view = view; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->ownview = ownview; } buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; + buffer_info &operator=(const buffer_info &) = delete; - buffer_info(buffer_info &&other) { - (*this) = std::move(other); - } + buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } - buffer_info& operator=(buffer_info &&rhs) { + buffer_info &operator=(buffer_info &&rhs) noexcept { ptr = rhs.ptr; itemsize = rhs.itemsize; size = rhs.size; @@ -110,17 +141,28 @@ struct buffer_info { } ~buffer_info() { - if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } + if (m_view && ownview) { + PyBuffer_Release(m_view); + delete m_view; + } } Py_buffer *view() const { return m_view; } Py_buffer *&view() { return m_view; } -private: - struct private_ctr_tag { }; - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } +private: + struct private_ctr_tag {}; + + buffer_info(private_ctr_tag, + void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container &&shape_in, + detail::any_container &&strides_in, + bool readonly) + : buffer_info( + ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} Py_buffer *m_view = nullptr; bool ownview = false; @@ -128,17 +170,22 @@ struct buffer_info { PYBIND11_NAMESPACE_BEGIN(detail) -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { +template +struct compare_buffer_info { + static bool compare(const buffer_info &b) { return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); } }; -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info &b) { + return (size_t) b.itemsize == sizeof(T) + && (b.format == format_descriptor::value + || ((sizeof(T) == sizeof(long)) + && b.format == (std::is_unsigned::value ? "L" : "l")) + || ((sizeof(T) == sizeof(size_t)) + && b.format == (std::is_unsigned::value ? "N" : "n"))); } }; diff --git a/3rdparty/pybind11/include/pybind11/cast.h b/3rdparty/pybind11/include/pybind11/cast.h index 11c61a441f..a0e32281bc 100644 --- a/3rdparty/pybind11/include/pybind11/cast.h +++ b/3rdparty/pybind11/include/pybind11/cast.h @@ -10,1029 +10,168 @@ #pragma once -#include "pytypes.h" -#include "detail/typeid.h" +#include "detail/common.h" #include "detail/descr.h" -#include "detail/internals.h" +#include "detail/type_caster_base.h" +#include "detail/typeid.h" +#include "pytypes.h" + #include -#include +#include +#include +#include +#include +#include +#include #include #include - -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L -# define PYBIND11_HAS_U8STRING -#endif +#include +#include PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) -/// A life support system for temporary objects created by `type_caster::load()`. -/// Adding a patient will keep it alive up until the enclosing function returns. -class loader_life_support { -public: - /// A new patient frame is created when a function is entered - loader_life_support() { - get_internals().loader_patient_stack.push_back(nullptr); - } - - /// ... and destroyed after it returns - ~loader_life_support() { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - pybind11_fail("loader_life_support: internal error"); - - auto ptr = stack.back(); - stack.pop_back(); - Py_CLEAR(ptr); - - // A heuristic to reduce the stack's capacity (e.g. after long recursive calls) - if (stack.capacity() > 16 && !stack.empty() && stack.capacity() / stack.size() > 2) - stack.shrink_to_fit(); - } - - /// This can only be used inside a pybind11-bound function, either by `argument_loader` - /// at argument preparation time or by `py::cast()` at execution time. - PYBIND11_NOINLINE static void add_patient(handle h) { - auto &stack = get_internals().loader_patient_stack; - if (stack.empty()) - throw cast_error("When called outside a bound function, py::cast() cannot " - "do Python -> C++ conversions which require the creation " - "of temporary values"); - - auto &list_ptr = stack.back(); - if (list_ptr == nullptr) { - list_ptr = PyList_New(1); - if (!list_ptr) - pybind11_fail("loader_life_support: error allocating list"); - PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr()); - } else { - auto result = PyList_Append(list_ptr, h.ptr()); - if (result == -1) - pybind11_fail("loader_life_support: error adding patient"); - } - } -}; - -// Gets the cache entry for the given type, creating it if necessary. The return value is the pair -// returned by emplace, i.e. an iterator for the entry and a bool set to `true` if the entry was -// just created. -inline std::pair all_type_info_get_cache(PyTypeObject *type); - -// Populates a just-created cache entry. -PYBIND11_NOINLINE inline void all_type_info_populate(PyTypeObject *t, std::vector &bases) { - std::vector check; - for (handle parent : reinterpret_borrow(t->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - - auto const &type_dict = get_internals().registered_types_py; - for (size_t i = 0; i < check.size(); i++) { - auto type = check[i]; - // Ignore Python2 old-style class super type: - if (!PyType_Check((PyObject *) type)) continue; - - // Check `type` in the current set of registered python types: - auto it = type_dict.find(type); - if (it != type_dict.end()) { - // We found a cache entry for it, so it's either pybind-registered or has pre-computed - // pybind bases, but we have to make sure we haven't already seen the type(s) before: we - // want to follow Python/virtual C++ rules that there should only be one instance of a - // common base. - for (auto *tinfo : it->second) { - // NB: Could use a second set here, rather than doing a linear search, but since - // having a large number of immediate pybind11-registered types seems fairly - // unlikely, that probably isn't worthwhile. - bool found = false; - for (auto *known : bases) { - if (known == tinfo) { found = true; break; } - } - if (!found) bases.push_back(tinfo); - } - } - else if (type->tp_bases) { - // It's some python type, so keep follow its bases classes to look for one or more - // registered types - if (i + 1 == check.size()) { - // When we're at the end, we can pop off the current element to avoid growing - // `check` when adding just one base (which is typical--i.e. when there is no - // multiple inheritance) - check.pop_back(); - i--; - } - for (handle parent : reinterpret_borrow(type->tp_bases)) - check.push_back((PyTypeObject *) parent.ptr()); - } - } -} - -/** - * Extracts vector of type_info pointers of pybind-registered roots of the given Python type. Will - * be just 1 pybind type for the Python type of a pybind-registered class, or for any Python-side - * derived class that uses single inheritance. Will contain as many types as required for a Python - * class that uses multiple inheritance to inherit (directly or indirectly) from multiple - * pybind-registered classes. Will be empty if neither the type nor any base classes are - * pybind-registered. - * - * The value is cached for the lifetime of the Python type. - */ -inline const std::vector &all_type_info(PyTypeObject *type) { - auto ins = all_type_info_get_cache(type); - if (ins.second) - // New cache entry: populate it - all_type_info_populate(type, ins.first->second); - - return ins.first->second; -} - -/** - * Gets a single pybind11 type info for a python type. Returns nullptr if neither the type nor any - * ancestors are pybind11-registered. Throws an exception if there are multiple bases--use - * `all_type_info` instead if you want to support multiple bases. - */ -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto &bases = all_type_info(type); - if (bases.empty()) - return nullptr; - if (bases.size() > 1) - pybind11_fail("pybind11::detail::get_type_info: type has multiple pybind11-registered bases"); - return bases.front(); -} - -inline detail::type_info *get_local_type_info(const std::type_index &tp) { - auto &locals = registered_local_types_cpp(); - auto it = locals.find(tp); - if (it != locals.end()) - return it->second; - return nullptr; -} - -inline detail::type_info *get_global_type_info(const std::type_index &tp) { - auto &types = get_internals().registered_types_cpp; - auto it = types.find(tp); - if (it != types.end()) - return it->second; - return nullptr; -} - -/// Return the type info for a given C++ type; on lookup failure can either throw or return nullptr. -PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_index &tp, - bool throw_if_missing = false) { - if (auto ltype = get_local_type_info(tp)) - return ltype; - if (auto gtype = get_global_type_info(tp)) - return gtype; - - if (throw_if_missing) { - std::string tname = tp.name(); - detail::clean_type_id(tname); - pybind11_fail("pybind11::detail::get_type_info: unable to find type info for \"" + tname + "\""); - } - return nullptr; -} - -PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool throw_if_missing) { - detail::type_info *type_info = get_type_info(tp, throw_if_missing); - return handle(type_info ? ((PyObject *) type_info->type) : nullptr); -} - -struct value_and_holder { - instance *inst = nullptr; - size_t index = 0u; - const detail::type_info *type = nullptr; - void **vh = nullptr; - - // Main constructor for a found value/holder: - value_and_holder(instance *i, const detail::type_info *type, size_t vpos, size_t index) : - inst{i}, index{index}, type{type}, - vh{inst->simple_layout ? inst->simple_value_holder : &inst->nonsimple.values_and_holders[vpos]} - {} - - // Default constructor (used to signal a value-and-holder not found by get_value_and_holder()) - value_and_holder() = default; - - // Used for past-the-end iterator - value_and_holder(size_t index) : index{index} {} - - template V *&value_ptr() const { - return reinterpret_cast(vh[0]); - } - // True if this `value_and_holder` has a non-null value pointer - explicit operator bool() const { return value_ptr(); } - - template H &holder() const { - return reinterpret_cast(vh[1]); - } - bool holder_constructed() const { - return inst->simple_layout - ? inst->simple_holder_constructed - : inst->nonsimple.status[index] & instance::status_holder_constructed; - } - void set_holder_constructed(bool v = true) { - if (inst->simple_layout) - inst->simple_holder_constructed = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_holder_constructed; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_holder_constructed; - } - bool instance_registered() const { - return inst->simple_layout - ? inst->simple_instance_registered - : inst->nonsimple.status[index] & instance::status_instance_registered; - } - void set_instance_registered(bool v = true) { - if (inst->simple_layout) - inst->simple_instance_registered = v; - else if (v) - inst->nonsimple.status[index] |= instance::status_instance_registered; - else - inst->nonsimple.status[index] &= (uint8_t) ~instance::status_instance_registered; - } -}; - -// Container for accessing and iterating over an instance's values/holders -struct values_and_holders { -private: - instance *inst; - using type_vec = std::vector; - const type_vec &tinfo; - -public: - values_and_holders(instance *inst) : inst{inst}, tinfo(all_type_info(Py_TYPE(inst))) {} - - struct iterator { - private: - instance *inst = nullptr; - const type_vec *types = nullptr; - value_and_holder curr; - friend struct values_and_holders; - iterator(instance *inst, const type_vec *tinfo) - : inst{inst}, types{tinfo}, - curr(inst /* instance */, - types->empty() ? nullptr : (*types)[0] /* type info */, - 0, /* vpos: (non-simple types only): the first vptr comes first */ - 0 /* index */) - {} - // Past-the-end iterator: - iterator(size_t end) : curr(end) {} - public: - bool operator==(const iterator &other) const { return curr.index == other.curr.index; } - bool operator!=(const iterator &other) const { return curr.index != other.curr.index; } - iterator &operator++() { - if (!inst->simple_layout) - curr.vh += 1 + (*types)[curr.index]->holder_size_in_ptrs; - ++curr.index; - curr.type = curr.index < types->size() ? (*types)[curr.index] : nullptr; - return *this; - } - value_and_holder &operator*() { return curr; } - value_and_holder *operator->() { return &curr; } - }; - - iterator begin() { return iterator(inst, &tinfo); } - iterator end() { return iterator(tinfo.size()); } - - iterator find(const type_info *find_type) { - auto it = begin(), endit = end(); - while (it != endit && it->type != find_type) ++it; - return it; - } - - size_t size() { return tinfo.size(); } -}; - -/** - * Extracts C++ value and holder pointer references from an instance (which may contain multiple - * values/holders for python-side multiple inheritance) that match the given type. Throws an error - * if the given type (or ValueType, if omitted) is not a pybind11 base of the given instance. If - * `find_type` is omitted (or explicitly specified as nullptr) the first value/holder are returned, - * regardless of type (and the resulting .type will be nullptr). - * - * The returned object should be short-lived: in particular, it must not outlive the called-upon - * instance. - */ -PYBIND11_NOINLINE inline value_and_holder instance::get_value_and_holder(const type_info *find_type /*= nullptr default in common.h*/, bool throw_if_missing /*= true in common.h*/) { - // Optimize common case: - if (!find_type || Py_TYPE(this) == find_type->type) - return value_and_holder(this, find_type, 0, 0); - - detail::values_and_holders vhs(this); - auto it = vhs.find(find_type); - if (it != vhs.end()) - return *it; - - if (!throw_if_missing) - return value_and_holder(); - -#if defined(NDEBUG) - pybind11_fail("pybind11::detail::instance::get_value_and_holder: " - "type is not a pybind11 base of the given instance " - "(compile in debug mode for type details)"); -#else - pybind11_fail("pybind11::detail::instance::get_value_and_holder: `" + - get_fully_qualified_tp_name(find_type->type) + "' is not a pybind11 base of the given `" + - get_fully_qualified_tp_name(Py_TYPE(this)) + "' instance"); -#endif -} - -PYBIND11_NOINLINE inline void instance::allocate_layout() { - auto &tinfo = all_type_info(Py_TYPE(this)); - - const size_t n_types = tinfo.size(); - - if (n_types == 0) - pybind11_fail("instance allocation failed: new instance has no pybind11-registered base types"); - - simple_layout = - n_types == 1 && tinfo.front()->holder_size_in_ptrs <= instance_simple_holder_in_ptrs(); - - // Simple path: no python-side multiple inheritance, and a small-enough holder - if (simple_layout) { - simple_value_holder[0] = nullptr; - simple_holder_constructed = false; - simple_instance_registered = false; - } - else { // multiple base types or a too-large holder - // Allocate space to hold: [v1*][h1][v2*][h2]...[bb...] where [vN*] is a value pointer, - // [hN] is the (uninitialized) holder instance for value N, and [bb...] is a set of bool - // values that tracks whether each associated holder has been initialized. Each [block] is - // padded, if necessary, to an integer multiple of sizeof(void *). - size_t space = 0; - for (auto t : tinfo) { - space += 1; // value pointer - space += t->holder_size_in_ptrs; // holder instance - } - size_t flags_at = space; - space += size_in_ptrs(n_types); // status bytes (holder_constructed and instance_registered) - - // Allocate space for flags, values, and holders, and initialize it to 0 (flags and values, - // in particular, need to be 0). Use Python's memory allocation functions: in Python 3.6 - // they default to using pymalloc, which is designed to be efficient for small allocations - // like the one we're doing here; in earlier versions (and for larger allocations) they are - // just wrappers around malloc. -#if PY_VERSION_HEX >= 0x03050000 - nonsimple.values_and_holders = (void **) PyMem_Calloc(space, sizeof(void *)); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); -#else - nonsimple.values_and_holders = (void **) PyMem_New(void *, space); - if (!nonsimple.values_and_holders) throw std::bad_alloc(); - std::memset(nonsimple.values_and_holders, 0, space * sizeof(void *)); -#endif - nonsimple.status = reinterpret_cast(&nonsimple.values_and_holders[flags_at]); - } - owned = true; -} - -PYBIND11_NOINLINE inline void instance::deallocate_layout() { - if (!simple_layout) - PyMem_Free(nonsimple.values_and_holders); -} - -PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) { - handle type = detail::get_type_handle(tp, false); - if (!type) - return false; - return isinstance(obj, type); -} - -PYBIND11_NOINLINE inline std::string error_string() { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "Unknown internal error occurred"); - return "Unknown internal error occurred"; - } - - error_scope scope; // Preserve error state - - std::string errorString; - if (scope.type) { - errorString += handle(scope.type).attr("__name__").cast(); - errorString += ": "; - } - if (scope.value) - errorString += (std::string) str(scope.value); - - PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); - -#if PY_MAJOR_VERSION >= 3 - if (scope.trace != nullptr) - PyException_SetTraceback(scope.value, scope.trace); -#endif - -#if !defined(PYPY_VERSION) - if (scope.trace) { - auto *trace = (PyTracebackObject *) scope.trace; - - /* Get the deepest trace possible */ - while (trace->tb_next) - trace = trace->tb_next; - - PyFrameObject *frame = trace->tb_frame; - errorString += "\n\nAt:\n"; - while (frame) { - int lineno = PyFrame_GetLineNumber(frame); - errorString += - " " + handle(frame->f_code->co_filename).cast() + - "(" + std::to_string(lineno) + "): " + - handle(frame->f_code->co_name).cast() + "\n"; - frame = frame->f_back; - } - } -#endif - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail::type_info *type ) { - auto &instances = get_internals().registered_instances; - auto range = instances.equal_range(ptr); - for (auto it = range.first; it != range.second; ++it) { - for (const auto &vh : values_and_holders(it->second)) { - if (vh.type == type) - return handle((PyObject *) it->second); - } - } - return handle(); -} - -inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) - return PyThreadState_GET(); -#elif PY_VERSION_HEX < 0x03000000 - return _PyThreadState_Current; -#elif PY_VERSION_HEX < 0x03050000 - return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current); -#elif PY_VERSION_HEX < 0x03050200 - return (PyThreadState*) _PyThreadState_Current.value; -#else - return _PyThreadState_UncheckedGet(); -#endif -} - -// Forward declarations -inline void keep_alive_impl(handle nurse, handle patient); -inline PyObject *make_new_instance(PyTypeObject *type); - -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) - : typeinfo(get_type_info(type_info)), cpptype(&type_info) { } - - type_caster_generic(const type_info *typeinfo) - : typeinfo(typeinfo), cpptype(typeinfo ? typeinfo->cpptype : nullptr) { } - - bool load(handle src, bool convert) { - return load_impl(src, convert); - } - - PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, - const detail::type_info *tinfo, - void *(*copy_constructor)(const void *), - void *(*move_constructor)(const void *), - const void *existing_holder = nullptr) { - if (!tinfo) // no type info: error will be set already - return handle(); - - void *src = const_cast(_src); - if (src == nullptr) - return none().release(); - - auto it_instances = get_internals().registered_instances.equal_range(src); - for (auto it_i = it_instances.first; it_i != it_instances.second; ++it_i) { - for (auto instance_type : detail::all_type_info(Py_TYPE(it_i->second))) { - if (instance_type && same_type(*instance_type->cpptype, *tinfo->cpptype)) - return handle((PyObject *) it_i->second).inc_ref(); - } - } - - auto inst = reinterpret_steal(make_new_instance(tinfo->type)); - auto wrapper = reinterpret_cast(inst.ptr()); - wrapper->owned = false; - void *&valueptr = values_and_holders(wrapper).begin()->value_ptr(); - - switch (policy) { - case return_value_policy::automatic: - case return_value_policy::take_ownership: - valueptr = src; - wrapper->owned = true; - break; - - case return_value_policy::automatic_reference: - case return_value_policy::reference: - valueptr = src; - wrapper->owned = false; - break; - - case return_value_policy::copy: - if (copy_constructor) - valueptr = copy_constructor(src); - else { -#if defined(NDEBUG) - throw cast_error("return_value_policy = copy, but type is " - "non-copyable! (compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = copy, but type " + - type_name + " is non-copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::move: - if (move_constructor) - valueptr = move_constructor(src); - else if (copy_constructor) - valueptr = copy_constructor(src); - else { -#if defined(NDEBUG) - throw cast_error("return_value_policy = move, but type is neither " - "movable nor copyable! " - "(compile in debug mode for details)"); -#else - std::string type_name(tinfo->cpptype->name()); - detail::clean_type_id(type_name); - throw cast_error("return_value_policy = move, but type " + - type_name + " is neither movable nor copyable!"); -#endif - } - wrapper->owned = true; - break; - - case return_value_policy::reference_internal: - valueptr = src; - wrapper->owned = false; - keep_alive_impl(inst, parent); - break; - - default: - throw cast_error("unhandled return_value_policy: should not happen!"); - } - - tinfo->init_instance(wrapper, existing_holder); - - return inst.release(); - } - - // Base methods for generic caster; there are overridden in copyable_holder_caster - void load_value(value_and_holder &&v_h) { - auto *&vptr = v_h.value_ptr(); - // Lazy allocation for unallocated values: - if (vptr == nullptr) { - auto *type = v_h.type ? v_h.type : typeinfo; - if (type->operator_new) { - vptr = type->operator_new(type->type_size); - } else { - #if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912) - if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - vptr = ::operator new(type->type_size, - std::align_val_t(type->type_align)); - else - #endif - vptr = ::operator new(type->type_size); - } - } - value = vptr; - } - bool try_implicit_casts(handle src, bool convert) { - for (auto &cast : typeinfo->implicit_casts) { - type_caster_generic sub_caster(*cast.first); - if (sub_caster.load(src, convert)) { - value = cast.second(sub_caster.value); - return true; - } - } - return false; - } - bool try_direct_conversions(handle src) { - for (auto &converter : *typeinfo->direct_conversions) { - if (converter(src.ptr(), value)) - return true; - } - return false; - } - void check_holder_compat() {} - - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) - return caster.value; - return nullptr; - } - - /// Try to load with foreign typeinfo, if available. Used when there is no - /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = type::handle_of(src); - if (!hasattr(pytype, local_key)) - return false; - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) - return false; - - if (auto result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { - value = result; - return true; - } - return false; - } - - // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant - // bits of code between here and copyable_holder_caster where the two classes need different - // logic (without having to resort to virtual inheritance). - template - PYBIND11_NOINLINE bool load_impl(handle src, bool convert) { - if (!src) return false; - if (!typeinfo) return try_load_foreign_module_local(src); - if (src.is_none()) { - // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; - value = nullptr; - return true; - } - - auto &this_ = static_cast(*this); - this_.check_holder_compat(); - - PyTypeObject *srctype = Py_TYPE(src.ptr()); - - // Case 1: If src is an exact type match for the target type then we can reinterpret_cast - // the instance's value pointer to the target type: - if (srctype == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2: We have a derived class - else if (PyType_IsSubtype(srctype, typeinfo->type)) { - auto &bases = all_type_info(srctype); - bool no_cpp_mi = typeinfo->simple_type; - - // Case 2a: the python type is a Python-inherited derived class that inherits from just - // one simple (no MI) pybind11 class, or is an exact match, so the C++ instance is of - // the right type and we can use reinterpret_cast. - // (This is essentially the same as case 2b, but because not using multiple inheritance - // is extremely common, we handle it specially to avoid the loop iterator and type - // pointer lookup overhead) - if (bases.size() == 1 && (no_cpp_mi || bases.front()->type == typeinfo->type)) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder()); - return true; - } - // Case 2b: the python type inherits from multiple C++ bases. Check the bases to see if - // we can find an exact match (or, for a simple C++ type, an inherited match); if so, we - // can safely reinterpret_cast to the relevant pointer. - else if (bases.size() > 1) { - for (auto base : bases) { - if (no_cpp_mi ? PyType_IsSubtype(base->type, typeinfo->type) : base->type == typeinfo->type) { - this_.load_value(reinterpret_cast(src.ptr())->get_value_and_holder(base)); - return true; - } - } - } - - // Case 2c: C++ multiple inheritance is involved and we couldn't find an exact type match - // in the registered bases, above, so try implicit casting (needed for proper C++ casting - // when MI is involved). - if (this_.try_implicit_casts(src, convert)) - return true; - } - - // Perform an implicit conversion - if (convert) { - for (auto &converter : typeinfo->implicit_conversions) { - auto temp = reinterpret_steal(converter(src.ptr(), typeinfo->type)); - if (load_impl(temp, false)) { - loader_life_support::add_patient(temp); - return true; - } - } - if (this_.try_direct_conversions(src)) - return true; - } - - // Failed to match local typeinfo. Try again with global. - if (typeinfo->module_local) { - if (auto gtype = get_global_type_info(*typeinfo->cpptype)) { - typeinfo = gtype; - return load(src, false); - } - } - - // Global typeinfo has precedence over foreign module_local - return try_load_foreign_module_local(src); - } - - - // Called to do type lookup and wrap the pointer and type in a pair when a dynamic_cast - // isn't needed or can't be used. If the type is unknown, sets the error and returns a pair - // with .second = nullptr. (p.first = nullptr is not an error: it becomes None). - PYBIND11_NOINLINE static std::pair src_and_type( - const void *src, const std::type_info &cast_type, const std::type_info *rtti_type = nullptr) { - if (auto *tpi = get_type_info(cast_type)) - return {src, const_cast(tpi)}; - - // Not found, set error: - std::string tname = rtti_type ? rtti_type->name() : cast_type.name(); - detail::clean_type_id(tname); - std::string msg = "Unregistered type : " + tname; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return {nullptr, nullptr}; - } - - const type_info *typeinfo = nullptr; - const std::type_info *cpptype = nullptr; - void *value = nullptr; -}; - -/** - * Determine suitable casting operator for pointer-or-lvalue-casting type casters. The type caster - * needs to provide `operator T*()` and `operator T&()` operators. - * - * If the type supports moving the value away via an `operator T&&() &&` method, it should use - * `movable_cast_op_type` instead. - */ -template -using cast_op_type = - conditional_t>::value, - typename std::add_pointer>::type, - typename std::add_lvalue_reference>::type>; - -/** - * Determine suitable casting operator for a type caster with a movable value. Such a type caster - * needs to provide `operator T*()`, `operator T&()`, and `operator T&&() &&`. The latter will be - * called in appropriate contexts where the value can be moved rather than copied. - * - * These operator are automatically provided when using the PYBIND11_TYPE_CASTER macro. - */ -template -using movable_cast_op_type = - conditional_t::type>::value, - typename std::add_pointer>::type, - conditional_t::value, - typename std::add_rvalue_reference>::type, - typename std::add_lvalue_reference>::type>>; - -// std::is_copy_constructible isn't quite enough: it lets std::vector (and similar) through when -// T is non-copyable, but code containing such a copy constructor fails to actually compile. -template struct is_copy_constructible : std::is_copy_constructible {}; - -// Specialization for types that appear to be copy constructible but also look like stl containers -// (we specifically check for: has `value_type` and `reference` with `reference = value_type&`): if -// so, copy constructability depends on whether the value_type is copy constructible. -template struct is_copy_constructible, - std::is_same, - // Avoid infinite recursion - negation> - >::value>> : is_copy_constructible {}; - -// Likewise for std::pair -// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves -// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers). -template struct is_copy_constructible> - : all_of, is_copy_constructible> {}; - -// The same problems arise with std::is_copy_assignable, so we use the same workaround. -template struct is_copy_assignable : std::is_copy_assignable {}; -template struct is_copy_assignable, - std::is_same - >::value>> : is_copy_assignable {}; -template struct is_copy_assignable> - : all_of, is_copy_assignable> {}; - -PYBIND11_NAMESPACE_END(detail) - -// polymorphic_type_hook::get(src, tinfo) determines whether the object pointed -// to by `src` actually is an instance of some class derived from `itype`. -// If so, it sets `tinfo` to point to the std::type_info representing that derived -// type, and returns a pointer to the start of the most-derived object of that type -// (in which `src` is a subobject; this will be the same address as `src` in most -// single inheritance cases). If not, or if `src` is nullptr, it simply returns `src` -// and leaves `tinfo` at its default value of nullptr. -// -// The default polymorphic_type_hook just returns src. A specialization for polymorphic -// types determines the runtime type of the passed object and adjusts the this-pointer -// appropriately via dynamic_cast. This is what enables a C++ Animal* to appear -// to Python as a Dog (if Dog inherits from Animal, Animal is polymorphic, Dog is -// registered with pybind11, and this Animal is in fact a Dog). -// -// You may specialize polymorphic_type_hook yourself for types that want to appear -// polymorphic to Python but do not use C++ RTTI. (This is a not uncommon pattern -// in performance-sensitive applications, used most notably in LLVM.) -// -// polymorphic_type_hook_base allows users to specialize polymorphic_type_hook with -// std::enable_if. User provided specializations will always have higher priority than -// the default implementation and specialization provided in polymorphic_type_hook_base. -template -struct polymorphic_type_hook_base -{ - static const void *get(const itype *src, const std::type_info*&) { return src; } -}; -template -struct polymorphic_type_hook_base::value>> -{ - static const void *get(const itype *src, const std::type_info*& type) { - type = src ? &typeid(*src) : nullptr; - return dynamic_cast(src); - } -}; -template -struct polymorphic_type_hook : public polymorphic_type_hook_base {}; - -PYBIND11_NAMESPACE_BEGIN(detail) - -/// Generic type caster for objects stored on the heap -template class type_caster_base : public type_caster_generic { - using itype = intrinsic_t; - -public: - static constexpr auto name = _(); - - type_caster_base() : type_caster_base(typeid(type)) { } - explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { } - - static handle cast(const itype &src, return_value_policy policy, handle parent) { - if (policy == return_value_policy::automatic || policy == return_value_policy::automatic_reference) - policy = return_value_policy::copy; - return cast(&src, policy, parent); - } - - static handle cast(itype &&src, return_value_policy, handle parent) { - return cast(&src, return_value_policy::move, parent); - } - - // Returns a (pointer, type_info) pair taking care of necessary type lookup for a - // polymorphic type (using RTTI by default, but can be overridden by specializing - // polymorphic_type_hook). If the instance isn't derived, returns the base version. - static std::pair src_and_type(const itype *src) { - auto &cast_type = typeid(itype); - const std::type_info *instance_type = nullptr; - const void *vsrc = polymorphic_type_hook::get(src, instance_type); - if (instance_type && !same_type(cast_type, *instance_type)) { - // This is a base pointer to a derived type. If the derived type is registered - // with pybind11, we want to make the full derived object available. - // In the typical case where itype is polymorphic, we get the correct - // derived pointer (which may be != base pointer) by a dynamic_cast to - // most derived type. If itype is not polymorphic, we won't get here - // except via a user-provided specialization of polymorphic_type_hook, - // and the user has promised that no this-pointer adjustment is - // required in that case, so it's OK to use static_cast. - if (const auto *tpi = get_type_info(*instance_type)) - return {vsrc, tpi}; - } - // Otherwise we have either a nullptr, an `itype` pointer, or an unknown derived pointer, so - // don't do a cast - return type_caster_generic::src_and_type(src, cast_type, instance_type); - } - - static handle cast(const itype *src, return_value_policy policy, handle parent) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, policy, parent, st.second, - make_copy_constructor(src), make_move_constructor(src)); - } - - static handle cast_holder(const itype *src, const void *holder) { - auto st = src_and_type(src); - return type_caster_generic::cast( - st.first, return_value_policy::take_ownership, {}, st.second, - nullptr, nullptr, holder); - } - - template using cast_op_type = detail::cast_op_type; - - operator itype*() { return (type *) value; } - operator itype&() { if (!value) throw reference_cast_error(); return *((itype *) value); } - -protected: - using Constructor = void *(*)(const void *); - - /* Only enabled when the types are {copy,move}-constructible *and* when the type - does not have a private operator new implementation. */ - template ::value>> - static auto make_copy_constructor(const T *x) -> decltype(new T(*x), Constructor{}) { - return [](const void *arg) -> void * { - return new T(*reinterpret_cast(arg)); - }; - } - - template ::value>> - static auto make_move_constructor(const T *x) -> decltype(new T(std::move(*const_cast(x))), Constructor{}) { - return [](const void *arg) -> void * { - return new T(std::move(*const_cast(reinterpret_cast(arg)))); - }; - } - - static Constructor make_copy_constructor(...) { return nullptr; } - static Constructor make_move_constructor(...) { return nullptr; } -}; - -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; +template +class type_caster : public type_caster_base {}; +template +using make_caster = type_caster>; // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { +template +typename make_caster::template cast_op_type cast_op(make_caster &caster) { return caster.operator typename make_caster::template cast_op_type(); } -template typename make_caster::template cast_op_type::type> +template +typename make_caster::template cast_op_type::type> cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); + return std::move(caster).operator typename make_caster:: + template cast_op_type::type>(); } -template class type_caster> { +template +class type_caster> { private: using caster_t = make_caster; caster_t subcaster; - using subcaster_cast_op_type = typename caster_t::template cast_op_type; - static_assert(std::is_same::type &, subcaster_cast_op_type>::value, - "std::reference_wrapper caster requires T to have a caster with an `T &` operator"); + using reference_t = type &; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + + static_assert( + std::is_same::type &, subcaster_cast_op_type>::value + || std::is_same::value, + "std::reference_wrapper caster requires T to have a caster with an " + "`operator T &()` or `operator const T &()`"); + public: bool load(handle src, bool convert) { return subcaster.load(src, convert); } static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + static handle + cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + if (policy == return_value_policy::take_ownership + || policy == return_value_policy::automatic) { policy = return_value_policy::automatic_reference; + } return caster_t::cast(&src.get(), policy, parent); } - template using cast_op_type = std::reference_wrapper; - operator std::reference_wrapper() { return subcaster.operator subcaster_cast_op_type&(); } + template + using cast_op_type = std::reference_wrapper; + explicit operator std::reference_wrapper() { return cast_op(subcaster); } }; -#define PYBIND11_TYPE_CASTER(type, py_name) \ - protected: \ - type value; \ - public: \ - static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ - if (!src) return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ - auto h = cast(std::move(*src), policy, parent); delete src; return h; \ - } else { \ - return cast(*src, policy, parent); \ - } \ - } \ - operator type*() { return &value; } \ - operator type&() { return value; } \ - operator type&&() && { return std::move(value); } \ - template using cast_op_type = pybind11::detail::movable_cast_op_type - - -template using is_std_char_type = any_of< - std::is_same, /* std::string */ +#define PYBIND11_TYPE_CASTER(type, py_name) \ +protected: \ + type value; \ + \ +public: \ + static constexpr auto name = py_name; \ + template >::value, \ + int> = 0> \ + static ::pybind11::handle cast( \ + T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ + if (!src) \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) { \ + auto h = cast(std::move(*src), policy, parent); \ + delete src; \ + return h; \ + } \ + return cast(*src, policy, parent); \ + } \ + operator type *() { return &value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ + operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ + template \ + using cast_op_type = ::pybind11::detail::movable_cast_op_type + +template +using is_std_char_type = any_of, /* std::string */ #if defined(PYBIND11_HAS_U8STRING) - std::is_same, /* std::u8string */ + std::is_same, /* std::u8string */ #endif - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; - + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ + >; template struct type_caster::value && !is_std_char_type::value>> { using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using _py_type_1 = conditional_t::value, + _py_type_0, + typename std::make_unsigned<_py_type_0>::type>; using py_type = conditional_t::value, double, _py_type_1>; -public: +public: bool load(handle src, bool convert) { py_type py_value; - if (!src) + if (!src) { return false; + } + +#if !defined(PYPY_VERSION) + auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; +#else + // In PyPy 7.3.3, `PyIndex_Check` is implemented by calling `__index__`, + // while CPython only considers the existence of `nb_index`/`__index__`. + auto index_check = [](PyObject *o) { return hasattr(o, "__index__"); }; +#endif if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) + if (convert || PyFloat_Check(src.ptr())) { py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else + } else { return false; - } else if (PyFloat_Check(src.ptr())) { + } + } else if (PyFloat_Check(src.ptr()) + || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { return false; - } else if (std::is_unsigned::value) { - py_value = as_unsigned(src.ptr()); - } else { // signed integer: - py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); + } else { + handle src_or_index = src; + // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) + object index; + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + index = reinterpret_steal(PyNumber_Index(src.ptr())); + if (!index) { + PyErr_Clear(); + if (!convert) + return false; + } else { + src_or_index = index; + } + } +#endif + if (std::is_unsigned::value) { + py_value = as_unsigned(src_or_index.ptr()); + } else { // signed integer: + py_value = sizeof(T) <= sizeof(long) + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + } } // Python API reported an error @@ -1040,19 +179,14 @@ struct type_caster::value && !is_std_char_t // Check to see if the conversion is valid (integers should match exactly) // Signed/unsigned checks happen elsewhere - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { - bool type_error = py_err && PyErr_ExceptionMatches( -#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION) - PyExc_SystemError -#else - PyExc_TypeError -#endif - ); + if (py_err + || (std::is_integral::value && sizeof(py_type) != sizeof(T) + && py_value != (py_type) (T) py_value)) { PyErr_Clear(); - if (type_error && convert && PyNumber_Check(src.ptr())) { + if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) { auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); PyErr_Clear(); return load(tmp, false); } @@ -1063,62 +197,75 @@ struct type_caster::value && !is_std_char_t return true; } - template + template static typename std::enable_if::value, handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyFloat_FromDouble((double) src); } - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) <= sizeof(long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PYBIND11_LONG_FROM_SIGNED((long) src); } - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) <= sizeof(unsigned long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); } - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) > sizeof(long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromLongLong((long long) src); } - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) > sizeof(unsigned long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromUnsignedLongLong((unsigned long long) src); } - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); + PYBIND11_TYPE_CASTER(T, const_name::value>("int", "float")); }; -template struct void_caster { +template +struct void_caster { public: bool load(handle src, bool) { - if (src && src.is_none()) + if (src && src.is_none()) { return true; + } return false; } static handle cast(T, return_value_policy /* policy */, handle /* parent */) { return none().inc_ref(); } - PYBIND11_TYPE_CASTER(T, _("None")); + PYBIND11_TYPE_CASTER(T, const_name("None")); }; -template <> class type_caster : public void_caster {}; +template <> +class type_caster : public void_caster {}; -template <> class type_caster : public type_caster { +template <> +class type_caster : public type_caster { public: using type_caster::cast; bool load(handle h, bool) { if (!h) { return false; - } else if (h.is_none()) { + } + if (h.is_none()) { value = nullptr; return true; } @@ -1130,7 +277,7 @@ template <> class type_caster : public type_caster { } /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); + const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); if (bases.size() == 1) { // Only allowing loading from a single-value type value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); return true; @@ -1141,191 +288,247 @@ template <> class type_caster : public type_caster { } static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) + if (ptr) { return capsule(ptr).release(); - else - return none().inc_ref(); + } + return none().inc_ref(); } - template using cast_op_type = void*&; - operator void *&() { return value; } - static constexpr auto name = _("capsule"); + template + using cast_op_type = void *&; + explicit operator void *&() { return value; } + static constexpr auto name = const_name("capsule"); + private: void *value = nullptr; }; -template <> class type_caster : public void_caster { }; +template <> +class type_caster : public void_caster {}; -template <> class type_caster { +template <> +class type_caster { public: bool load(handle src, bool convert) { - if (!src) return false; - else if (src.ptr() == Py_True) { value = true; return true; } - else if (src.ptr() == Py_False) { value = false; return true; } - else if (convert || !strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name)) { + if (!src) { + return false; + } + if (src.ptr() == Py_True) { + value = true; + return true; + } + if (src.ptr() == Py_False) { + value = false; + return true; + } + if (convert || (std::strcmp("numpy.bool_", Py_TYPE(src.ptr())->tp_name) == 0)) { // (allow non-implicit conversion for numpy booleans) Py_ssize_t res = -1; if (src.is_none()) { - res = 0; // None is implicitly converted to False + res = 0; // None is implicitly converted to False } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists +#if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" attr exists else if (hasattr(src, PYBIND11_BOOL_ATTR)) { res = PyObject_IsTrue(src.ptr()); } - #else +#else // Alternate approach for CPython: this does the same as the above, but optimized // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + else if (auto *tp_as_number = src.ptr()->ob_type->tp_as_number) { if (PYBIND11_NB_BOOL(tp_as_number)) { res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); } } - #endif +#endif if (res == 0 || res == 1) { - value = (bool) res; + value = (res != 0); return true; - } else { - PyErr_Clear(); } + PyErr_Clear(); } return false; } static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { return handle(src ? Py_True : Py_False).inc_ref(); } - PYBIND11_TYPE_CASTER(bool, _("bool")); + PYBIND11_TYPE_CASTER(bool, const_name("bool")); }; // Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { +template +struct string_caster { using CharT = typename StringType::value_type; // Simplify life by being able to assume standard char sizes (the standard only guarantees // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char size != 1"); #if defined(PYBIND11_HAS_U8STRING) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char8_t size != 1"); #endif - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, + "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, + "Unsupported char32_t size != 4"); // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); + "Unsupported wchar_t size != 2/4"); static constexpr size_t UTF_N = 8 * sizeof(CharT); bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif handle load_src = src; if (!src) { return false; - } else if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (std::is_same::value) { - return load_bytes(load_src); - } + } + if (!PyUnicode_Check(load_src.ptr())) { + return load_raw(load_src); + } - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) + // For UTF-8 we avoid the need for a temporary `bytes` object by using + // `PyUnicode_AsUTF8AndSize`. + if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) { + Py_ssize_t size = -1; + const auto *buffer + = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); + if (!buffer) { + PyErr_Clear(); return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif + } + value = StringType(buffer, static_cast(size)); + return true; } - auto utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } + auto utfNbytes + = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr)); + if (!utfNbytes) { + PyErr_Clear(); + return false; + } - const auto *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + const auto *buffer + = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); - if (UTF_N > 8) { buffer++; length--; } // Skip BOM for UTF-16/32 + // Skip BOM for UTF-16/32 + if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) { + buffer++; + length--; + } value = StringType(buffer, length); // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) + if (IsView) { loader_life_support::add_patient(utfNbytes); + } return true; } - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + static handle + cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { const char *buffer = reinterpret_cast(src.data()); auto nbytes = ssize_t(src.size() * sizeof(CharT)); handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); + if (!s) { + throw error_already_set(); + } return s; } - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + PYBIND11_TYPE_CASTER(StringType, const_name(PYBIND11_STRING_NAME)); private: static handle decode_utfN(const char *buffer, ssize_t nbytes) { #if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); + return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) + : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) + : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); #else - // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as well), - // so bypass the whole thing by just passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); + // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as + // well), so bypass the whole thing by just passing the encoding as a string value, which + // works properly: + return PyUnicode_Decode(buffer, + nbytes, + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr); #endif } - // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e. // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. // which supports loading a unicode from a str, doesn't take this path. template - bool load_bytes(enable_if_t::value, handle> src) { + bool load_raw(enable_if_t::value, handle> src) { if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // We were passed raw bytes; accept it into a std::string or char* // without any encoding attempt. const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; + if (!bytes) { + pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); } + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + if (PyByteArray_Check(src.ptr())) { + // We were passed a bytearray; accept it into a std::string or char* + // without any encoding attempt. + const char *bytearray = PyByteArray_AsString(src.ptr()); + if (!bytearray) { + pybind11_fail("Unexpected PyByteArray_AsString() failure."); + } + value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); + return true; } return false; } template - bool load_bytes(enable_if_t::value, handle>) { return false; } + bool load_raw(enable_if_t::value, handle>) { + return false; + } }; template -struct type_caster, enable_if_t::value>> +struct type_caster, + enable_if_t::value>> : string_caster> {}; #ifdef PYBIND11_HAS_STRING_VIEW template -struct type_caster, enable_if_t::value>> +struct type_caster, + enable_if_t::value>> : string_caster, true> {}; #endif // Type caster for C-style strings. We basically use a std::string type caster, but also add the // ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { +template +struct type_caster::value>> { using StringType = std::basic_string; - using StringCaster = type_caster; + using StringCaster = make_caster; StringCaster str_caster; bool none = false; CharT one_char = 0; + public: bool load(handle src, bool convert) { - if (!src) return false; + if (!src) { + return false; + } if (src.is_none()) { // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; + if (!convert) { + return false; + } none = true; return true; } @@ -1333,45 +536,58 @@ template struct type_caster::value) { handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); + if (!s) { + throw error_already_set(); + } return s; } return StringCaster::cast(StringType(1, src), policy, parent); } - operator CharT*() { return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } - operator CharT&() { - if (none) + explicit operator CharT *() { + return none ? nullptr : const_cast(static_cast(str_caster).c_str()); + } + explicit operator CharT &() { + if (none) { throw value_error("Cannot convert None to a character"); + } auto &value = static_cast(str_caster); size_t str_len = value.size(); - if (str_len == 0) + if (str_len == 0) { throw value_error("Cannot convert empty string to a character"); + } // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { + // is too high, and one for multiple unicode characters (caught later), so we need to + // figure out how long the first encoded character is in bytes to distinguish between these + // two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as + // those can fit into a single char value. + if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 8) && str_len > 1 && str_len <= 4) { auto v0 = static_cast(value[0]); - size_t char0_bytes = !(v0 & 0x80) ? 1 : // low bits only: 0-127 - (v0 & 0xE0) == 0xC0 ? 2 : // 0b110xxxxx - start of 2-byte sequence - (v0 & 0xF0) == 0xE0 ? 3 : // 0b1110xxxx - start of 3-byte sequence - 4; // 0b11110xxx - start of 4-byte sequence + // low bits only: 0-127 + // 0b110xxxxx - start of 2-byte sequence + // 0b1110xxxx - start of 3-byte sequence + // 0b11110xxx - start of 4-byte sequence + size_t char0_bytes = (v0 & 0x80) == 0 ? 1 + : (v0 & 0xE0) == 0xC0 ? 2 + : (v0 & 0xF0) == 0xE0 ? 3 + : 4; if (char0_bytes == str_len) { // If we have a 128-255 value, we can decode it into a single char: if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + one_char = static_cast(((v0 & 3) << 6) + + (static_cast(value[1]) & 0x3F)); return one_char; } // Otherwise we have a single character, but it's > U+00FF @@ -1382,36 +598,42 @@ template struct type_caster(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) + if (one_char >= 0xD800 && one_char < 0xE000) { throw value_error("Character code point not in range(0x10000)"); + } } - if (str_len != 1) + if (str_len != 1) { throw value_error("Expected a character, but multi-character string found"); + } one_char = value[0]; return one_char; } - static constexpr auto name = _(PYBIND11_STRING_NAME); - template using cast_op_type = pybind11::detail::cast_op_type<_T>; + static constexpr auto name = const_name(PYBIND11_STRING_NAME); + template + using cast_op_type = pybind11::detail::cast_op_type<_T>; }; // Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { +template