diff --git a/.gitignore b/.gitignore index 9b3ffd0238..7f7a8f4559 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ build/ /Testing/* CTestTestfile.cmake install_manifest.txt +*.gdb_history /bbasm /ImportExecutables.cmake *-coverage/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b02e73d7e..2aafabc245 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,7 +171,7 @@ if (BUILD_PYTHON) endif () endif() -include_directories(common/ json/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS}) +include_directories(common/ json/ ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} .) aux_source_directory(common/ COMMON_SRC_FILES) aux_source_directory(json/ JSON_PARSER_FILES) set(COMMON_FILES ${COMMON_SRC_FILES} ${JSON_PARSER_FILES}) diff --git a/README.md b/README.md index 010acd8be3..64f15714c2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ nextpnr -- a portable FPGA place and route tool =============================================== +### *This `vpr_place` branch contains code adapted from the [Verilog-To-Routing](https://verilogtorouting.org/) project.* + nextpnr aims to be a vendor neutral, timing driven, FOSS FPGA place and route tool. diff --git a/common/command.cc b/common/command.cc index ab0c92f2e9..423ebc2794 100644 --- a/common/command.cc +++ b/common/command.cc @@ -106,6 +106,8 @@ po::options_description CommandHandler::getGeneralOptions() general.add_options()("no-tmdriv", "disable timing-driven placement"); general.add_options()("save", po::value(), "project file to write"); general.add_options()("load", po::value(), "project file to read"); + + general.add_options()("vpr_place", "use VPR placer"); return general; } @@ -207,7 +209,10 @@ int CommandHandler::executeMain(std::unique_ptr ctx) ctx->check(); print_utilisation(ctx.get()); if (!vm.count("pack-only")) { - if (!ctx->place() && !ctx->force) + if (vm.count("vpr_place")) { + if (!ctx->place_vpr() && !ctx->force) + log_error("Placing design failed.\n"); + } else if (!ctx->place() && !ctx->force) log_error("Placing design failed.\n"); ctx->check(); if (!ctx->route() && !ctx->force) diff --git a/common/placer1.cc b/common/placer1.cc index 01f822a553..14ab154674 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -171,6 +171,8 @@ class SAPlacer double avg_metric = curr_metric; temp = 10000; + tot_move = tot_accept = 0; + // Main simulated annealing loop for (int iter = 1;; iter++) { n_move = n_accept = 0; @@ -267,9 +269,15 @@ class SAPlacer curr_metric += wl; } + tot_move += n_move; + tot_accept += n_accept; + // Let the UI show visualization updates. ctx->yield(); } + + log_info(" swaps attempted: %d acceptance rate: %.3f\n", tot_move, double(tot_accept)/double(tot_move)); + // Final post-pacement validitiy check ctx->yield(); for (auto bel : ctx->getBels()) { @@ -476,6 +484,7 @@ class SAPlacer float temp = 1000; bool improved = false; int n_move, n_accept; + int tot_move, tot_accept; int diameter = 35, max_x = 1, max_y = 1; std::unordered_map bel_types; std::vector>>> fast_bels; diff --git a/common/placer1.h b/common/placer1.h index 55db1fa570..d203edfde8 100644 --- a/common/placer1.h +++ b/common/placer1.h @@ -16,8 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ -#ifndef PLACE_H -#define PLACE_H +#ifndef PLACER1_H +#define PLACER1_H #include "nextpnr.h" #include "settings.h" @@ -34,4 +34,4 @@ extern bool placer1(Context *ctx, Placer1Cfg cfg); NEXTPNR_NAMESPACE_END -#endif // PLACE_H +#endif // PLACER1_H diff --git a/common/placer_vpr.cc b/common/placer_vpr.cc new file mode 100644 index 0000000000..1b0b099937 --- /dev/null +++ b/common/placer_vpr.cc @@ -0,0 +1,366 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * Simulated annealing implementation based on arachne-pnr + * Copyright (C) 2015-2018 Cotton Seed + * + * 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 "placer_vpr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "place_common.h" +#include "timing.h" +#include "util.h" + +namespace vpr { + using namespace NEXTPNR_NAMESPACE; + + static Context* npnr_ctx = NULL; + std::vector npnr_cells; + + // base/device_grid.h + struct DeviceGrid { + inline size_t width() const { return npnr_ctx->chip_info->width; } + inline size_t height() const { return npnr_ctx->chip_info->height; } + inline const std::vector>& operator[](size_t x) { return _bels.at(x); } + std::vector>> _bels; + }; + // base/netlist_fwd.h + enum PinType + { + DRIVER = PortType::PORT_OUT, + SINK = PortType::PORT_IN, + }; + // base/clustered_netlist_fwd.h + typedef size_t ClusterBlockId; + // base/globals.h + static struct VprContext { + struct t_clustering { + struct { + std::unordered_map>& nets() const { return npnr_ctx->nets; } + bool net_is_global(const NetInfo* net) const { return npnr_ctx->isGlobalNet(net); } + std::vector& net_sinks(NetInfo* net) const { return net->users; } + struct t_net_pins { + t_net_pins(const NetInfo* net) : _net(net) {} + size_t size() const { return _net->users.size() + 1; } + const NetInfo* _net; + }; + t_net_pins net_pins(const NetInfo* net) const { return t_net_pins(net); } + std::unordered_map& block_pins(CellInfo* cell) { return cell->ports; } + PinType pin_type(const PortInfo& port) { return static_cast(port.type); } + CellInfo* net_driver_block(const NetInfo* net) const { return net->driver.cell; } + size_t pin_net_index(const PortInfo& port, const CellInfo* cell) { + auto net = port.net; + for (auto it = net->users.begin(); it != net->users.end(); ++it) { + if (it->port == port.name && it->cell == cell) + return it - net->users.begin() + 1; + } + throw; + } + std::unordered_map>& blocks() const { return npnr_ctx->cells; } + IdString block_type(const CellInfo* cell) const { return cell->type; } + std::string block_name(const CellInfo* cell) const { return cell->name.str(npnr_ctx); } + } clb_nlist; + t_clustering& operator()() { return *this; } + } clustering; + struct t_device { + DeviceGrid grid; + t_device& operator()() { return *this; } + } device; + } g_vpr_ctx; + // libtatum + namespace tatum { + struct TimingPathInfo { + TimingPathInfo(float delay=0) : _delay(delay) {} + float delay() { return _delay; } + float _delay; + }; + }; + // timing/timing_info.h + struct SetupTimingInfo { + delay_t sWNS, sTNS; + delay_t max_req; + delay_t worst_path_slack; + void update() { + if (npnr_ctx->slack_redist_iter > 0) + worst_path_slack = assign_budget(npnr_ctx, true /* quiet */); + else + worst_path_slack = timing_analysis(npnr_ctx, false /* print_fmax */, false /* print_histogram */, false /* print_fmax */); + + sWNS = std::numeric_limits::max(); + sTNS = 0; + max_req = delay_t(1.0e12 / npnr_ctx->target_freq); + + // Compute the delay for every pin on every net + for (auto &n : npnr_ctx->nets) { + auto net = n.second.get(); + + bool driver_gb; + CellInfo *driver_cell = net->driver.cell; + if (!driver_cell) + continue; + if (driver_cell->bel == BelId()) + continue; + driver_gb = npnr_ctx->getBelGlobalBuf(driver_cell->bel); + if (driver_gb) + continue; + for (auto& load : net->users) { + if (!load.cell) + continue; + CellInfo *load_cell = load.cell; + if (load_cell->bel == BelId()) + continue; + auto net_delay = npnr_ctx->getNetinfoRouteDelay(net, load); + auto slack = load.budget - net_delay; + sWNS = std::min(sWNS, slack); + if (slack < 0) + sTNS += slack; + } + } + } + tatum::TimingPathInfo least_slack_critical_path() + { + return tatum::TimingPathInfo(npnr_ctx->getDelayNS(max_req - worst_path_slack)); + } + float setup_total_negative_slack() { return npnr_ctx->getDelayNS(sTNS); } + float setup_worst_negative_slack() { return npnr_ctx->getDelayNS(sWNS); } + }; + std::unique_ptr make_setup_timing_info(/*std::shared_ptr delay_calculator*/) { + return std::unique_ptr(new SetupTimingInfo); + } + // timing/timing_util.h + float calculate_clb_net_pin_criticality(const SetupTimingInfo& timing_info, /*const ClusteredPinAtomPinsLookup& pin_lookup,*/ const PortRef& load, const NetInfo* net) + { + NPNR_ASSERT(npnr_ctx->timing_driven); + + bool driver_gb; + CellInfo *driver_cell = net->driver.cell; + if (!driver_cell) + return 0; + if (driver_cell->bel == BelId()) + return 0; + driver_gb = npnr_ctx->getBelGlobalBuf(driver_cell->bel); + WireId drv_wire = npnr_ctx->getBelPinWire(driver_cell->bel, net->driver.port); + if (driver_gb) + return 0; + if (load.cell == nullptr) + return 0; + CellInfo *load_cell = load.cell; + if (load_cell->bel == BelId()) + return 0; + WireId user_wire = npnr_ctx->getBelPinWire(load_cell->bel, load.port); + delay_t raw_wl = npnr_ctx->estimateDelay(drv_wire, user_wire); + delay_t slack = load.budget - raw_wl; + delay_t shift = std::min(timing_info.sWNS, 0); + float crit = 1 - (float(slack + shift) / (timing_info.max_req + shift)); + crit = std::max(0., crit); + crit = std::min(1., crit); + return crit; + } + // draw/draw.h + void update_screen(/*ScreenUpdatePriority priority, const char *msg, enum pic_type pic_on_screen_val, + std::shared_ptr timing_info*/) + { + npnr_ctx->yield(); + } + + #define VTR_ASSERT NPNR_ASSERT + #define VTR_ASSERT_SAFE NPNR_ASSERT + #define VTR_ASSERT_SAFE_MSG NPNR_ASSERT_MSG + #define vpr_throw(__a, __b, __c, ...) log_error(__VA_ARGS__) + namespace vtr { + constexpr size_t bufsize = 32768; + template + inline void printf_warning(const char*, unsigned, const char* fmt, Args... args) { + log_warning(fmt, std::forward(args)...); + } + inline void printf_info(const char* fmt) { + log_info(fmt); + } + template + inline void printf_info(const char* fmt, Args... args) { + log_info(fmt, std::forward(args)...); + } + template + inline void printf_error(const char*, int, const char* fmt, Args... args) { + log_error(fmt, std::forward(args)...); + } + inline void printf(const char* fmt) { + log_info(fmt); + } + template + inline void printf(const char* fmt, Args... args) { + log_info(fmt, std::forward(args)...); + } + + int irand(int imax) { return npnr_ctx->rng(imax+1); } + float frand() { return npnr_ctx->rng() / float(0x3fffffff); } + } + + // libarchfpga + #define OPEN -1 + + #include "vpr/place/timing_place.cpp" + #include "vpr/place/place.cpp" + #include "vpr/place/place_macro.cpp" +} + +NEXTPNR_NAMESPACE_BEGIN + +class VPRPlacer +{ + public: + VPRPlacer(Context *ctx) : ctx(ctx) + { + vpr::npnr_ctx = ctx; + int max_y = 0; + auto &grid = vpr::g_vpr_ctx.device.grid; + for (auto bel : ctx->getBels()) { + auto loc = ctx->getBelLocation(bel); + if (loc.x >= int(grid._bels.size())) + grid._bels.resize(loc.x+1); + max_y = std::max(loc.y, max_y); + if (max_y >= int(grid._bels[loc.x].size())) + grid._bels[loc.x].resize(max_y+1); + if (loc.z >= int(grid._bels[loc.x][loc.y].size())) + grid._bels[loc.x][loc.y].resize(loc.z+1); + grid._bels[loc.x][loc.y][loc.z] = bel; + } + for (auto& c : grid._bels) + c.resize(max_y+1); + + int32_t cell_idx = 0; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->bel == BelId()) { + vpr::npnr_cells.push_back(cell.second.get()); + } + ci->udata = cell_idx++; + + auto loc = ci->attrs.find(ctx->id("BEL")); + if (loc != ci->attrs.end()) { + const std::string& loc_name = loc->second; + auto bel = ctx->getBelByName(ctx->id(loc_name)); + if (bel == BelId()) { + log_error("No Bel named \'%s\' located for " + "this chip (processing BEL attribute on \'%s\')\n", + loc_name.c_str(), ci->name.c_str(ctx)); + } + + auto bel_type = ctx->getBelType(bel); + if (bel_type != ci->type) { + log_error("Bel \'%s\' of type \'%s\' does not match cell " + "\'%s\' of type \'%s\'", + loc_name.c_str(), bel_type.c_str(ctx), ci->name.c_str(ctx), + ci->type.c_str(ctx)); + } + ctx->bindBel(bel, ci, STRENGTH_USER); + } + } + int32_t net_idx = 0; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + ni->udata = net_idx++; + } + } + + bool place() + { + log_break(); + ctx->lock(); + + vpr::t_placer_opts placer_opts; + placer_opts.place_algorithm = vpr::PATH_TIMING_DRIVEN_PLACE; + placer_opts.enable_timing_computations = ctx->timing_driven; + placer_opts.inner_loop_recompute_divider = 0; + placer_opts.recompute_crit_iter = 1; + placer_opts.td_place_exp_first = 1.0; + placer_opts.td_place_exp_last = 8.0; + placer_opts.timing_tradeoff = 0.5; + + vpr::t_annealing_sched annealing_sched; + annealing_sched.type = vpr::AUTO_SCHED; + annealing_sched.inner_num = 10; + + vpr::try_place(placer_opts, annealing_sched); + + // Final post-pacement validitiy check + ctx->yield(); + for (auto bel : ctx->getBels()) { + CellInfo *cell = ctx->getBoundBelCell(bel); + if (!ctx->isBelLocationValid(bel)) { + std::string cell_text = "no cell"; + if (cell != nullptr) + cell_text = std::string("cell '") + ctx->nameOf(cell) + "'"; + if (ctx->force) { + log_warning("post-placement validity check failed for Bel '%s' " + "(%s)\n", + ctx->getBelName(bel).c_str(ctx), cell_text.c_str()); + } else { + log_error("post-placement validity check failed for Bel '%s' " + "(%s)\n", + ctx->getBelName(bel).c_str(ctx), cell_text.c_str()); + } + } + } + for (auto cell : sorted(ctx->cells)) + if (get_constraints_distance(ctx, cell.second) != 0) + log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx), + ctx->getBelName(cell.second->bel).c_str(ctx)); + timing_analysis(ctx); + ctx->unlock(); + return true; + } + + private: + Context *ctx; +}; + +bool placer_vpr(Context *ctx) +{ + try { + VPRPlacer placer(ctx); + placer.place(); + log_info("Checksum: 0x%08x\n", ctx->checksum()); +#ifndef NDEBUG + ctx->check(); +#endif + return true; + } catch (log_execution_error_exception) { +#ifndef NDEBUG + ctx->check(); +#endif + return false; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/common/placer_vpr.h b/common/placer_vpr.h new file mode 100644 index 0000000000..eff3ab939e --- /dev/null +++ b/common/placer_vpr.h @@ -0,0 +1,30 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford 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 PLACER_VPR_H +#define PLACER_VPR_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +extern bool placer_vpr(Context *ctx); + +NEXTPNR_NAMESPACE_END + +#endif // PLACER_VPR_H diff --git a/common/router1.cc b/common/router1.cc index c4708de7bb..3f22782cbf 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -951,7 +951,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg) #ifndef NDEBUG ctx->check(); #endif - timing_analysis(ctx, true /* slack_histogram */, true /* print_path */); + timing_analysis(ctx, true /* print_fmax */, true /* print_histogram */, true /* print_path */); ctx->unlock(); return true; } catch (log_execution_error_exception) { diff --git a/common/timing.cc b/common/timing.cc index d1a85779b0..3edfbce2ec 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -319,7 +319,7 @@ struct Timing } }; -void assign_budget(Context *ctx, bool quiet) +delay_t assign_budget(Context *ctx, bool quiet) { if (!quiet) { log_break(); @@ -360,9 +360,11 @@ void assign_budget(Context *ctx, bool quiet) if (!quiet) log_info("Checksum: 0x%08x\n", ctx->checksum()); + + return timing.min_slack; } -void timing_analysis(Context *ctx, bool print_histogram, bool print_path) +delay_t timing_analysis(Context *ctx, bool print_histogram, bool print_path, bool print_fmax) { PortRefVector crit_path; DelayFrequency slack_histogram; @@ -411,8 +413,10 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) } } - delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq); - log_info("estimated Fmax = %.2f MHz\n", 1e3 / ctx->getDelayNS(default_slack - min_slack)); + if (print_fmax) { + delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq); + log_info("estimated Fmax = %.2f MHz\n", 1e3 / ctx->getDelayNS(default_slack - min_slack)); + } if (print_histogram && slack_histogram.size() > 0) { unsigned num_bins = 20; @@ -439,6 +443,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) std::string(bins[i] * bar_width / max_freq, '*').c_str(), (bins[i] * bar_width) % max_freq > 0 ? '+' : ' '); } + + return min_slack; } NEXTPNR_NAMESPACE_END diff --git a/common/timing.h b/common/timing.h index cfb71ae0a9..655e33e16f 100644 --- a/common/timing.h +++ b/common/timing.h @@ -25,11 +25,11 @@ NEXTPNR_NAMESPACE_BEGIN // Evenly redistribute the total path slack amongst all sinks on each path -void assign_budget(Context *ctx, bool quiet = false); +delay_t assign_budget(Context *ctx, bool quiet = false); // Perform timing analysis and print out the fmax, and optionally the // critical path -void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_path = false); +delay_t timing_analysis(Context *ctx, bool print_histogram = true, bool print_path = false, bool print_fmax = true); NEXTPNR_NAMESPACE_END diff --git a/ice40/arch.cc b/ice40/arch.cc index eb26ae5ae1..7b91700e68 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -27,6 +27,7 @@ #include "placer1.h" #include "router1.h" #include "util.h" +#include "placer_vpr.h" NEXTPNR_NAMESPACE_BEGIN @@ -609,6 +610,51 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } +bool Arch::place_vpr() +{ + auto ctx = getCtx(); + + // VPR's initial placement is greedy and will place each cell + // into a randomly selected bel of the same type. + // However, the ice40's GBs have restrictions on which are + // not amenable to this greedy approach. + // Work around this my placing those restrictive GBs upfront. + std::vector gb_reset; + std::vector gb_cen; + for (auto bel : ctx->getBels()) { + auto type = ctx->getBelType(bel); + if (type == id_SB_GB) { + IdString glb_net = ctx->getWireName(ctx->getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT)); + int glb_id = std::stoi(std::string("") + glb_net.str(ctx).back()); + if (glb_id % 2 == 0) + gb_reset.push_back(bel); + else + gb_cen.push_back(bel); + } + } + for (auto &c : ctx->cells) { + CellInfo *cell = c.second.get(); + if (cell->type == id_SB_GB) { + auto net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net; + NPNR_ASSERT(net != nullptr); + bool is_reset = net->is_reset, is_cen = net->is_enable; + NPNR_ASSERT(!is_reset || !is_cen); + if (is_reset) { + ctx->bindBel(gb_reset.back(), cell, STRENGTH_WEAK); + gb_reset.pop_back(); + } + else if (is_cen) { + ctx->bindBel(gb_cen.back(), cell, STRENGTH_WEAK); + gb_cen.pop_back(); + } + } + } + + return placer_vpr(ctx); +} + +// ----------------------------------------------------------------------- + bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } // ----------------------------------------------------------------------- @@ -941,6 +987,11 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_GLOBAL_BUFFER_OUTPUT; } +bool Arch::isIO(const CellInfo* cell) const +{ + return cell->type == id("SB_IO"); +} + // Assign arch arg info void Arch::assignArchInfo() { diff --git a/ice40/arch.h b/ice40/arch.h index 27d5db9feb..93c0b5308e 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -782,6 +782,7 @@ struct Arch : BaseCtx bool pack(); bool place(); + bool place_vpr(); bool route(); // ------------------------------------------------- @@ -802,6 +803,7 @@ struct Arch : BaseCtx TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; + bool isIO(const CellInfo* cell) const; // ------------------------------------------------- diff --git a/vpr/base/vpr_types.h b/vpr/base/vpr_types.h new file mode 100644 index 0000000000..2d21699173 --- /dev/null +++ b/vpr/base/vpr_types.h @@ -0,0 +1,1200 @@ +/* This is a core file that defines the major data types used by VPR + + This file is divided into generally 4 major sections: + + 1. Global data types and constants + 2. Packing specific data types + 3. Placement specific data types + 4. Routing specific data types + + Key background file: + + An understanding of libarchfpga/physical_types.h is crucial to understanding this file. physical_types.h contains information about the architecture described in the architecture description language + + Key data structures: + t_rr_node - The basic building block of the interconnect in the FPGA architecture + + Cluster-specific main data structure: + t_pb: Stores the mapping between the user netlist and the logic blocks on the FPGA architecture. For example, if a user design has 10 clusters of 5 LUTs each, you will have 10 t_pb instances of type cluster and within each of those clusters another 5 t_pb instances of type LUT. + The t_pb hierarchy follows what is described by t_pb_graph_node + + */ + +#ifndef VPR_TYPES_H +#define VPR_TYPES_H + +//#include +//#include +//#include "arch_types.h" +//#include "atom_netlist_fwd.h" +//#include "clustered_netlist_fwd.h" +//#include "constant_nets.h" +//#include "clock_modeling.h" +// +//#include "vtr_assert.h" +//#include "vtr_ndmatrix.h" +//#include "vtr_vector_map.h" +// +///******************************************************************************* +// * Global data types and constants +// ******************************************************************************/ +///*#define CREATE_ECHO_FILES*//* prints echo files */ +///*#define PRINT_SINK_DELAYS*//*prints the sink delays to files */ +///*#define PRINT_SLACKS*//*prints out all slacks in the circuit */ +///*#define PRINT_PLACE_CRIT_PATH*//*prints out placement estimated critical path */ +///*#define PRINT_NET_DELAYS*//*prints out delays for all connections */ +///*#define PRINT_TIMING_GRAPH*//*prints out the timing graph */ +///*#define DUMP_BLIF_ECHO*//*dump blif of internal representation of user circuit. Useful for ensuring functional correctness via logical equivalence with input blif*/ +// +////#define ROUTER_DEBUG //Prints out very detailed routing progress information if defined +// +//#define TOKENS " \t\n" /* Input file parsing. */ +// +////#define VERBOSE //Prints additional intermediate data +// +///* For update_screen. Denotes importance of update. +// * By default MINOR only updates the screen, while MAJOR +// * pauses graphics for the user to interact */ +//enum class ScreenUpdatePriority { +// MINOR = 0, +// MAJOR = 1 +//}; +// +//#define MAX_SHORT 32767 + +/* Values large enough to be way out of range for any data, but small enough + to allow a small number to be added to them without going out of range. */ +#define HUGE_POSITIVE_FLOAT 1.e30 +//#define HUGE_NEGATIVE_FLOAT -1.e30 +// +///* Used to avoid floating-point errors when comparing values close to 0 */ +//#define EPSILON 1.e-15 +//#define NEGATIVE_EPSILON -1.e-15 +// +//#define HIGH_FANOUT_NET_LIM 64 /* All nets with this number of sinks or more are considered high fanout nets */ +// +//#define FIRST_ITER_WIRELENTH_LIMIT 0.85 /* If used wirelength exceeds this value in first iteration of routing, do not route */ +// +///* Defining macros for the placement_ctx t_grid_blocks. Assumes that ClusterBlockId's won't exceed positive 32-bit integers */ +//constexpr auto EMPTY_BLOCK_ID = ClusterBlockId(-1); +//constexpr auto INVALID_BLOCK_ID = ClusterBlockId(-2); +// +//constexpr const char* EMPTY_BLOCK_NAME = "EMPTY"; +// +///* +// * Files +// */ +///******************************************************************************* +// * Packing specific data types and constants +// * Packing takes the circuit described in the technology mapped user netlist +// * and maps it to the complex logic blocks found in the arhictecture +// ******************************************************************************/ +// +//#define NO_CLUSTER -1 +//#define NEVER_CLUSTER -2 +//#define NOT_VALID -10000 /* Marks gains that aren't valid */ +///* Ensure no gain can ever be this negative! */ +//#ifndef UNDEFINED +//#define UNDEFINED -1 +//#endif +// +///* Selection algorithm for selecting next seed */ +//enum e_cluster_seed { +// VPACK_TIMING, VPACK_MAX_INPUTS, VPACK_BLEND +//}; +// +//enum e_block_pack_status { +// BLK_PASSED, +// BLK_FAILED_FEASIBLE, +// BLK_FAILED_ROUTE, +// BLK_STATUS_UNDEFINED +//}; +// +//struct t_ext_pin_util { +// t_ext_pin_util() = default; +// t_ext_pin_util(float in, float out) +// : input_pin_util(in), output_pin_util(out) {} +// +// float input_pin_util = 1.; +// float output_pin_util = 1.; +//}; +// +////Specifies the utilization of external input/output pins +////during packing +//class t_ext_pin_util_targets { +//public: +// t_ext_pin_util_targets() = default; +// t_ext_pin_util_targets(float default_in_util, float default_out_util); +// +// //Returns the input pin util of the specified block (or default if unspecified) +// t_ext_pin_util get_pin_util(std::string block_type_name) const; +// +//public: +// //Sets the pin util for the specified block type +// //Returns true if non-default was previously set +// void set_block_pin_util(std::string block_type_name, t_ext_pin_util target); +// +// //Sets the default pin util +// //Returns true if a default was previously set +// void set_default_pin_util(t_ext_pin_util target); +//private: +// t_ext_pin_util defaults_; +// std::map overrides_; +//}; +// +///* these are defined later, but need to declare here because it is used */ +//class t_rr_node; +//struct t_pack_molecule; +//struct t_pb_stats; +//struct t_pb_route; +// +///* A t_pb represents an instance of a clustered block, which may be: +// * 1) A top level clustered block which is placeable at a location in FPGA device +// * grid location (e.g. a Logic block, RAM block, DSP block), or +// * 2) An internal 'block' representing an intermediate level of hierarchy inside a top level +// * block (e.g. a BLE), or +// * 3) A leaf (i.e. atom or primitive) block representing an element of netlist (e.g. LUT, +// * flip-lop, memory slice etc.) +// * +// * t_pb (in combination with t_pb_route) implement the mapping from the netlist elements to architectural +// * instances. +// */ +//struct t_pb { +// char *name = nullptr; /* Name of this physical block */ +// t_pb_graph_node *pb_graph_node = nullptr; /* pointer to pb_graph_node this pb corresponds to */ +// +// int mode = 0; /* mode that this pb is set to */ +// +// t_pb **child_pbs = nullptr; /* children pbs attached to this pb [0..num_child_pb_types - 1][0..child_type->num_pb - 1] */ +// t_pb *parent_pb = nullptr; /* pointer to parent node */ +// +// t_pb_stats *pb_stats = nullptr; /* statistics for current pb */ +// +// /* Representation of intra-logic block routing, t_pb_route describes all internal hierarchy routing. +// * t_pb_route is an array of size [t_pb->pb_graph_node->total_pb_pins] +// * Only valid for the top-level t_pb (parent_pb == nullptr). On any child pb, t_pb_route will be nullptr. */ +// t_pb_route *pb_route = nullptr; +// +// int clock_net = 0; /* Records clock net driving a flip-flop, valid only for lowest-level, flip-flop PBs */ +// +// +// int get_num_child_types() const { +// if (child_pbs != nullptr && has_modes()) { +// return pb_graph_node->pb_type->modes[mode].num_pb_type_children; +// } else { +// return 0; +// } +// } +// +// int get_num_children_of_type(int type_index) const { +// t_mode* mode_ptr = get_mode(); +// if (mode_ptr) { +// return mode_ptr->pb_type_children[type_index].num_pb; +// } +// return 0; //No mode +// } +// +// t_mode* get_mode() const { +// if (has_modes()) { +// return &pb_graph_node->pb_type->modes[mode]; +// } else { +// return nullptr; +// } +// } +// +// bool has_modes() const { +// return pb_graph_node->pb_type->num_modes > 0; +// } +// +// //Returns the t_pb associated with the specified gnode which is contained +// //within the current pb +// const t_pb* find_pb(const t_pb_graph_node* gnode) const { +// //Base case +// if(pb_graph_node == gnode) { +// return this; +// } +// +// //Search recursively +// for(int ichild_type = 0; ichild_type < get_num_child_types(); ++ichild_type) { +// +// if(child_pbs[ichild_type] == nullptr) continue; +// +// for(int ipb = 0; ipb < get_num_children_of_type(ichild_type); ++ipb) { +// +// const t_pb* child_pb = &child_pbs[ichild_type][ipb]; +// +// const t_pb* found_pb = child_pb->find_pb(gnode); +// if(found_pb != nullptr) { +// VTR_ASSERT(found_pb->pb_graph_node == gnode); +// return found_pb; //Found +// } +// } +// } +// return nullptr; //Not found +// } +// +// const t_pb* find_pb_for_model(const std::string& blif_model) const { +// //Base case +// const t_model* model = pb_graph_node->pb_type->model; +// if (model && model->name == blif_model) { +// return this; +// } +// +// //Search recursively +// for(int ichild_type = 0; ichild_type < get_num_child_types(); ++ichild_type) { +// +// if(child_pbs[ichild_type] == nullptr) continue; +// +// for(int ipb = 0; ipb < get_num_children_of_type(ichild_type); ++ipb) { +// +// const t_pb* child_pb = &child_pbs[ichild_type][ipb]; +// +// const t_pb* matching_pb = child_pb->find_pb_for_model(blif_model); +// if (matching_pb) { +// return this; +// } +// } +// } +// return nullptr; //Not found +// } +// +// //Returns the root pb containing this pb +// const t_pb* root_pb() const { +// +// const t_pb* curr_pb = this; +// while(!is_root()) { +// curr_pb = curr_pb->parent_pb; +// } +// +// VTR_ASSERT(curr_pb->parent_pb == nullptr); +// return curr_pb; +// } +// +// bool is_root() const { +// return parent_pb == nullptr; +// } +// +// //Returns true if this pb corresponds to a primitive block (i.e. in the AtomNetlist) +// bool is_primitive() const { +// return child_pbs == nullptr; +// } +// +// //Returns the bit index into the AtomPort for the specified primitive +// //pb_graph_pin, considering any pin rotations which have been applied to logically +// //equivalent pins +// BitIndex atom_pin_bit_index(const t_pb_graph_pin* gpin) const { +// VTR_ASSERT_MSG(is_primitive(), "Atom pin indicies can only be looked up from primitives"); +// +// auto iter = pin_rotations_.find(gpin); +// +// if(iter != pin_rotations_.end()) { +// //Return the original atom pin index +// return iter->second; +// } else { +// //No re-mapping, return the index directly +// return gpin->pin_number; +// } +// } +// +// //For a given gpin, sets the mapping to the original atom netlist pin's bit index in +// //it's AtomPort. This is used to record any pin rotations which have been applied to +// //logically equivalent pins +// void set_atom_pin_bit_index(const t_pb_graph_pin* gpin, BitIndex atom_pin_bit_idx) { +// pin_rotations_[gpin] = atom_pin_bit_idx; +// } +// +//private: +// std::map pin_rotations_; //Contains the atom netlist port bit index associated +// //with any primitive pins which have been rotated during clustering +// +//}; +// +///* Representation of intra-logic block routing */ +//struct t_pb_route { +// AtomNetId atom_net_id; /* which net in the atom netlist uses this pin */ +// int driver_pb_pin_id; /* The pb_pin id of the pb_pin that drives this pin */ +// std::vector sink_pb_pin_ids; /* The pb_pin id's of the pb_pins driven by this node */ +// const t_pb_graph_pin* pb_graph_pin = nullptr; /* The graph pin associated with this node */ +// +// t_pb_route() { +// atom_net_id = AtomNetId::INVALID(); +// driver_pb_pin_id = OPEN; +// } +//}; +// +//enum e_pack_pattern_molecule_type { +// MOLECULE_SINGLE_ATOM, MOLECULE_FORCED_PACK +//}; +// +///* Represents a grouping of atom blocks that match a pack_pattern, these groups are intended to be placed as a single unit during packing +// * Store in linked list +// * +// * A chain is a special type of pack pattern. A chain can extend across multiple logic blocks. +// * Must segment the chain to fit in a logic block by identifying the actual atom that forms the root of the new chain. +// * Assumes that the root of a chain is the primitive that starts the chain or is driven from outside the logic block +// */ +//struct t_pack_molecule { +// enum e_pack_pattern_molecule_type type; /* what kind of molecule is this? */ +// t_pack_patterns *pack_pattern; /* If this is a forced_pack molecule, pattern this molecule matches */ +// t_model_chain_pattern *chain_pattern; /* If this is a chain molecule, chain that this molecule matches */ +// std::vector atom_block_ids; /* [0..num_blocks-1] IDs of atom blocks that implements this molecule, +// index on pack_pattern_block->index of pack pattern */ +// bool valid; /* Whether or not this molecule is still valid */ +// +// int num_blocks; /* number of atom blocks of molecule */ +// int root; /* root index of molecule, atom_block_ids[root] is the root atom block */ +// +// float base_gain; /* Intrinsic "goodness" score for molecule independant of rest of netlist */ +// +// int num_ext_inputs; /* number of input pins used by molecule that are not self-contained by pattern molecule matches */ +// t_pack_molecule *next; +//}; +// +///* Stats keeper for placement information during packing +// * Contains linked lists to placement locations based on status of primitive +// */ +//struct t_cluster_placement_stats { +// int num_pb_types; /* num primitive pb_types inside complex block */ +// const t_pack_molecule *curr_molecule; /* current molecule being considered for packing */ +// t_cluster_placement_primitive **valid_primitives; /* [0..num_pb_types-1] ptrs to linked list of valid primitives, for convenience, each linked list head is empty */ +// t_cluster_placement_primitive *in_flight; /* ptrs to primitives currently being considered */ +// t_cluster_placement_primitive *tried; /* ptrs to primitives that are open but current logic block unable to pack to */ +// t_cluster_placement_primitive *invalid; /* ptrs to primitives that are invalid */ +//}; +// +///****************************************************************** +// * Timing data types +// *******************************************************************/ +// +////Enable the legacy STA engine to run along side the +////new STA engine +////#define ENABLE_CLASSIC_VPR_STA +// +//// #define PATH_COUNTING 'P' +///* Uncomment this to turn on path counting. Its value determines how path criticality +// is calculated from forward and backward weights. Possible values: +// 'S' - sum of forward and backward weights +// 'P' - product of forward and backward weights +// 'L' - natural log of the product of forward and backward weights +// 'R' - product of the natural logs of forward and backward weights +// See path_delay.h for further path-counting options. */ +// +///* Timing graph information */ +//struct t_tedge { +// /* Edge in the timing graph. */ +// int to_node; /* index of node at the sink end of this edge */ +// float Tdel; /* delay to go to to_node along this edge */ +//}; +// +//enum e_tnode_type { +// /* Types of tnodes (timing graph nodes). */ +// TN_INPAD_SOURCE, /* input to an input I/O pad */ +// TN_INPAD_OPIN, /* output from an input I/O pad */ +// TN_OUTPAD_IPIN, /* input to an output I/O pad */ +// TN_OUTPAD_SINK, /* output from an output I/O pad */ +// TN_CB_IPIN, /* input pin to complex block */ +// TN_CB_OPIN, /* output pin from complex block */ +// TN_INTERMEDIATE_NODE, /* Used in post-packed timing graph only: +// connection between intra-cluster pins. */ +// TN_PRIMITIVE_IPIN, /* input pin to a primitive (e.g. a LUT) */ +// TN_PRIMITIVE_OPIN, /* output pin from a primitive (e.g. a LUT) */ +// TN_FF_IPIN, /* input pin to a flip-flop - goes to TN_FF_SINK */ +// TN_FF_OPIN, /* output pin from a flip-flop - comes from TN_FF_SOURCE */ +// TN_FF_SINK, /* sink (D) pin of flip-flop */ +// TN_FF_SOURCE, /* source (Q) pin of flip-flop */ +// TN_FF_CLOCK, /* clock pin of flip-flop */ +// TN_CLOCK_SOURCE, /* An on-chip clock generator such as a pll */ +// TN_CLOCK_OPIN, /* Output pin from an on-chip clock source - comes from TN_CLOCK_SOURCE */ +// TN_CONSTANT_GEN_SOURCE /* source of a constant logic 1 or 0 */ +//}; +// +//struct t_prepacked_tnode_data { +// /* Data only used by prepacked tnodes. Stored separately so it +// doesn't need to be allocated in the post-packed netlist. */ +// int model_port, model_pin; /* technology mapped model pin */ +// t_model_ports *model_port_ptr; +//#ifndef PATH_COUNTING +// long num_critical_input_paths, num_critical_output_paths; /* count of critical paths fanning into/out of this tnode */ +// float normalized_slack; /* slack (normalized with respect to max slack) */ +// float normalized_total_critical_paths; /* critical path count (normalized with respect to max count) */ +// float normalized_T_arr; /* arrival time (normalized with respect to max time) */ +//#endif +//}; +// +//struct t_tnode { +// /* Node in the timing graph. Note: we combine 2 members into a bit field. */ +// e_tnode_type type; /* see the above enum */ +// t_tedge *out_edges; /* [0..num_edges - 1] array of edges fanning out from this tnode. +// Note: there is a correspondence in indexing between out_edges and the +// net data structure: out_edges[iedge] = net[inet].node_block[iedge + 1] +// There is an offset of 1 because net[inet].node_block includes the driver +// node at index 0, while out_edges is part of the driver node and does +// not bother to refer to itself. */ +// int num_edges; +// float T_arr; /* Arrival time of the last input signal to this node. */ +// float T_req; /* Required arrival time of the last input signal to this node +// if the critical path is not to be lengthened. */ +// ClusterBlockId block; /* atom block primitive which this tnode is part of */ +// +//#ifdef PATH_COUNTING +// float forward_weight, backward_weight; /* Weightings of the importance of paths +// fanning into and out of this node, respectively. */ +//#endif +// +// /* Valid values for TN_FF_SINK, TN_FF_SOURCE, TN_FF_CLOCK, TN_INPAD_SOURCE, and TN_OUTPAD_SINK only: */ +// int clock_domain; /* Index of the clock in timing_ctx.sdc->constrained_clocks which this flip-flop or I/O is constrained on. */ +// float clock_delay; /* The time taken for a clock signal to get to the flip-flop or I/O (assumed 0 for I/Os). */ +// +// /* Used in post-packing timing graph only: */ +// t_pb_graph_pin *pb_graph_pin; /* pb_graph_pin that this block is connected to */ +// +// /* Used in pre-packing timing graph only: */ +// t_prepacked_tnode_data * prepacked_data; +// +// unsigned int is_comb_loop_breakpoint : 1; /* Indicates that this tnode had input edges purposely +// disconnected to break a combinational loop */ +//}; +// +///* Other structures storing timing information */ +//struct t_clock { +// /* Stores information on clocks given timing constraints. +// Used in SDC parsing and timing analysis. */ +// char * name; +// bool is_netlist_clock; /* Is this a netlist or virtual (external) clock? */ +// int fanout; +//}; +// +//struct t_io { +// /* Stores information on I/Os given timing constraints. +// Used in SDC parsing and timing analysis. */ +// char * name; /* I/O port name with an SDC constraint */ +// char * clock_name; /* Clock it was constrained on */ +// float delay; /* Delay through the I/O in this constraint */ +// int file_line_number; /* line in the SDC file I/O was constrained on - used for error reporting */ +//}; +// +//struct t_timing_stats { +// /* Timing statistics for final reporting for each constraint +// (pair of constrained source and sink clock domains). +// +// cpd holds the critical path delay, the longest path between the +// pair of domains, or equivalently the path with the least slack. +// +// least_slack holds the slack of the connection with the least slack +// over all paths in this constraint, even if this connection is part +// of another constraint and has a lower slack from that constraint. +// +// The "critical path" of the entire design is the path with the least +// slack in the constraint with the least slack +// (see get_critical_path_delay()). */ +// +// float ** cpd; +// float ** least_slack; +//}; +// +//struct t_slack { +// /* Matrices storing slacks and criticalities of each sink pin on each net +// [0..net.size()-1][1..num_pins-1] for post-packed netlists. */ +// float ** slack; +// float ** timing_criticality; +//}; +// +//struct t_override_constraint { +// /* A special-case constraint to override the default, calculated, timing constraint. Holds data from +// set_clock_groups, set_false_path, set_max_delay, and set_multicycle_path commands. Can hold data for +// clock-to-clock, clock-to-flip-flop, flip-flop-to-clock or flip-flop-to-flip-flop constraints, each of +// which has its own array (timing_ctx.sdc->cc_constraints, timing_ctx.sdc->cf_constraints, timing_ctx.sdc->fc_constraints, and timing_ctx.sdc->ff_constraints). */ +// char ** source_list; /* Array of net names of flip-flops or clocks */ +// char ** sink_list; +// int num_source; +// int num_sink; +// float constraint; +// int num_multicycles; +// int file_line_number; /* line in the SDC file clock was constrained on - used for error reporting */ +//}; +// +//struct t_timing_constraints { /* Container structure for all SDC timing constraints. +// See top-level comment to read_sdc.c for details on members. */ +// int num_constrained_clocks; /* number of clocks with timing constraints */ +// t_clock * constrained_clocks; /* [0..timing_ctx.sdc->num_constrained_clocks - 1] array of clocks with timing constraints */ +// +// vtr::Matrix domain_constraint; /* [0..num_constrained_clocks - 1 (source)][0..num_constrained_clocks - 1 (destination)] */ +// +// int num_constrained_inputs; /* number of inputs with timing constraints */ +// t_io * constrained_inputs; /* [0..num_constrained_inputs - 1] array of inputs with timing constraints */ +// +// int num_constrained_outputs; /* number of outputs with timing constraints */ +// t_io * constrained_outputs; /* [0..num_constrained_outputs - 1] array of outputs with timing constraints */ +// +// int num_cc_constraints; /* number of special-case clock-to-clock constraints overriding default, calculated, timing constraints */ +// t_override_constraint * cc_constraints; /* [0..num_cc_constraints - 1] array of such constraints */ +// +// int num_cf_constraints; /* number of special-case clock-to-flipflop constraints */ +// t_override_constraint * cf_constraints; /* [0..num_cf_constraints - 1] array of such constraints */ +// +// int num_fc_constraints; /* number of special-case flipflop-to-clock constraints */ +// t_override_constraint * fc_constraints; /* [0..num_fc_constraints - 1] */ +// +// int num_ff_constraints; /* number of special-case flipflop-to-flipflop constraints */ +// t_override_constraint * ff_constraints; /* [0..num_ff_constraints - 1] array of such constraints */ +//}; +// +///* Cluster timing delays: +// * C_ipin_cblock: Capacitance added to a routing track by the isolation * +// * buffer between a track and the Cblocks at an (i,j) loc. * +// * T_ipin_cblock: Delay through an input pin connection box (from a * +// * routing track to a logic block input pin). */ +//struct t_timing_inf { +// bool timing_analysis_enabled; +// float C_ipin_cblock; +// float T_ipin_cblock; +// std::string SDCFile; +// std::string slack_definition; +//}; + +/*************************************************************************** + * Placement and routing data types + ****************************************************************************/ + +/* Timing data structures end */ +enum sched_type { + AUTO_SCHED, USER_SCHED +}; +/* Annealing schedule */ + +//enum pic_type { +// NO_PICTURE, PLACEMENT, ROUTING +//}; +///* What's on screen? */ +// +//enum pfreq { +// PLACE_NEVER, PLACE_ONCE, PLACE_ALWAYS +//}; +// +///* Are the pads free to be moved, locked in a random configuration, or +// * locked in user-specified positions? */ +//enum e_pad_loc_type { +// FREE, RANDOM, USER +//}; +// +///* Power data for t_netlist structure */ +//struct t_net_power { +// /* Signal probability - long term probability that signal is logic-high*/ +// float probability; +// +// /* Transistion density - average # of transitions per clock cycle +// * For example, a clock would have density = 2 +// */ +// float density; +//}; +// +///* s_grid_tile is the minimum tile of the fpga +// * type: Pointer to type descriptor, NULL for illegal +// * width_offset: Number of grid tiles reserved based on width (right) of a block +// * height_offset: Number of grid tiles reserved based on height (top) of a block */ +//struct t_grid_tile { +// t_type_ptr type = nullptr; +// int width_offset = 0; +// int height_offset = 0; +//}; + +/* Stores the bounding box of a net in terms of the minimum and * + * maximum coordinates of the blocks forming the net, clipped to * + * the region: * + * (1..device_ctx.grid.width()-2, 1..device_ctx.grid.height()-1) */ +struct t_bb { + int xmin; + int xmax; + int ymin; + int ymax; +}; + +///* capacity: Capacity of this region, in tracks. * +// * occupancy: Expected number of tracks that will be occupied. * +// * cost: Current cost of this usage. */ +//struct t_place_region { +// float capacity; +// float inv_capacity; +// float occupancy; +// float cost; +//}; +// +///* Stores the information of the move for a block that is * +// * moved during placement * +// * block_num: the index of the moved block * +// * xold: the x_coord that the block is moved from * +// * xnew: the x_coord that the block is moved to * +// * yold: the y_coord that the block is moved from * +// * xnew: the x_coord that the block is moved to */ +//struct t_pl_moved_block { +// ClusterBlockId block_num; +// int xold; +// int xnew; +// int yold; +// int ynew; +// int zold; +// int znew; +// int swapped_to_was_empty; +// int swapped_from_is_empty; +//}; +// +///* Stores the list of blocks to be moved in a swap during * +// * placement. * +// * num_moved_blocks: total number of blocks moved when * +// * swapping two blocks. * +// * moved blocks: a list of moved blocks data structure with * +// * information on the move. * +// * [0...num_moved_blocks-1] */ +//struct t_pl_blocks_to_be_moved { +// int num_moved_blocks; +// t_pl_moved_block * moved_blocks; +//}; +// +///* legal positions for type */ +//struct t_legal_pos { +// int x; +// int y; +// int z; +//}; +// +///* Represents the placement location of a clustered block +// * x: x-coordinate +// * y: y-coordinate +// * z: occupancy coordinate +// * is_fixed: true if this block's position is fixed by the user and shouldn't be moved during annealing +// * nets_and_pins_synced_to_z_coordinate: true if the associated clb's pins have been synced to the z location (i.e. after placement) */ +//struct t_block_loc { +// int x = OPEN; +// int y = OPEN; +// int z = OPEN; +// +// bool is_fixed = false; +// bool nets_and_pins_synced_to_z_coordinate = false; +//}; +// +///* Stores the clustered blocks placed at a particular grid location */ +//struct t_grid_blocks { +// //How many valid blocks are in use at this location +// int usage; +// +// //The clustered blocks associated with this grid location +// std::vector blocks; +//}; +// +///* Names of various files */ +//struct t_file_name_opts { +// std::string ArchFile; +// std::string CircuitName; +// std::string BlifFile; +// std::string NetFile; +// std::string PlaceFile; +// std::string RouteFile; +// std::string ActFile; +// std::string PowerFile; +// std::string CmosTechFile; +// std::string out_file_prefix; +// bool verify_file_digests; +//}; +// +///* Options for netlist loading */ +//struct t_netlist_opts { +// bool absorb_buffer_luts = true; +// bool sweep_dangling_primary_ios = true; +// bool sweep_dangling_blocks = true; +// bool sweep_dangling_nets = true; +// bool sweep_constant_primary_outputs = false; +// +// bool verbose_sweep = false; //Verbose output during netlist cleaning +//}; +// +////Should a stage in the CAD flow be skipped, loaded from a file, or performed +//enum e_stage_action { +// STAGE_SKIP = 0, +// STAGE_LOAD, +// STAGE_DO +//}; +// +///* Options for packing +// * TODO: document each packing parameter */ +//enum e_packer_algorithm { +// PACK_GREEDY, PACK_BRUTE_FORCE +//}; +// +//struct t_packer_opts { +// std::string blif_file_name; +// std::string sdc_file_name; +// std::string output_file; +// bool global_clocks; +// bool hill_climbing_flag; +// bool timing_driven; +// enum e_cluster_seed cluster_seed_type; +// float alpha; +// float beta; +// float inter_cluster_net_delay; +// float target_device_utilization; +// bool auto_compute_inter_cluster_net_delay; +// bool allow_unrelated_clustering; +// bool connection_driven; +// bool debug_clustering; +// bool enable_pin_feasibility_filter; +// t_ext_pin_util_targets target_external_pin_util; +// e_stage_action doPacking; +// enum e_packer_algorithm packer_algorithm; +// std::string device_layout; +// std::string hmetis_input_file; +//}; + +/* Annealing schedule information for the placer. The schedule type * + * is either USER_SCHED or AUTO_SCHED. Inner_num is multiplied by * + * num_blocks^4/3 to find the number of moves per temperature. The * + * remaining information is used only for USER_SCHED, and have the * + * obvious meanings. */ +struct t_annealing_sched { + enum sched_type type; + float inner_num; + float init_t; + float alpha_t; + float exit_t; +}; + +/* Various options for the placer. * + * place_algorithm: BOUNDING_BOX_PLACE or PATH_TIMING_DRIVEN_PLACE * + * timing_tradeoff: When TIMING_DRIVEN_PLACE mode, what is the tradeoff * + * timing driven and BOUNDING_BOX_PLACE. * + * place_cost_exp: Power to which denominator is raised for linear_cong. * + * place_chan_width: The channel width assumed if only one placement is * + * performed. * + * pad_loc_type: Are pins FREE, fixed randomly, or fixed from a file. * + * pad_loc_file: File to read pin locations form if pad_loc_type * + * is USER. * + * place_freq: Should the placement be skipped, done once, or done for each * + * channel width in the binary search. * + * recompute_crit_iter: how many temperature stages pass before we recompute * + * criticalities based on average point to point delay * + * enable_timing_computations: in bounding_box mode, normally, timing * + * information is not produced, this causes the information * + * to be computed. in *_TIMING_DRIVEN modes, this has no effect* + * inner_loop_crit_divider: (move_lim/inner_loop_crit_divider) determines how* + * many inner_loop iterations pass before a recompute of * + * criticalities is done. * + * td_place_exp_first: exponent that is used on the timing_driven criticlity * + * it is the value that the exponent starts at. * + * td_place_exp_last: value that the criticality exponent will be at the end * + * doPlacement: true if placement is supposed to be done in the CAD flow, false otherwise */ +enum e_place_algorithm { + BOUNDING_BOX_PLACE, PATH_TIMING_DRIVEN_PLACE +}; + +struct t_placer_opts { + enum e_place_algorithm place_algorithm; + float timing_tradeoff; + float place_cost_exp; + int place_chan_width; +// enum e_pad_loc_type pad_loc_type; + std::string pad_loc_file; +// enum pfreq place_freq; + int recompute_crit_iter; + bool enable_timing_computations; + int inner_loop_recompute_divider; + float td_place_exp_first; + int seed; + float td_place_exp_last; +// e_stage_action doPlacement; +}; + +///* All the parameters controlling the router's operation are in this * +// * structure. * +// * first_iter_pres_fac: Present sharing penalty factor used for the * +// * very first (congestion mapping) Pathfinder iteration. * +// * initial_pres_fac: Initial present sharing penalty factor for * +// * Pathfinder; used to set pres_fac on 2nd iteration. * +// * pres_fac_mult: Amount by which pres_fac is multiplied each * +// * routing iteration. * +// * acc_fac: Historical congestion cost multiplier. Used unchanged * +// * for all iterations. * +// * bend_cost: Cost of a bend (usually non-zero only for global routing). * +// * max_router_iterations: Maximum number of iterations before giving * +// * up. * +// * min_incremental_reroute_fanout: Minimum fanout a net needs to have * +// * for incremental reroute to be applied to it through route * +// * tree pruning. Larger circuits should get larger thresholds * +// * bb_factor: Linear distance a route can go outside the net bounding * +// * box. * +// * route_type: GLOBAL or DETAILED. * +// * fixed_channel_width: Only attempt to route the design once, with the * +// * channel width given. If this variable is * +// * == NO_FIXED_CHANNEL_WIDTH, do a binary search * +// * on channel width. * +// * router_algorithm: BREADTH_FIRST or TIMING_DRIVEN. Selects the desired * +// * routing algorithm. * +// * base_cost_type: Specifies how to compute the base cost of each type of * +// * rr_node. DELAY_NORMALIZED -> base_cost = "demand" * +// * x average delay to route past 1 CLB. DEMAND_ONLY -> * +// * expected demand of this node (old breadth-first costs). * +// * * +// * The following parameters are used only by the timing-driven router. * +// * * +// * astar_fac: Factor (alpha) used to weight expected future costs to * +// * target in the timing_driven router. astar_fac = 0 leads to * +// * an essentially breadth-first search, astar_fac = 1 is near * +// * the usual astar algorithm and astar_fac > 1 are more * +// * aggressive. * +// * max_criticality: The maximum criticality factor (from 0 to 1) any sink * +// * will ever have (i.e. clip criticality to this number). * +// * criticality_exp: Set criticality to (path_length(sink) / longest_path) ^ * +// * criticality_exp (then clip to max_criticality). * +// * doRouting: true if routing is supposed to be done, false otherwise * +// * routing_failure_predictor: sets the configuration to be used by the * +// * routing failure predictor, how aggressive the threshold used to judge * +// * and abort routings deemed unroutable * +// * write_rr_graph_name: stores the file name of the output rr graph * +// * read_rr_graph_name: stores the file name of the rr graph to be read by vpr */ +//enum e_route_type { +// GLOBAL, DETAILED +//}; +//enum e_router_algorithm { +// BREADTH_FIRST, TIMING_DRIVEN, NO_TIMING +//}; +//enum e_base_cost_type { +// DELAY_NORMALIZED, DEMAND_ONLY +//}; +//enum e_routing_failure_predictor { +// OFF, SAFE, AGGRESSIVE +//}; +//enum e_routing_budgets_algorithm { +// MINIMAX, SCALE_DELAY, DISABLE +//}; +// +//enum class e_timing_report_detail { +// NETLIST, //Only show netlist elements +// AGGREGATED, //Show aggregated intra-block and inter-block delays +// //DETAILED_ROUTING, //Show inter-block routing resources used +//}; +// +//constexpr int NO_FIXED_CHANNEL_WIDTH = -1; +// +//struct t_router_opts { +// float first_iter_pres_fac; +// float initial_pres_fac; +// float pres_fac_mult; +// float acc_fac; +// float bend_cost; +// int max_router_iterations; +// int min_incremental_reroute_fanout; +// int bb_factor; +// enum e_route_type route_type; +// int fixed_channel_width; +// int min_channel_width_hint; //Hint to binary search of what the minimum channel width is +// bool trim_empty_channels; +// bool trim_obs_channels; +// enum e_router_algorithm router_algorithm; +// enum e_base_cost_type base_cost_type; +// float astar_fac; +// float max_criticality; +// float criticality_exp; +// bool verify_binary_search; +// bool full_stats; +// bool congestion_analysis; +// bool fanout_analysis; +// bool switch_usage_analysis; +// e_stage_action doRouting; +// enum e_routing_failure_predictor routing_failure_predictor; +// enum e_routing_budgets_algorithm routing_budgets_algorithm; +//}; +// +//struct t_analysis_opts { +// e_stage_action doAnalysis; +// +// bool gen_post_synthesis_netlist; +// +// int timing_report_npaths; +// e_timing_report_detail timing_report_detail; +// bool timing_report_skew; +//}; +// +///* Defines the detailed routing architecture of the FPGA. Only important * +// * if the route_type is DETAILED. * +// * (UDSD by AY) directionality: Should the tracks be uni-directional or * +// * bi-directional? * +// * switch_block_type: Pattern of switches at each switch block. I * +// * assume Fs is always 3. If the type is SUBSET, I use a * +// * Xilinx-like switch block where track i in one channel always * +// * connects to track i in other channels. If type is WILTON, * +// * I use a switch block where track i does not always connect * +// * to track i in other channels. See Steve Wilton, Phd Thesis, * +// * University of Toronto, 1996. The UNIVERSAL switch block is * +// * from Y. W. Chang et al, TODAES, Jan. 1996, pp. 80 - 101. * +// * A CUSTOM switch block has also been added which allows a user * +// * to describe custom permutation functions and connection * +// * patterns. See comment at top of SRC/route/build_switchblocks.c * +// * switchblocks: A vector of custom switch block descriptions that is * +// * used with the CUSTOM switch block type. See comment at top of * +// * SRC/route/build_switchblocks.c * +// * num_segment: Number of distinct segment types in the FPGA. * +// * delayless_switch: Index of a zero delay switch (used to connect things * +// * that should have no delay). * +// * wire_to_arch_ipin_switch: keeps track of the type of architecture switch * +// * that connects wires to ipins * +// * wire_to_rr_ipin_switch: keeps track of the type of RR graph switch that * +// * connects wires to ipins in the RR graph * +// * R_minW_nmos: Resistance (in Ohms) of a minimum width nmos transistor. * +// * Used only in the FPGA area model. * +// * R_minW_pmos: Resistance (in Ohms) of a minimum width pmos transistor. * +// * * +// * read_rr_graph_filename: File to read the RR graph from (overrides * +// * architecture) * +// * write_rr_graph_filename: File to write the RR graph to after generation * +// * */ +// +//struct t_det_routing_arch { +// enum e_directionality directionality; /* UDSD by AY */ +// int Fs; +// enum e_switch_block_type switch_block_type; +// std::vector switchblocks; +// int num_segment; +// +// short global_route_switch; +// short delayless_switch; +// int wire_to_arch_ipin_switch; +// int wire_to_rr_ipin_switch; +// float R_minW_nmos; +// float R_minW_pmos; +// +// std::string read_rr_graph_filename; +// std::string write_rr_graph_filename; +//}; +// +//enum e_direction : unsigned char { +// INC_DIRECTION = 0, +// DEC_DIRECTION = 1, +// BI_DIRECTION = 2, +// NO_DIRECTION = 3, +// NUM_DIRECTIONS +//}; +// +//constexpr std::array DIRECTIONS_STRING = { {"INC_DIRECTION", "DEC_DIRECTION", "BI_DIRECTION", "NO_DIRECTION"} }; +// +///* Lists detailed information about segmentation. [0 .. W-1]. * +// * length: length of segment. * +// * start: index at which a segment starts in channel 0. * +// * longline: true if this segment spans the entire channel. * +// * sb: [0..length]: true for every channel intersection, relative to the * +// * segment start, at which there is a switch box. * +// * cb: [0..length-1]: true for every logic block along the segment at * +// * which there is a connection box. * +// * arch_wire_switch: Index of the switch type that connects other wires * +// * *to* this segment. Note that this index is in relation * +// * to the switches from the architecture file, not the * +// * expanded list of switches that is built at the end of * +// * build_rr_graph. * +// * arch_opin_switch: Index of the switch type that connects output pins * +// * (OPINs) *to* this segment. Note that this index is in * +// * relation to the switches from the architecture file, * +// * not the expanded list of switches that is is built * +// * at the end of build_rr_graph * +// * Cmetal: Capacitance of a routing track, per unit logic block length. * +// * Rmetal: Resistance of a routing track, per unit logic block length. * +// * direction: The direction of a routing track. * +// * index: index of the segment type used for this track. * +// * type_name_ptr: pointer to name of the segment type this track belongs * +// * to. points to the appropriate name in s_segment_inf */ +//struct t_seg_details { +// int length; +// int start; +// bool longline; +// bool *sb; +// bool *cb; +// short arch_wire_switch; +// short arch_opin_switch; +// float Rmetal; +// float Cmetal; +// bool twisted; +// enum e_direction direction; +// int group_start; +// int group_size; +// int seg_start; +// int seg_end; +// int index; +// float Cmetal_per_m; /* Used for power */ +// const char *type_name_ptr; +//}; +// +///* Defines a 2-D array of t_seg_details data structures (one per channel) */ +//typedef vtr::Matrix t_chan_details; +// +///* A linked list of float pointers. Used for keeping track of * +// * which pathcosts in the router have been changed. */ +// +//struct t_linked_f_pointer { +// t_linked_f_pointer *next; +// float *fptr; +//}; +// +//typedef std::vector>>>> t_rr_node_indices; //[0..num_rr_types-1][0..grid_width-1][0..grid_height-1][0..NUM_SIDES-1][0..max_ptc-1] +// +///* Uncomment lines below to save some memory, at the cost of debugging ease. */ +///*enum e_rr_type {SOURCE, SINK, IPIN, OPIN, CHANX, CHANY}; */ +///* typedef short t_rr_type */ +// +///* Type of a routing resource node. x-directed channel segment, * +// * y-directed channel segment, input pin to a clb to pad, output * +// * from a clb or pad (i.e. output pin of a net) and: * +// * SOURCE: A dummy node that is a logical output within a block * +// * -- i.e., the gate that generates a signal. * +// * SINK: A dummy node that is a logical input within a block * +// * -- i.e. the gate that needs a signal. */ +//typedef enum e_rr_type : unsigned char { +// SOURCE = 0, SINK, IPIN, OPIN, CHANX, CHANY, INTRA_CLUSTER_EDGE, NUM_RR_TYPES +//} t_rr_type; +// +//constexpr std::array RR_TYPES = { { +// SOURCE, SINK, IPIN, OPIN, CHANX, CHANY, INTRA_CLUSTER_EDGE +//} }; +//constexpr std::array rr_node_typename { { +// "SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY", "INTRA_CLUSTER_EDGE" +//} }; +// +///* Basic element used to store the traceback (routing) of each net. * +// * index: Array index (ID) of this routing resource node. * +// * iswitch: Index of the switch type used to go from this rr_node to * +// * the next one in the routing. OPEN if there is no next node * +// * (i.e. this node is the last one (a SINK) in a branch of the * +// * net's routing). * +// * next: Pointer to the next traceback element in this route. */ +//struct t_trace { +// t_trace *next; +// int index; +// short iswitch; +//}; +// +///* Extra information about each rr_node needed only during routing (i.e. * +// * during the maze expansion). * +// * * +// * prev_node: Index of the previous node (on the lowest cost path known to * +// * reach this node); used to generate the traceback. If there * +// * is no predecessor, prev_node = NO_PREVIOUS. * +// * prev_edge: Index of the edge (from 0 to num_edges-1 of prev_node) that * +// * was used to reach this node from the previous node. If * +// * there is no predecessor, prev_edge = NO_PREVIOUS. * +// * pres_cost: Present congestion cost term for this node. * +// * acc_cost: Accumulated cost term from previous Pathfinder iterations. * +// * path_cost: Total cost of the path up to and including this node + * +// * the expected cost to the target if the timing_driven router * +// * is being used. * +// * backward_path_cost: Total cost of the path up to and including this * +// * node. Not used by breadth-first router. * +// * target_flag: Is this node a target (sink) for the current routing? * +// * Number of times this node must be reached to fully route. * +// * occ: The current occupancy of the associated rr node */ +//struct t_rr_node_route_inf { +// int prev_node; +// short prev_edge; +// +// float pres_cost; +// float acc_cost; +// float path_cost; +// float backward_path_cost; +// +// short target_flag; +// +// public: //Accessors +// short occ() const { return occ_; } +// +// public: //Mutators +// void set_occ(int new_occ) { occ_ = new_occ; } +// +// private: //Data +// short occ_ = 0; +//}; +// +////Information about the current status of a particular net as pertains to routing +//struct t_net_routing_status { +// bool is_routed = false; //Whether the net has been legally routed +// bool is_fixed = false; //Whether the net is fixed (i.e. not to be re-routed) +//}; +// +// +// +//#define NO_PREVIOUS -1 +// +///* Index of the SOURCE, SINK, OPIN, IPIN, etc. member of device_ctx.rr_indexed_data. */ +//enum e_cost_indices { +// SOURCE_COST_INDEX = 0, +// SINK_COST_INDEX, +// OPIN_COST_INDEX, +// IPIN_COST_INDEX, +// CHANX_COST_INDEX_START +//}; +// +///* Power estimation options */ +//struct t_power_opts { +// bool do_power; /* Perform power estimation? */ +//}; +// +///* Channel width data */ +//struct t_chan_width { +// int max; +// int x_max; +// int y_max; +// int x_min; +// int y_min; +// int *x_list; +// int *y_list; +//}; +// +///* Type to store our list of token to enum pairings */ +//struct t_TokenPair { +// const char *Str; +// int Enum; +//}; +// +//struct t_lb_type_rr_node; /* Defined in pack_types.h */ +// +///* Store settings for VPR */ +//struct t_vpr_setup { +// bool TimingEnabled; /* Is VPR timing enabled */ +// t_file_name_opts FileNameOpts; /* File names */ +// t_model * user_models; /* blif models defined by the user */ +// t_model * library_models; /* blif models in VPR */ +// t_netlist_opts NetlistOpts; /* Options for packer */ +// t_packer_opts PackerOpts; /* Options for packer */ +// t_placer_opts PlacerOpts; /* Options for placer */ +// t_annealing_sched AnnealSched; /* Placement option annealing schedule */ +// t_router_opts RouterOpts; /* router options */ +// t_analysis_opts AnalysisOpts; /* Analysis options */ +// t_det_routing_arch RoutingArch; /* routing architecture */ +// std::vector *PackerRRGraph; +// t_segment_inf * Segments; /* wires in routing architecture */ +// t_timing_inf Timing; /* timing information */ +// float constant_net_delay; /* timing information when place and route not run */ +// bool ShowGraphics; /* option to show graphics */ +// bool gen_netlist_as_blif; /* option to print out post-pack/pre-place netlist as blif */ +// int GraphPause; /* user interactiveness graphics option */ +// t_power_opts PowerOpts; +// std::string device_layout; +// e_constant_net_method constant_net_method; //How constant nets should be handled +// e_clock_modeling clock_modeling; //How clocks should be handled +//}; +// +//class RouteStatus { +// public: +// RouteStatus() = default; +// RouteStatus(bool status_val, int chan_width_val) +// : success_(status_val) +// , chan_width_(chan_width_val) {} +// +// //Was routing successful? +// operator bool() { return success(); } +// bool success() { return success_; } +// +// //What was the channel width? +// int chan_width() { return chan_width_; } +// +// private: +// bool success_ = false; +// int chan_width_ = -1; +//}; +// +//typedef vtr::vector_map>> t_clb_opins_used; //[0..num_blocks-1][0..class-1][0..used_pins-1] + +#endif diff --git a/vpr/place/place.cpp b/vpr/place/place.cpp new file mode 100644 index 0000000000..aabceddc84 --- /dev/null +++ b/vpr/place/place.cpp @@ -0,0 +1,3438 @@ +//#include +//#include +//#include +//#include +//using namespace std; +// +//#include "vtr_assert.h" +//#include "vtr_log.h" +//#include "vtr_util.h" +//#include "vtr_random.h" +//#include "vtr_matrix.h" + +#include "vpr/base/vpr_types.h" +//#include "vpr_error.h" +//#include "vpr_utils.h" +// +//#include "globals.h" +//#include "place.h" +//#include "read_place.h" +//#include "draw.h" +//#include "place_and_route.h" +//#include "net_delay.h" +//#include "path_delay.h" +//#include "timing_place_lookup.h" +//#include "timing_place.h" +//#include "read_xml_arch_file.h" +//#include "echo_files.h" +//#include "vpr_utils.h" +#include "vpr/place/place_macro.h" +//#include "histogram.h" +//#include "place_util.h" +// +//#include "PlacementDelayCalculator.h" +//#include "timing_util.h" +//#include "timing_info.h" +//#include "tatum/echo_writer.hpp" +// +///************** Types and defines local to place.c ***************************/ + +/* Cut off for incremental bounding box updates. * + * 4 is fastest -- I checked. */ +/* To turn off incremental bounding box updates, set this to a huge value */ +#define SMALL_NET 4 + +/* This defines the error tolerance for floating points variables used in * + * cost computation. 0.01 means that there is a 1% error tolerance. */ +#define ERROR_TOL .01 + +/* This defines the maximum number of swap attempts before invoking the * + * once-in-a-while placement legality check as well as floating point * + * variables round-offs check. */ +#define MAX_MOVES_BEFORE_RECOMPUTE 50000 + +///* The maximum number of tries when trying to place a carry chain at a * +// * random location before trying exhaustive placement - find the fist * +// * legal position and place it during initial placement. */ +#define MAX_NUM_TRIES_TO_PLACE_MACROS_RANDOMLY 4 + +/* Flags for the states of the bounding box. * + * Stored as char for memory efficiency. */ +#define NOT_UPDATED_YET 'N' +#define UPDATED_ONCE 'U' +#define GOT_FROM_SCRATCH 'S' + +/* For comp_cost. NORMAL means use the method that generates updateable * + * bounding boxes for speed. CHECK means compute all bounding boxes from * + * scratch using a very simple routine to allow checks of the other * + * costs. */ +enum e_cost_methods { + NORMAL, CHECK +}; + +/* This is for the placement swap routines. A swap attempt could be * + * rejected, accepted or aborted (due to the limitations placed on the * + * carry chain support at this point). */ +enum e_swap_result { + REJECTED, ACCEPTED, ABORTED +}; + +struct t_placer_statistics { + double av_cost, av_bb_cost, av_timing_cost, + sum_of_squares, av_delay_cost; + int success_sum; +}; + +#define MAX_INV_TIMING_COST 1.e9 +/* Stops inverse timing cost from going to infinity with very lax timing constraints, +which avoids multiplying by a gigantic inverse_prev_timing_cost when auto-normalizing. +The exact value of this cost has relatively little impact, but should not be +large enough to be on the order of timing costs for normal constraints. */ + +/********************** Variables local to place.c ***************************/ + +/* Cost of a net, and a temporary cost of a net used during move assessment. */ +static std::vector net_cost, temp_net_cost; + +static std::vector> legal_pos; +//static int *num_legal_pos = nullptr; /* [0..num_legal_pos-1] */ + +/* [0...cluster_ctx.clb_nlist.nets().size()-1] * + * A flag array to indicate whether the specific bounding box has been updated * + * in this particular swap or not. If it has been updated before, the code * + * must use the updated data, instead of the out-of-date data passed into the * + * subroutine, particularly used in try_swap(). The value NOT_UPDATED_YET * + * indicates that the net has not been updated before, UPDATED_ONCE indicated * + * that the net has been updated once, if it is going to be updated again, the * + * values from the previous update must be used. GOT_FROM_SCRATCH is only * + * applicable for nets larger than SMALL_NETS and it indicates that the * + * particular bounding box cannot be updated incrementally before, hence the * + * bounding box is got from scratch, so the bounding box would definitely be * + * right, DO NOT update again. */ +static std::vector bb_updated_before; + +/* [0..cluster_ctx.clb_nlist.nets().size()-1][1..num_pins-1]. What is the value of the timing */ +/* driven portion of the cost function. These arrays will be set to */ +/* (criticality * delay) for each point to point connection. */ + +static std::vector point_to_point_timing_cost; +static std::vector temp_point_to_point_timing_cost; + +/* [0..cluster_ctx.clb_nlist.nets().size()-1][1..num_pins-1]. What is the value of the delay */ +/* for each connection in the circuit */ +static std::vector point_to_point_delay_cost; +static std::vector temp_point_to_point_delay_cost; + +///* [0..cluster_ctx.clb_nlist.blocks().size()-1][0..pins_per_clb-1]. Indicates which pin on the net */ +///* this block corresponds to, this is only required during timing-driven */ +///* placement. It is used to allow us to update individual connections on */ +///* each net */ +//static vtr::vector> net_pin_indices; + +/* [0..cluster_ctx.clb_nlist.nets().size()-1]. Store the bounding box coordinates and the number of * + * blocks on each of a net's bounding box (to allow efficient updates), * + * respectively. */ + +static std::vector bb_coords, bb_num_on_edges; + +/* Store the information on the blocks to be moved in a swap during * + * placement, in the form of array of structs instead of struct with * + * arrays for cache effifiency * + */ +static std::vector> blocks_affected; + +///* The arrays below are used to precompute the inverse of the average * +// * number of tracks per channel between [subhigh] and [sublow]. Access * +// * them as chan?_place_cost_fac[subhigh][sublow]. They are used to * +// * speed up the computation of the cost function that takes the length * +// * of the net bounding box in each dimension, divided by the average * +// * number of tracks in that direction; for other cost functions they * +// * will never be used. * +// */ +//static float** chanx_place_cost_fac; //[0...device_ctx.grid.width()-2] +//static float** chany_place_cost_fac; //[0...device_ctx.grid.height()-2] + +/* The following arrays are used by the try_swap function for speed. */ +///* [0...cluster_ctx.clb_nlist.nets().size()-1] */ +static std::vector ts_bb_coord_new, ts_bb_edge_new; +static std::vector ts_nets_to_update; + +/* The pl_macros array stores all the carry chains placement macros. * + * [0...num_pl_macros-1] */ +static std::vector pl_macros; + +/* These file-scoped variables keep track of the number of swaps * + * rejected, accepted or aborted. The total number of swap attempts * + * is the sum of the three number. */ +static int num_swap_rejected = 0; +static int num_swap_accepted = 0; +static int num_swap_aborted = 0; +static int num_ts_called = 0; + +/* Expected crossing counts for nets with different #'s of pins. From * + * ICCAD 94 pp. 690 - 695 (with linear interpolation applied by me). * + * Multiplied to bounding box of a net to better estimate wire length * + * for higher fanout nets. Each entry is the correction factor for the * + * fanout index-1 */ +static const float cross_count[50] = { /* [0..49] */1.0, 1.0, 1.0, 1.0828, 1.1536, 1.2206, 1.2823, 1.3385, 1.3991, 1.4493, 1.4974, + 1.5455, 1.5937, 1.6418, 1.6899, 1.7304, 1.7709, 1.8114, 1.8519, 1.8924, + 1.9288, 1.9652, 2.0015, 2.0379, 2.0743, 2.1061, 2.1379, 2.1698, 2.2016, + 2.2334, 2.2646, 2.2958, 2.3271, 2.3583, 2.3895, 2.4187, 2.4479, 2.4772, + 2.5064, 2.5356, 2.5610, 2.5864, 2.6117, 2.6371, 2.6625, 2.6887, 2.7148, + 2.7410, 2.7671, 2.7933 }; + +///********************* Static subroutines local to place.c *******************/ +//#ifdef VERBOSE +// static void print_clb_placement(const char *fname); +//#endif + +static void alloc_and_load_placement_structs( + /*float place_cost_exp,*/ t_placer_opts placer_opts /*, + t_direct_inf *directs, int num_directs*/); + +//static void alloc_and_load_net_pin_indices(); + +static void alloc_and_load_try_swap_structs(); + +static void free_placement_structs(t_placer_opts placer_opts); + +//static void alloc_and_load_for_fast_cost_update(float place_cost_exp); +// +//static void free_fast_cost_update(); +// +//static void alloc_legal_placements(); +static void load_legal_placements(); + +//static void free_legal_placements(); + +static int check_macro_can_be_placed(int imacro, int itype, int x, int y, int z); + +static int try_place_macro(int itype, /*int*/ Loc ipos, int macro); + +static void initial_placement_pl_macros(int macros_max_num_tries, /*int * */ std::vector> &free_locations); + +static void initial_placement_blocks(/*int * */ std::vector> &free_locations /*, enum e_pad_loc_type pad_loc_type*/); +static void initial_placement_location(/*int **/ std::vector> &free_locations, /*ClusterBlockId*/ CellInfo* blk_id, + /*int *pipos,*/ int *px, int *py, int *pz); + +static void initial_placement(/*enum e_pad_loc_type pad_loc_type, + const char *pad_loc_file*/); + +static float comp_bb_cost(e_cost_methods method); + +static int setup_blocks_affected(/*ClusterBlockId*/ CellInfo* b_from, int x_to, int y_to, int z_to); + +static int find_affected_blocks(/*ClusterBlockId*/ CellInfo* b_from, int x_to, int y_to, int z_to); + +static e_swap_result try_swap(float t, float *cost, float *bb_cost, float *timing_cost, + float rlim, + enum e_place_algorithm place_algorithm, float timing_tradeoff, + float inverse_prev_bb_cost, float inverse_prev_timing_cost, + float *delay_cost); + +static /*ClusterBlockId*/ CellInfo* pick_from_block(); + +static void check_place(float bb_cost, float timing_cost, + enum e_place_algorithm place_algorithm, + float delay_cost); + +static float starting_t(float *cost_ptr, float *bb_cost_ptr, + float *timing_cost_ptr, + t_annealing_sched annealing_sched, int max_moves, float rlim, + enum e_place_algorithm place_algorithm, float timing_tradeoff, + float inverse_prev_bb_cost, float inverse_prev_timing_cost, + float *delay_cost_ptr); + +static void update_t(float *t, float rlim, float success_rat, + t_annealing_sched annealing_sched); + +static void update_rlim(float *rlim, float success_rat, const DeviceGrid& grid); + +static int exit_crit(float t, float cost, + t_annealing_sched annealing_sched); + +static int count_connections(); + +static double get_std_dev(int n, double sum_x_squared, double av_x); + +static float recompute_bb_cost(); + +static float comp_td_point_to_point_delay(/*ClusterNetId*/ NetInfo* net_id, int ipin); + +static void comp_td_point_to_point_delays(); + +static void update_td_cost(); + +static bool driven_by_moved_block(const /*ClusterNetId*/ NetInfo* net); + +static void comp_td_costs(float *timing_cost, float *connection_delay_sum); + +static e_swap_result assess_swap(float delta_c, float t); + +static bool find_to(/*t_type_ptr type,*/ float rlim, + int x_from, int y_from, + int *px_to, int *py_to, int *pz_to, + CellInfo* cell_from); +static void find_to_location(/*t_type_ptr*/ IdString type, float rlim, + int x_from, int y_from, + int *px_to, int *py_to, int *pz_to); + +static void get_non_updateable_bb(/*ClusterNetId*/ NetInfo* net_id, t_bb *bb_coord_new); + +static void update_bb(/*ClusterNetId*/ NetInfo* net_id, t_bb *bb_coord_new, + t_bb *bb_edge_new, int xold, int yold, int xnew, int ynew); + +static int find_affected_nets_and_update_costs(e_place_algorithm place_algorithm, float& bb_delta_c, float& timing_delta_c, float& delay_delta_c); + +static void record_affected_net(/*const ClusterNetId*/ NetInfo *net, int& num_affected_nets); + +static void update_net_bb(/*const ClusterNetId*/ NetInfo* net, /*int iblk, const ClusterBlockId*/ CellInfo *blk, /*const ClusterPinId blk_pin*/ + BelId bel_from); +static void update_td_delta_costs(/*const ClusterNetId*/ NetInfo *net, const /*ClusterPinId*/ PortInfo &pin, float& delta_timing_cost, float& delta_delay_cost, + CellInfo* blk); + +static float get_net_cost(/*ClusterNetId*/ NetInfo* net_id, t_bb *bb_ptr); + +static void get_bb_from_scratch(/*ClusterNetId*/ NetInfo* net, t_bb *coords, + t_bb *num_on_edges); + +static double get_net_wirelength_estimate(/*ClusterNetId*/ NetInfo* net_id, t_bb *bbptr); + +//static void free_try_swap_arrays(); + +static void outer_loop_recompute_criticalities(t_placer_opts placer_opts, + int num_connections, float crit_exponent, float bb_cost, + float * place_delay_value, float * timing_cost, float * delay_cost, + int * outer_crit_iter_count, float * inverse_prev_timing_cost, + float * inverse_prev_bb_cost, + /*const ClusteredPinAtomPinsLookup& netlist_pin_lookup,*/ +//#ifdef ENABLE_CLASSIC_VPR_STA +// t_slack* slacks, +// t_timing_inf timing_inf, +//#endif + SetupTimingInfo& timing_info); + +static void placement_inner_loop(float t, float rlim, t_placer_opts placer_opts, + float inverse_prev_bb_cost, float inverse_prev_timing_cost, int move_lim, + float crit_exponent, int inner_recompute_limit, + t_placer_statistics *stats, float * cost, float * bb_cost, float * timing_cost, + float * delay_cost, +//#ifdef ENABLE_CLASSIC_VPR_STA +// t_slack* slacks, +// t_timing_inf timing_inf, +//#endif + /*const ClusteredPinAtomPinsLookup& netlist_pin_lookup,*/ + SetupTimingInfo& timing_info); + +/*****************************************************************************/ +void try_place(t_placer_opts placer_opts, + t_annealing_sched annealing_sched/*, + t_chan_width_dist chan_width_dist, t_router_opts router_opts, + t_det_routing_arch *det_routing_arch, t_segment_inf * segment_inf, +//#ifdef ENABLE_CLASSIC_VPR_STA +// t_timing_inf timing_inf, +//#endif + t_direct_inf *directs, int num_directs*/) { + +// /* Does almost all the work of placing a circuit. Width_fac gives the * +// * width of the widest channel. Place_cost_exp says what exponent the * +// * width should be taken to when calculating costs. This allows a * +// * greater bias for anisotropic architectures. */ + + int tot_iter, move_lim, moves_since_cost_recompute, /*width_fac,*/ num_connections, + outer_crit_iter_count, inner_recompute_limit; + //unsigned int ipin; + float t, success_rat, rlim, cost, timing_cost, bb_cost, new_bb_cost, new_timing_cost, + delay_cost, new_delay_cost, place_delay_value, inverse_prev_bb_cost, inverse_prev_timing_cost, + oldt, crit_exponent, + first_rlim, final_rlim , inverse_delta_rlim; + tatum::TimingPathInfo critical_path; + float sTNS = NAN; + float sWNS = NAN; + + double std_dev; + char msg[vtr::bufsize]; + t_placer_statistics stats; +//#ifdef ENABLE_CLASSIC_VPR_STA +// t_slack * slacks = NULL; +//#endif + + auto& device_ctx = g_vpr_ctx.device(); + auto& cluster_ctx = g_vpr_ctx.clustering(); + + std::shared_ptr timing_info; +// std::shared_ptr placement_delay_calc; +// +// /* Allocated here because it goes into timing critical code where each memory allocation is expensive */ +// IntraLbPbPinLookup pb_gpin_lookup(device_ctx.block_types, device_ctx.num_block_types); + + + /* init file scope variables */ + num_swap_rejected = 0; + num_swap_accepted = 0; + num_swap_aborted = 0; + num_ts_called = 0; + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE + || placer_opts.enable_timing_computations) { + /*do this before the initial placement to avoid messing up the initial placement */ + alloc_lookups_and_criticalities(/*chan_width_dist, router_opts, det_routing_arch, segment_inf, directs, num_directs*/); + +//#ifdef ENABLE_CLASSIC_VPR_STA +// slacks = alloc_and_load_timing_graph(timing_inf); +//#endif + } + +// width_fac = placer_opts.place_chan_width; +// +// init_chan(width_fac, chan_width_dist); + + alloc_and_load_placement_structs(/*placer_opts.place_cost_exp,*/ placer_opts /*, + directs, num_directs*/); + + initial_placement(/*placer_opts.pad_loc_type, placer_opts.pad_loc_file.c_str()*/); + +// init_draw_coords((float) width_fac); +// +// //Enables fast look-up of atom pins connect to CLB pins +// ClusteredPinAtomPinsLookup netlist_pin_lookup(cluster_ctx.clb_nlist, pb_gpin_lookup); + + /* Gets initial cost and loads bounding boxes. */ + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE || placer_opts.enable_timing_computations) { + bb_cost = comp_bb_cost(NORMAL); + + crit_exponent = placer_opts.td_place_exp_first; /*this will be modified when rlim starts to change */ + + num_connections = count_connections(); + vtr::printf_info("\n"); + vtr::printf_info("There are %d point to point connections in this circuit.\n", num_connections); + vtr::printf_info("\n"); + + place_delay_value = 0; + + //Update the point-to-point delays from the initial placement + comp_td_point_to_point_delays(); + + /* + * Initialize timing analysis + */ +// auto& atom_ctx = g_vpr_ctx.atom(); +// placement_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, point_to_point_delay_cost); + timing_info = make_setup_timing_info(/*placement_delay_calc*/); + + timing_info->update(); +// timing_info->set_warn_unconstrained(false); //Don't warn again about unconstrained nodes again during placement + + //Initial slack estimates + load_criticalities(*timing_info, crit_exponent /*, netlist_pin_lookup*/); + + critical_path = timing_info->least_slack_critical_path(); + +// //Write out the initial timing echo file +// if(isEchoFileEnabled(E_ECHO_INITIAL_PLACEMENT_TIMING_GRAPH)) { +// auto& timing_ctx = g_vpr_ctx.timing(); +// +// tatum::write_echo(getEchoFileName(E_ECHO_INITIAL_PLACEMENT_TIMING_GRAPH), +// *timing_ctx.graph, *timing_ctx.constraints, *placement_delay_calc, timing_info->analyzer()); +// } + +//#ifdef ENABLE_CLASSIC_VPR_STA +// load_timing_graph_net_delays(point_to_point_delay_cost); +// do_timing_analysis(slacks, timing_inf, false, true); +// +// float cpd_diff_ns = std::abs(get_critical_path_delay() - 1e9*critical_path.delay()); +// if(cpd_diff_ns > ERROR_TOL) { +// print_classic_cpds(); +// print_tatum_cpds(timing_info->critical_paths()); +// +// vpr_throw(VPR_ERROR_TIMING, __FILE__, __LINE__, "Classic VPR and Tatum critical paths do not match (%g and %g respectively)", get_critical_path_delay(), 1e9*critical_path.delay()); +// } +//#endif + + /*now we can properly compute costs */ + comp_td_costs(&timing_cost, &delay_cost); /*also updates values in point_to_point_delay_cost */ + +// if (getEchoEnabled()) { +//#ifdef ENABLE_CLASSIC_VPR_STA +// if(isEchoFileEnabled(E_ECHO_INITIAL_PLACEMENT_SLACK)) +// print_slack(slacks->slack, false, getEchoFileName(E_ECHO_INITIAL_PLACEMENT_SLACK)); +// if(isEchoFileEnabled(E_ECHO_INITIAL_PLACEMENT_CRITICALITY)) +// print_criticality(slacks, getEchoFileName(E_ECHO_INITIAL_PLACEMENT_CRITICALITY)); +//#endif +// } + outer_crit_iter_count = 1; + + inverse_prev_timing_cost = 1 / timing_cost; + inverse_prev_bb_cost = 1 / bb_cost; + cost = 1; /*our new cost function uses normalized values of */ + /*bb_cost and timing_cost, the value of cost will be reset */ + /*to 1 at each temperature when *_TIMING_DRIVEN_PLACE is true */ + } else { /*BOUNDING_BOX_PLACE */ + cost = bb_cost = comp_bb_cost(NORMAL); + timing_cost = 0; + delay_cost = 0; + place_delay_value = 0; + outer_crit_iter_count = 0; + num_connections = 0; + crit_exponent = 0; + + inverse_prev_timing_cost = 0; /*inverses not used */ + inverse_prev_bb_cost = 0; + } + + //Sanity check that initial placement is legal + check_place(bb_cost, timing_cost, placer_opts.place_algorithm, delay_cost); + + //Initial pacement statistics + vtr::printf_info("Initial placement cost: %g bb_cost: %g td_cost: %g delay_cost: %g\n", + cost, bb_cost, timing_cost, delay_cost); + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + vtr::printf_info("Initial placement estimated Critical Path Delay (CPD): %g ns\n", + /*1e9**/critical_path.delay()); + vtr::printf_info("Initial placement estimated setup Total Negative Slack (sTNS): %g ns\n", + /*1e9**/timing_info->setup_total_negative_slack()); + vtr::printf_info("Initial placement estimated setup Worst Negative Slack (sWNS): %g ns\n", + /*1e9**/timing_info->setup_worst_negative_slack()); + vtr::printf_info("\n"); + +// vtr::printf_info("Initial placement estimated setup slack histogram:\n"); +// print_histogram(create_setup_slack_histogram(*timing_info->setup_analyzer())); + } + vtr::printf_info("\n"); + + //Table header + vtr::printf_info("%7s " + "%7s %10s %10s %10s " + "%10s %7s %10s %8s " + "%7s %7s %7s %6s " + "%9s %6s\n", + "-------", + "-------", "----------", "----------", "----------", + "----------", "-------", "----------", "--------", + "-------", "-------", "-------", "------", + "---------", "------"); + vtr::printf_info("%7s " + "%7s %10s %10s %10s " + "%10s %7s %10s %8s " + "%7s %7s %7s %6s " + "%9s %6s\n", + "T", + "Cost", "Av BB Cost", "Av TD Cost", "Av Tot Del", + "P to P Del", "CPD", "sTNS", "sWNS", + "Ac Rate", "Std Dev", "R limit", "Exp", + "Tot Moves", "Alpha"); + vtr::printf_info("%7s " + "%7s %10s %10s %10s " + "%10s %7s %10s %8s " + "%7s %7s %7s %6s " + "%9s %6s\n", + "-------", + "-------", "----------", "----------", "----------", + "----------", "-------", "----------", "--------", + "-------", "-------", "-------", "------", + "---------", "------"); + + sprintf(msg, "Initial Placement. Cost: %g BB Cost: %g TD Cost %g Delay Cost: %g \t Channel Factor: %d", + cost, bb_cost, timing_cost, delay_cost, /*width_fac*/ -1); + + //Draw the initial placement + update_screen(/*ScreenUpdatePriority::MAJOR, msg, PLACEMENT, timing_info*/); + + move_lim = (int) (annealing_sched.inner_num * pow(cluster_ctx.clb_nlist.blocks().size(), 1.3333)); + + /* Sometimes I want to run the router with a random placement. Avoid * + * using 0 moves to stop division by 0 and 0 length vector problems, * + * by setting move_lim to 1 (which is still too small to do any * + * significant optimization). */ + if (move_lim <= 0) + move_lim = 1; + + if (placer_opts.inner_loop_recompute_divider != 0) { + inner_recompute_limit = (int) + (0.5 + (float) move_lim / (float) placer_opts.inner_loop_recompute_divider); + } else { + /*don't do an inner recompute */ + inner_recompute_limit = move_lim + 1; + } + + rlim = (float) std::max(device_ctx.grid.width(), device_ctx.grid.height()); + + first_rlim = rlim; /*used in timing-driven placement for exponent computation */ + final_rlim = 1; + inverse_delta_rlim = 1 / (first_rlim - final_rlim); + + t = starting_t(&cost, &bb_cost, &timing_cost, + annealing_sched, move_lim, rlim, + placer_opts.place_algorithm, placer_opts.timing_tradeoff, + inverse_prev_bb_cost, inverse_prev_timing_cost, &delay_cost); + + tot_iter = 0; + moves_since_cost_recompute = 0; + + /* Outer loop of the simmulated annealing begins */ + while (exit_crit(t, cost, annealing_sched) == 0) { + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + cost = 1; + } + + outer_loop_recompute_criticalities(placer_opts, num_connections, + crit_exponent, bb_cost, &place_delay_value, &timing_cost, &delay_cost, + &outer_crit_iter_count, &inverse_prev_timing_cost, &inverse_prev_bb_cost, + /*netlist_pin_lookup,*/ +//#ifdef ENABLE_CLASSIC_VPR_STA +// slacks, +// timing_inf, +//#endif + *timing_info); + + placement_inner_loop(t, rlim, placer_opts, inverse_prev_bb_cost, inverse_prev_timing_cost, + move_lim, crit_exponent, inner_recompute_limit, &stats, + &cost, &bb_cost, &timing_cost, &delay_cost, +//#ifdef ENABLE_CLASSIC_VPR_STA +// slacks, +// timing_inf, +//#endif + /*netlist_pin_lookup,*/ + *timing_info); + + /* Lines below prevent too much round-off error from accumulating * + * in the cost over many iterations. This round-off can lead to * + * error checks failing because the cost is different from what * + * you get when you recompute from scratch. */ + + moves_since_cost_recompute += move_lim; + if (moves_since_cost_recompute > MAX_MOVES_BEFORE_RECOMPUTE) { + new_bb_cost = recompute_bb_cost(); + if (fabs(new_bb_cost - bb_cost) > bb_cost * ERROR_TOL) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "in try_place: new_bb_cost = %g, old bb_cost = %g\n", + new_bb_cost, bb_cost); + } + bb_cost = new_bb_cost; + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + comp_td_costs(&new_timing_cost, &new_delay_cost); + if (fabs(new_timing_cost - timing_cost) > timing_cost * ERROR_TOL) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "in try_place: new_timing_cost = %g, old timing_cost = %g, ERROR_TOL = %g\n", + new_timing_cost, timing_cost, ERROR_TOL); + } + if (fabs(new_delay_cost - delay_cost) > delay_cost * ERROR_TOL) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "in try_place: new_delay_cost = %g, old delay_cost = %g, ERROR_TOL = %g\n", + new_delay_cost, delay_cost, ERROR_TOL); + } + timing_cost = new_timing_cost; + } + + if (placer_opts.place_algorithm == BOUNDING_BOX_PLACE) { + cost = new_bb_cost; + } + moves_since_cost_recompute = 0; + } + + tot_iter += move_lim; + success_rat = ((float) stats.success_sum) / move_lim; + if (stats.success_sum == 0) { + stats.av_cost = cost; + stats.av_bb_cost = bb_cost; + stats.av_timing_cost = timing_cost; + stats.av_delay_cost = delay_cost; + } else { + stats.av_cost /= stats.success_sum; + stats.av_bb_cost /= stats.success_sum; + stats.av_timing_cost /= stats.success_sum; + stats.av_delay_cost /= stats.success_sum; + } + std_dev = get_std_dev(stats.success_sum, stats.sum_of_squares, stats.av_cost); + + oldt = t; /* for finding and printing alpha. */ + update_t(&t, rlim, success_rat, annealing_sched); + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + critical_path = timing_info->least_slack_critical_path(); + sTNS = timing_info->setup_total_negative_slack(); + sWNS = timing_info->setup_worst_negative_slack(); + } + + vtr::printf_info("%7.3f " + "%7.4f %10.4f %-10.5g %-10.5g " + "%-10.5g %7.3f % 10.3g % 8.3f " + "%7.4f %7.4f %7.4f %6.3f" + "%9d %6.3f\n", + oldt, + stats.av_cost, stats.av_bb_cost, stats.av_timing_cost, stats.av_delay_cost, + place_delay_value, /*1e9**/critical_path.delay(), /*1e9**/sTNS, /*1e9**/sWNS, + success_rat, std_dev, rlim, crit_exponent, + tot_iter, t / oldt); + +//#ifdef ENABLE_CLASSIC_VPR_STA +// if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { +// float cpd_diff_ns = std::abs(get_critical_path_delay() - 1e9*critical_path.delay()); +// if(cpd_diff_ns > ERROR_TOL) { +// print_classic_cpds(); +// print_tatum_cpds(timing_info->critical_paths()); +// +// vpr_throw(VPR_ERROR_TIMING, __FILE__, __LINE__, "Classic VPR and Tatum critical paths do not match (%g and %g respectively)", get_critical_path_delay(), 1e9*critical_path.delay()); +// } +// } +//#endif + + sprintf(msg, "Cost: %g BB Cost %g TD Cost %g Temperature: %g", + cost, bb_cost, timing_cost, t); + update_screen(/*ScreenUpdatePriority::MINOR, msg, PLACEMENT, timing_info*/); + update_rlim(&rlim, success_rat, device_ctx.grid); + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + crit_exponent = (1 - (rlim - final_rlim) * inverse_delta_rlim) + * (placer_opts.td_place_exp_last - placer_opts.td_place_exp_first) + + placer_opts.td_place_exp_first; + } +#ifdef VERBOSE + if (getEchoEnabled()) { + print_clb_placement("first_iteration_clb_placement.echo"); + } +#endif + } + /* Outer loop of the simmulated annealing ends */ + + + outer_loop_recompute_criticalities(placer_opts, num_connections, + crit_exponent, bb_cost, &place_delay_value, &timing_cost, &delay_cost, + &outer_crit_iter_count, &inverse_prev_timing_cost, &inverse_prev_bb_cost, + /*netlist_pin_lookup,*/ +//#ifdef ENABLE_CLASSIC_VPR_STA +// slacks, +// timing_inf, +//#endif + *timing_info); + + t = 0; /* freeze out */ + + /* Run inner loop again with temperature = 0 so as to accept only swaps + * which reduce the cost of the placement */ + placement_inner_loop(t, rlim, placer_opts, inverse_prev_bb_cost, inverse_prev_timing_cost, + move_lim, crit_exponent, inner_recompute_limit, &stats, + &cost, &bb_cost, &timing_cost, &delay_cost, +//#ifdef ENABLE_CLASSIC_VPR_STA +// slacks, +// timing_inf, +//#endif + /*netlist_pin_lookup,*/ + *timing_info); + + tot_iter += move_lim; + success_rat = ((float) stats.success_sum) / move_lim; + if (stats.success_sum == 0) { + stats.av_cost = cost; + stats.av_bb_cost = bb_cost; + stats.av_delay_cost = delay_cost; + stats.av_timing_cost = timing_cost; + } else { + stats.av_cost /= stats.success_sum; + stats.av_bb_cost /= stats.success_sum; + stats.av_delay_cost /= stats.success_sum; + stats.av_timing_cost /= stats.success_sum; + } + + std_dev = get_std_dev(stats.success_sum, stats.sum_of_squares, stats.av_cost); + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + critical_path = timing_info->least_slack_critical_path(); + sTNS = timing_info->setup_total_negative_slack(); + sWNS = timing_info->setup_worst_negative_slack(); + } + + vtr::printf_info("%7.3f " + "%7.4f %10.4f %-10.5g %-10.5g " + "%-10.5g %7.3f % 10.3g % 8.3f " + "%7.4f %7.4f %7.4f %6.3f" + "%9d %6.3f\n", + t, + stats.av_cost, stats.av_bb_cost, stats.av_timing_cost, stats.av_delay_cost, + place_delay_value, /*1e9**/critical_path.delay(), /*1e9**/sTNS, /*1e9**/sWNS, + success_rat, std_dev, rlim, crit_exponent, + tot_iter, 0.); + + // TODO: + // 1. print a message about number of aborted moves. + // 2. add some subroutine hierarchy! Too big! + +#ifdef VERBOSE + if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_END_CLB_PLACEMENT)) { + print_clb_placement(getEchoFileName(E_ECHO_END_CLB_PLACEMENT)); + } +#endif + + check_place(bb_cost, timing_cost, placer_opts.place_algorithm, delay_cost); + + //Some stats + vtr::printf_info("\n"); + vtr::printf_info("Swaps called: %d\n", num_ts_called); + +// if (placer_opts.enable_timing_computations +// && placer_opts.place_algorithm == BOUNDING_BOX_PLACE) { +// /*need this done since the timing data has not been kept up to date* +// *in bounding_box mode */ +// for (auto net_id : cluster_ctx.clb_nlist.nets()) { +// for (ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net_id).size(); ipin++) +// set_timing_place_crit(net_id, ipin, 0); /*dummy crit values */ +// } +// comp_td_costs(&timing_cost, &delay_cost); /*computes point_to_point_delay_cost */ +// } + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE + || placer_opts.enable_timing_computations) { + + //Final timing estimate + VTR_ASSERT(timing_info); + timing_info->update(); //Tatum + critical_path = timing_info->least_slack_critical_path(); + +// if(isEchoFileEnabled(E_ECHO_FINAL_PLACEMENT_TIMING_GRAPH)) { +// auto& timing_ctx = g_vpr_ctx.timing(); +// +// tatum::write_echo(getEchoFileName(E_ECHO_FINAL_PLACEMENT_TIMING_GRAPH), +// *timing_ctx.graph, *timing_ctx.constraints, *placement_delay_calc, timing_info->analyzer()); +// } + +//#ifdef ENABLE_CLASSIC_VPR_STA +// //Old VPR analyzer +// load_timing_graph_net_delays(point_to_point_delay_cost); +// do_timing_analysis(slacks, timing_inf, false, true); +//#endif + + + /* Print critical path delay. */ + vtr::printf_info("\n"); + vtr::printf_info("Placement estimated critical path delay: %g ns", + 1e9*critical_path.delay() /*, get_critical_path_delay()*/); +//#ifdef ENABLE_CLASSIC_VPR_STA +// vtr::printf_info(" (classic VPR STA %g ns)", get_critical_path_delay()); +//#endif + vtr::printf("\n"); + vtr::printf_info("Placement estimated setup Total Negative Slack (sTNS): %g ns\n", + 1e9*timing_info->setup_total_negative_slack()); + vtr::printf_info("Placement estimated setup Worst Negative Slack (sWNS): %g ns\n", + 1e9*timing_info->setup_worst_negative_slack()); + vtr::printf_info("\n"); + +// vtr::printf_info("Placement estimated setup slack histogram:\n"); +// print_histogram(create_setup_slack_histogram(*timing_info->setup_analyzer())); +// vtr::printf_info("\n"); + +//#ifdef ENABLE_CLASSIC_VPR_STA +// float cpd_diff_ns = std::abs(get_critical_path_delay() - 1e9*critical_path.delay()); +// if(cpd_diff_ns > ERROR_TOL) { +// print_classic_cpds(); +// print_tatum_cpds(timing_info->critical_paths()); +// +// vpr_throw(VPR_ERROR_TIMING, __FILE__, __LINE__, "Classic VPR and Tatum critical paths do not match (%g and %g respectively)", get_critical_path_delay(), 1e9*critical_path.delay()); +// } +//#endif + } + + sprintf(msg, "Placement. Cost: %g bb_cost: %g td_cost: %g Channel Factor: %d", + cost, bb_cost, timing_cost, /*width_fac*/ -1); + vtr::printf_info("Placement cost: %g, bb_cost: %g, td_cost: %g, delay_cost: %g\n", + cost, bb_cost, timing_cost, delay_cost); + update_screen(/*ScreenUpdatePriority::MAJOR, msg, PLACEMENT, timing_info*/); + + // Print out swap statistics + size_t total_swap_attempts = num_swap_rejected + num_swap_accepted + num_swap_aborted; + VTR_ASSERT(total_swap_attempts > 0); + + size_t num_swap_print_digits = ceil(log10(total_swap_attempts)); + float reject_rate = (float) num_swap_rejected / total_swap_attempts; + float accept_rate = (float) num_swap_accepted / total_swap_attempts; + float abort_rate = (float) num_swap_aborted / total_swap_attempts; + vtr::printf_info("Placement total # of swap attempts: %*d\n", num_swap_print_digits, total_swap_attempts); + vtr::printf_info("\tSwaps accepted: %*d (%4.1f %%)\n", num_swap_print_digits, num_swap_accepted, 100*accept_rate); + vtr::printf_info("\tSwaps rejected: %*d (%4.1f %%)\n", num_swap_print_digits, num_swap_rejected, 100*reject_rate); + vtr::printf_info("\tSwaps aborted : %*d (%4.1f %%)\n", num_swap_print_digits, num_swap_aborted, 100*abort_rate); + + free_placement_structs(placer_opts); + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE + || placer_opts.enable_timing_computations) { + +//#ifdef ENABLE_CLASSIC_VPR_STA +// free_timing_graph(slacks); +//#endif + + free_lookups_and_criticalities(); + } + +// free_try_swap_arrays(); +} + +/* Function to recompute the criticalities before the inner loop of the annealing */ +static void outer_loop_recompute_criticalities(t_placer_opts placer_opts, + int num_connections, float crit_exponent, float bb_cost, + float * place_delay_value, float * timing_cost, float * delay_cost, + int * outer_crit_iter_count, float * inverse_prev_timing_cost, + float * inverse_prev_bb_cost, + /*const ClusteredPinAtomPinsLookup& netlist_pin_lookup,*/ +//#ifdef ENABLE_CLASSIC_VPR_STA +// t_slack* slacks, +// t_timing_inf timing_inf, +//#endif + SetupTimingInfo& timing_info) { + + if (placer_opts.place_algorithm != PATH_TIMING_DRIVEN_PLACE) + return; + + /*at each temperature change we update these values to be used */ + /*for normalizing the tradeoff between timing and wirelength (bb) */ + if (*outer_crit_iter_count >= placer_opts.recompute_crit_iter + || placer_opts.inner_loop_recompute_divider != 0) { +#ifdef VERBOSE + vtr::printf_info("Outer loop recompute criticalities\n"); +#endif + num_connections = std::max(num_connections, 1); //Avoid division by zero + VTR_ASSERT(num_connections > 0); + + *place_delay_value = (*delay_cost) / num_connections; + + //Per-temperature timing update + timing_info.update(); + load_criticalities(timing_info, crit_exponent /*, netlist_pin_lookup*/); + +//#ifdef ENABLE_CLASSIC_VPR_STA +// load_timing_graph_net_delays(point_to_point_delay_cost); +// do_timing_analysis(slacks, timing_inf, false, true); +//#endif + + /*recompute costs from scratch, based on new criticalities */ + comp_td_costs(timing_cost, delay_cost); + *outer_crit_iter_count = 0; + } + (*outer_crit_iter_count)++; + + /*at each temperature change we update these values to be used */ + /*for normalizing the tradeoff between timing and wirelength (bb) */ +*inverse_prev_bb_cost = 1 / bb_cost; +/*Prevent inverse timing cost from going to infinity */ +*inverse_prev_timing_cost = std::min(1 / (*timing_cost), (float)MAX_INV_TIMING_COST); +} + +/* Function which contains the inner loop of the simulated annealing */ +static void placement_inner_loop(float t, float rlim, t_placer_opts placer_opts, + float inverse_prev_bb_cost, float inverse_prev_timing_cost, int move_lim, + float crit_exponent, int inner_recompute_limit, + t_placer_statistics *stats, float * cost, float * bb_cost, float * timing_cost, + float * delay_cost, +//#ifdef ENABLE_CLASSIC_VPR_STA +// t_slack* slacks, +// t_timing_inf timing_inf, +//#endif + /*const ClusteredPinAtomPinsLookup& netlist_pin_lookup,*/ + SetupTimingInfo& timing_info) { + + int inner_crit_iter_count, inner_iter; + + stats->av_cost = 0.; + stats->av_bb_cost = 0.; + stats->av_delay_cost = 0.; + stats->av_timing_cost = 0.; + stats->sum_of_squares = 0.; + stats->success_sum = 0; + + inner_crit_iter_count = 1; + + /* Inner loop begins */ + for (inner_iter = 0; inner_iter < move_lim; inner_iter++) { + e_swap_result swap_result = try_swap(t, cost, bb_cost, timing_cost, rlim, + placer_opts.place_algorithm, placer_opts.timing_tradeoff, + inverse_prev_bb_cost, inverse_prev_timing_cost, delay_cost); + + if (swap_result == ACCEPTED) { + /* Move was accepted. Update statistics that are useful for the annealing schedule. */ + stats->success_sum++; + stats->av_cost += *cost; + stats->av_bb_cost += *bb_cost; + stats->av_timing_cost += *timing_cost; + stats->av_delay_cost += *delay_cost; + stats->sum_of_squares += (*cost) * (*cost); + num_swap_accepted++; + } + else if (swap_result == ABORTED) { + num_swap_aborted++; + } + else { // swap_result == REJECTED + num_swap_rejected++; + } + + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + + /* Do we want to re-timing analyze the circuit to get updated slack and criticality values? + * We do this only once in a while, since it is expensive. + */ + if (inner_crit_iter_count >= inner_recompute_limit + && inner_iter != move_lim - 1) { /*on last iteration don't recompute */ + + inner_crit_iter_count = 0; +#ifdef VERBOSE + vtr::printf("Inner loop recompute criticalities\n"); +#endif + /* Using the delays in net_delay, do a timing analysis to update slacks and + * criticalities; then update the timing cost since it will change. + */ + //Inner loop timing update + timing_info.update(); + load_criticalities(timing_info, crit_exponent /*, netlist_pin_lookup*/); + +//#ifdef ENABLE_CLASSIC_VPR_STA +// load_timing_graph_net_delays(point_to_point_delay_cost); +// do_timing_analysis(slacks, timing_inf, false, true); +//#endif + + comp_td_costs(timing_cost, delay_cost); + } + inner_crit_iter_count++; + } +#ifdef VERBOSE + vtr::printf("t = %g cost = %g bb_cost = %g timing_cost = %g move = %d dmax = %g\n", + t, *cost, *bb_cost, *timing_cost, inner_iter, *delay_cost); + if (fabs((*bb_cost) - comp_bb_cost(CHECK)) > (*bb_cost) * ERROR_TOL) + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "fabs((*bb_cost) - comp_bb_cost(CHECK)) > (*bb_cost) * ERROR_TOL"); +#endif + } + /* Inner loop ends */ +} + +/*only count non-global connections */ +static int count_connections() { + + int count = 0; + + auto& cluster_ctx = g_vpr_ctx.clustering(); + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + if (cluster_ctx.clb_nlist.net_is_global(net_id)) + continue; + + count += cluster_ctx.clb_nlist.net_sinks(net_id).size(); + } + + return (count); +} + +static double get_std_dev(int n, double sum_x_squared, double av_x) { + + /* Returns the standard deviation of data set x. There are n sample points, * + * sum_x_squared is the summation over n of x^2 and av_x is the average x. * + * All operations are done in double precision, since round off error can be * + * a problem in the initial temp. std_dev calculation for big circuits. */ + + double std_dev; + + if (n <= 1) + std_dev = 0.; + else + std_dev = (sum_x_squared - n * av_x * av_x) / (double) (n - 1); + + if (std_dev > 0.) /* Very small variances sometimes round negative */ + std_dev = sqrt(std_dev); + else + std_dev = 0.; + + return (std_dev); +} + +static void update_rlim(float *rlim, float success_rat, const DeviceGrid& grid) { + + /* Update the range limited to keep acceptance prob. near 0.44. Use * + * a floating point rlim to allow gradual transitions at low temps. */ + + float upper_lim; + + *rlim = (*rlim) * (1. - 0.44 + success_rat); + upper_lim = std::max(grid.width() - 1, grid.height() - 1); + *rlim = std::min(*rlim, upper_lim); + *rlim = std::max(*rlim, (float)1.); +} + +/* Update the temperature according to the annealing schedule selected. */ +static void update_t(float *t, float rlim, float success_rat, + t_annealing_sched annealing_sched) { + + /* float fac; */ + + if (annealing_sched.type == USER_SCHED) { + *t = annealing_sched.alpha_t * (*t); + } else { /* AUTO_SCHED */ + if (success_rat > 0.96) { + *t = (*t) * 0.5; + } else if (success_rat > 0.8) { + *t = (*t) * 0.9; + } else if (success_rat > 0.15 || rlim > 1.) { + *t = (*t) * 0.95; + } else { + *t = (*t) * 0.8; + } + } +} + +static int exit_crit(float t, float cost, + t_annealing_sched annealing_sched) { + + /* Return 1 when the exit criterion is met. */ + + if (annealing_sched.type == USER_SCHED) { + if (t < annealing_sched.exit_t) { + return (1); + } else { + return (0); + } + } + + auto& cluster_ctx = g_vpr_ctx.clustering(); + + /* Automatic annealing schedule */ + float t_exit = 0.005 * cost / cluster_ctx.clb_nlist.nets().size(); + + if (t < t_exit) { + return (1); + } else if (std::isnan(t_exit)) { + //May get nan if there are no nets + return (1); + } else { + return (0); + } +} + +static float starting_t(float *cost_ptr, float *bb_cost_ptr, + float *timing_cost_ptr, + t_annealing_sched annealing_sched, int max_moves, float rlim, + enum e_place_algorithm place_algorithm, float timing_tradeoff, + float inverse_prev_bb_cost, float inverse_prev_timing_cost, + float *delay_cost_ptr) { + + /* Finds the starting temperature (hot condition). */ + + int i, num_accepted, move_lim; + double std_dev, av, sum_of_squares; /* Double important to avoid round off */ + + if (annealing_sched.type == USER_SCHED) + return (annealing_sched.init_t); + + auto& cluster_ctx = g_vpr_ctx.clustering(); + + move_lim = std::min(max_moves, (int) cluster_ctx.clb_nlist.blocks().size()); + + num_accepted = 0; + av = 0.; + sum_of_squares = 0.; + + /* Try one move per block. Set t high so essentially all accepted. */ + + for (i = 0; i < move_lim; i++) { + e_swap_result swap_result = try_swap(HUGE_POSITIVE_FLOAT, cost_ptr, bb_cost_ptr, timing_cost_ptr, rlim, + place_algorithm, timing_tradeoff, + inverse_prev_bb_cost, inverse_prev_timing_cost, delay_cost_ptr); + + if (swap_result == ACCEPTED) { + num_accepted++; + av += *cost_ptr; + sum_of_squares += *cost_ptr * (*cost_ptr); + num_swap_accepted++; + } else if (swap_result == ABORTED) { + num_swap_aborted++; + } else { + num_swap_rejected++; + } + } + + if (num_accepted != 0) + av /= num_accepted; + else + av = 0.; + + std_dev = get_std_dev(num_accepted, sum_of_squares, av); + + if (num_accepted != move_lim) { + vtr::printf_warning(__FILE__, __LINE__, + "Starting t: %d of %d configurations accepted.\n", num_accepted, move_lim); + } + +#ifdef VERBOSE + vtr::printf_info("std_dev: %g, average cost: %g, starting temp: %g\n", std_dev, av, 20. * std_dev); +#endif + + /* Set the initial temperature to 20 times the standard of deviation */ + /* so that the initial temperature adjusts according to the circuit */ + return (20. * std_dev); +} + + +static int setup_blocks_affected(/*ClusterBlockId*/ CellInfo* b_from, int x_to, int y_to, int z_to) { + + /* Find all the blocks affected when b_from is swapped with b_to. + * Returns abort_swap. */ + + int /*imoved_blk,*/ imacro; +// int x_from, y_from, z_from; + /*ClusterBlockId*/ CellInfo *b_to; + int abort_swap = false; + +// auto& place_ctx = g_vpr_ctx.mutable_placement(); + + auto bel_from = b_from->bel; + auto bel_to = g_vpr_ctx.device().grid[x_to][y_to][z_to]; + + b_to = npnr_ctx->getBoundBelCell(bel_to); + + // Check whether the to_location is empty + if (b_to == /*EMPTY_BLOCK_ID*/ nullptr) { + + npnr_ctx->unbindBel(bel_from); + npnr_ctx->bindBel(bel_to, b_from, STRENGTH_WEAK); + //NPNR_ASSERT(npnr_ctx->isBelLocationValid(bel_to)); + + blocks_affected.emplace_back(b_from, bel_from); + + } else /*if (b_to != INVALID_BLOCK_ID)*/ { + + // Does not allow a swap with a macro yet + get_imacro_from_iblk(&imacro, b_to->udata, pl_macros /*, num_pl_macros*/); + if (imacro != -1) { + abort_swap = true; + return (abort_swap); + } + + npnr_ctx->unbindBel(bel_to); + npnr_ctx->unbindBel(bel_from); + npnr_ctx->bindBel(bel_to, b_from, STRENGTH_WEAK); + npnr_ctx->bindBel(bel_from, b_to, STRENGTH_WEAK); + + blocks_affected.emplace_back(b_from, bel_from); + blocks_affected.emplace_back(b_to, bel_to); + + } // Finish swapping the blocks and setting up blocks_affected + + return (abort_swap); + +} + +static int find_affected_blocks(/*ClusterBlockId*/ CellInfo* b_from, int x_to, int y_to, int z_to) { + + /* Finds and set ups the affected_blocks array. + * Returns abort_swap. */ + + int imacro, imember; + int x_swap_offset, y_swap_offset, z_swap_offset, x_from, y_from, z_from; + /*ClusterBlockId*/ CellInfo* curr_b_from; + int curr_x_from, curr_y_from, curr_z_from, curr_x_to, curr_y_to, curr_z_to; + int abort_swap = false; + +// auto& place_ctx = g_vpr_ctx.placement(); + auto& device_ctx = g_vpr_ctx.device(); +// auto& cluster_ctx = g_vpr_ctx.clustering(); + + auto loc_from = npnr_ctx->getBelLocation(b_from->bel); + x_from = loc_from.x; + y_from = loc_from.y; + z_from = loc_from.z; + + get_imacro_from_iblk(&imacro, b_from->udata, pl_macros /*, num_pl_macros*/); + if (imacro != -1) { + // b_from is part of a macro, I need to swap the whole macro + + // Record down the relative position of the swap + x_swap_offset = x_to - x_from; + y_swap_offset = y_to - y_from; + z_swap_offset = z_to - z_from; + NPNR_ASSERT(z_swap_offset == 0); + + // Split existing for loop into two passes + // -- first checks validity of entire swap + // -- second calls setup_blocks_affected + for (imember = 0; imember < int(pl_macros[imacro].members.size()) && abort_swap == false; imember++) { + + // Gets the new from and to info for every block in the macro + // cannot use the old from and to info + curr_b_from = pl_macros[imacro].members[imember].blk_index; + + auto loc_from = npnr_ctx->getBelLocation(curr_b_from->bel); + curr_x_from = loc_from.x /*place_ctx.block_locs[curr_b_from].x*/; + curr_y_from = loc_from.y /*place_ctx.block_locs[curr_b_from].y*/; + curr_z_from = loc_from.z /*place_ctx.block_locs[curr_b_from].z*/; + + curr_x_to = curr_x_from + x_swap_offset; + curr_y_to = curr_y_from + y_swap_offset; + curr_z_to = curr_z_from + z_swap_offset; + + //Make sure that the swap_to location is valid + //It must be: + // * chip, and + // * match the correct block type + // + //Note that we need to explicitly check that the types match, since the device floorplan is not + //(neccessarily) translationally invariant for an arbitrary macro + if ( curr_x_to < 1 || curr_x_to >= int(device_ctx.grid.width()) + || curr_y_to < 1 || curr_y_to >= int(device_ctx.grid.height()) + || curr_z_to < 0 || curr_z_to >= int(device_ctx.grid[curr_x_to][curr_y_to].size()) + || npnr_ctx->getBelType(device_ctx.grid[curr_x_to][curr_y_to][curr_z_to]) != curr_b_from->type) { + abort_swap = true; + } else { + auto bel_to = device_ctx.grid[curr_x_to][curr_y_to][curr_z_to]; + if (!npnr_ctx->isValidBelForCell(pl_macros[imacro].members[imember].blk_index, bel_to)) { + abort_swap = true; + } + else { + auto cell_to = npnr_ctx->getBoundBelCell(bel_to); + if (cell_to) { + if (cell_to->belStrength > STRENGTH_WEAK) { + abort_swap = true; + } + else if (!npnr_ctx->isValidBelForCell(cell_to, pl_macros[imacro].members[imember].blk_index->bel)) { + abort_swap = true; + } + + // Does not allow a swap with a macro yet + int jmacro; + get_imacro_from_iblk(&jmacro, cell_to->udata, pl_macros /*, num_pl_macros*/); + if (jmacro != -1) { + abort_swap = true; + } + } + } + + //if (!abort_swap) + // abort_swap = setup_blocks_affected(curr_b_from, curr_x_to, curr_y_to, curr_z_to); + } + } + + for (imember = 0; imember < int(pl_macros[imacro].members.size()) && abort_swap == false; imember++) { + + // Gets the new from and to info for every block in the macro + // cannot use the old from and to info + curr_b_from = pl_macros[imacro].members[imember].blk_index; + + auto loc_from = npnr_ctx->getBelLocation(curr_b_from->bel); + curr_x_from = loc_from.x /*place_ctx.block_locs[curr_b_from].x*/; + curr_y_from = loc_from.y /*place_ctx.block_locs[curr_b_from].y*/; + curr_z_from = loc_from.z /*place_ctx.block_locs[curr_b_from].z*/; + + curr_x_to = curr_x_from + x_swap_offset; + curr_y_to = curr_y_from + y_swap_offset; + curr_z_to = curr_z_from + z_swap_offset; + +// //Make sure that the swap_to location is valid +// //It must be: +// // * chip, and +// // * match the correct block type +// // +// //Note that we need to explicitly check that the types match, since the device floorplan is not +// //(neccessarily) translationally invariant for an arbitrary macro +// if ( curr_x_to < 1 || curr_x_to >= int(device_ctx.grid.width()) +// || curr_y_to < 1 || curr_y_to >= int(device_ctx.grid.height()) +// || curr_z_to < 0 || curr_z_to >= int(device_ctx.grid[curr_x_to][curr_y_to].size()) +// || npnr_ctx->getBelType(device_ctx.grid[curr_x_to][curr_y_to][curr_z_to]) != curr_b_from->type) { +// abort_swap = true; +// } else { +// auto bel_to = device_ctx.grid[curr_x_to][curr_y_to][curr_z_to]; +// if (!npnr_ctx->isValidBelForCell(pl_macros[imacro].members[imember].blk_index, bel_to)) { +// abort_swap = true; +// } +// else { +// auto cell_name = npnr_ctx->getBoundBelCell(bel_to); +// if (cell_name != IdString()) { +// auto cell_to = npnr_ctx->cells[cell_name].get(); +// if (cell_to->belStrength > STRENGTH_WEAK) { +// abort_swap = true; +// } +// else if (!npnr_ctx->isValidBelForCell(cell_to, pl_macros[imacro].members[imember].blk_index->bel)) { +// abort_swap = true; +// } +// } +// } + + if (!abort_swap) + abort_swap = setup_blocks_affected(curr_b_from, curr_x_to, curr_y_to, curr_z_to); + NPNR_ASSERT(!abort_swap); +// } + } // Finish going through all the blocks in the macro + } else { + // This is not a macro - I could use the from and to info from before + + abort_swap = setup_blocks_affected(b_from, x_to, y_to, z_to); + + } // Finish handling cases for blocks in macro and otherwise + + return (abort_swap); + +} + +static e_swap_result try_swap(float t, float *cost, float *bb_cost, float *timing_cost, + float rlim, + enum e_place_algorithm place_algorithm, float timing_tradeoff, + float inverse_prev_bb_cost, float inverse_prev_timing_cost, + float *delay_cost) { + + /* Picks some block and moves it to another spot. If this spot is * + * occupied, switch the blocks. Assess the change in cost function. * + * rlim is the range limiter. * + * Returns whether the swap is accepted, rejected or aborted. * + * Passes back the new value of the cost functions. */ + +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.mutable_placement(); + + num_ts_called ++; + + /* I'm using negative values of temp_net_cost as a flag, so DO NOT * + * use cost functions that can go negative. */ + + float delta_c = 0; /* Change in cost due to this swap. */ + float bb_delta_c = 0; + float timing_delta_c = 0; + float delay_delta_c = 0.0; + + /* Pick a random block to be swapped with another random block. */ + auto b_from = pick_from_block(); + if (!b_from) { + return ABORTED; //No movable block found + } + + auto loc = npnr_ctx->getBelLocation(b_from->bel); + + int x_from = loc.x /*place_ctx.block_locs[b_from].x*/; + int y_from = loc.y /*place_ctx.block_locs[b_from].y*/; + int z_from = loc.z /*place_ctx.block_locs[b_from].z*/; + + int x_to = OPEN; + int y_to = OPEN; + int z_to = OPEN; + +// auto cluster_from_type = cluster_ctx.clb_nlist.block_type(b_from); +// auto grid_from_type = g_vpr_ctx.device().grid[x_from][y_from].type; +// VTR_ASSERT(cluster_from_type == grid_from_type); + + if (!find_to(/*cluster_ctx.clb_nlist.block_type(b_from),*/ rlim, x_from, y_from, &x_to, &y_to, &z_to, + b_from)) + return REJECTED; + +#if 0 + auto& grid = g_vpr_ctx.device().grid; + int b_to = place_ctx.grid_blocks[x_to][y_to].blocks[z_to]; + vtr::printf_info( "swap [%d][%d][%d] %s \"%s\" <=> [%d][%d][%d] %s \"%s\"\n", + x_from, y_from, z_from, grid[x_from][y_from].type->name, (b_from != -1 ? cluster_ctx.blocks[b_from].name : ""), + x_to, y_to, z_to, grid[x_to][y_to].type->name, (b_to != -1 ? cluster_ctx.blocks[b_to].name : "")); +#endif + +// /* Make the switch in order to make computing the new bounding * +// * box simpler. If the cost increase is too high, switch them * +// * back. (place_ctx.block_locs data structures switched, clbs not switched * +// * until success of move is determined.) * +// * Also check that whether those are the only 2 blocks * +// * to be moved - check for carry chains and other placement * +// * macros. */ +// +// /* Check whether the from_block is part of a macro first. * +// * If it is, the whole macro has to be moved. Calculate the * +// * x, y, z offsets of the swap to maintain relative placements * +// * of the blocks. Abort the swap if the to_block is part of a * +// * macro (not supported yet). */ + + bool abort_swap = find_affected_blocks(b_from, x_to, y_to, z_to); + + if (abort_swap == false) { + + // Find all the nets affected by this swap and update thier bounding box + /*int num_nets_affected =*/ find_affected_nets_and_update_costs(place_algorithm, bb_delta_c, timing_delta_c, delay_delta_c); + + if (place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + /*in this case we redefine delta_c as a combination of timing and bb. * + *additionally, we normalize all values, therefore delta_c is in * + *relation to 1*/ + + delta_c = (1 - timing_tradeoff) * bb_delta_c * inverse_prev_bb_cost + + timing_tradeoff * timing_delta_c * inverse_prev_timing_cost; + } else { + delta_c = bb_delta_c; + } + + /* 1 -> move accepted, 0 -> rejected. */ + e_swap_result keep_switch = assess_swap(delta_c, t); + + if (keep_switch == ACCEPTED) { + *cost = *cost + delta_c; + *bb_cost = *bb_cost + bb_delta_c; + + if (place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + /*update the point_to_point_timing_cost and point_to_point_delay_cost + * values from the temporary values */ + *timing_cost = *timing_cost + timing_delta_c; + *delay_cost = *delay_cost + delay_delta_c; + + update_td_cost(); + } + + + /* update net cost functions and reset flags. */ + for (auto net_id : ts_nets_to_update) { + bb_coords[net_id->udata] = ts_bb_coord_new[net_id->udata]; + if (net_id->users.size() >= SMALL_NET) + bb_num_on_edges[net_id->udata] = ts_bb_edge_new[net_id->udata]; + + net_cost[net_id->udata] = temp_net_cost[net_id->udata]; + + /* negative temp_net_cost value is acting as a flag. */ + temp_net_cost[net_id->udata] = -1; + bb_updated_before[net_id->udata] = NOT_UPDATED_YET; + } + + + // No need to update anything, as we've already done the swap + // in setup_blocks_affected + + } else { /* Move was rejected. */ + + /* Reset the net cost function flags first. */ + for (auto net_id : ts_nets_to_update) { + temp_net_cost[net_id->udata] = -1; + bb_updated_before[net_id->udata] = NOT_UPDATED_YET; + } + + /* Restore the place_ctx.block_locs data structures to their state before the move. */ + + // Since we already swapped in setup_blocks_affected(), unswap here; + // to prevent npnr assertions firing, unplace all bels first + for (const auto &b : blocks_affected) { + auto b_from = b.first; + npnr_ctx->unbindBel(b_from->bel); + } + for (const auto &b : blocks_affected) { + auto b_from = b.first; + auto bel = b.second; + + npnr_ctx->bindBel(bel, b_from, STRENGTH_WEAK); + } + } + + /* Resets the num_moved_blocks, but do not free blocks_moved array. Defensive Coding */ + blocks_affected.clear(); + ts_nets_to_update.clear(); + +#if 0 + //Check that each accepted swap yields a valid placement + check_place(*bb_cost, *timing_cost, /*place_algorithm,*/ *delay_cost); +#endif + + return (keep_switch); + } else { + /* Restore the place_ctx.block_locs data structures to their state before the move. */ + + // Since we already swapped in setup_blocks_affected(), unswap here; + // to prevent npnr assertions firing, unplace all bels first + for (const auto &b : blocks_affected) { + auto b_from = b.first; + npnr_ctx->unbindBel(b_from->bel); + } + for (const auto &b : blocks_affected) { + auto b_from = b.first; + auto bel = b.second; + + npnr_ctx->bindBel(bel, b_from, STRENGTH_WEAK); + } + + /* Resets the num_moved_blocks, but do not free blocks_moved array. Defensive Coding */ + blocks_affected.clear(); + +#if 0 + //Check that each accepted swap yields a valid placement + check_place(*bb_cost, *timing_cost, /*place_algorithm,*/ *delay_cost); +#endif + + + return ABORTED; + } +} + +//Pick a random block to be swapped with another random block. +//If none is found return ClusterBlockId::INVALID() +static /*ClusterBlockId*/ CellInfo* pick_from_block() { + /* Some blocks may be fixed, and should never be moved from their * + * initial positions. If we randomly selected such a block try * + * another random block. * + * * + * We need to track the blocks we have tried to avoid an infinite * + * loop if all blocks are fixed. */ + auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.mutable_placement(); + + std::unordered_set tried_from_blocks; + + //So long as untried blocks remain + while (tried_from_blocks.size() < cluster_ctx.clb_nlist.blocks().size()) { + + //Pick a block at random + //ClusterBlockId b_from = ClusterBlockId(vtr::irand((int) cluster_ctx.clb_nlist.blocks().size() - 1)); + + // Since npnr->cells is an unordered_map with ForwardIterators, + // it is inefficient to sample randomly from that container. + // Instead, I've created a (global) randomly-accessible container + // called npnr_cells that contains the same contents + auto b_from = npnr_cells.at(vtr::irand(npnr_cells.size() - 1)); + + //Record it as tried + tried_from_blocks.insert(b_from); + + if (b_from->belStrength > STRENGTH_WEAK) { + continue; //Fixed location, try again + } + + //Found a movable block + return b_from; + } + + //No movable blocks found + return NULL; +} + +//Puts all the nets changed by the current swap into nets_to_update, +//and updates their bounding box. +// +//Returns the number of affected nets. +static int find_affected_nets_and_update_costs(e_place_algorithm place_algorithm, float& bb_delta_c, float& timing_delta_c, float& delay_delta_c) { + VTR_ASSERT_SAFE(bb_delta_c == 0.); + VTR_ASSERT_SAFE(timing_delta_c == 0.); + VTR_ASSERT_SAFE(delay_delta_c == 0.); + auto& cluster_ctx = g_vpr_ctx.clustering(); + + int num_affected_nets = 0; + + //Go through all the blocks moved + for (const auto &b : blocks_affected) { + auto blk = b.first; + auto bel = b.second; + + //Go through all the pins in the moved block + for (const auto &p : cluster_ctx.clb_nlist.block_pins(blk)) { + const auto& blk_pin = p.second; + auto net_id = blk_pin.net; + if (!net_id) continue; + VTR_ASSERT_SAFE_MSG(net_id, "Only valid nets should be found in compressed netlist block pins"); + + + if (cluster_ctx.clb_nlist.net_is_global(net_id)) + continue; //Global nets are assumed to span the whole chip, and do not effect costs + + //Record effected nets + record_affected_net(net_id, num_affected_nets); + + //Update the net bounding boxes + // + //Do not update the net cost here since it should only be updated + //once per net, not once per pin. + update_net_bb(net_id, /*iblk, blk, blk_pin,*/ blk, bel); + + if (place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + //Determine the change in timing costs if required + update_td_delta_costs(net_id, blk_pin, timing_delta_c, delay_delta_c, blk); + } + } + } + + /* Now update the bounding box costs (since the net bounding boxes are up-to-date). + * The cost is only updated once per net. + */ + for (auto net_id : ts_nets_to_update) { + temp_net_cost[net_id->udata] = get_net_cost(net_id, &ts_bb_coord_new[net_id->udata]); + bb_delta_c += temp_net_cost[net_id->udata] - net_cost[net_id->udata]; + } + + return num_affected_nets; +} + +static void record_affected_net(/*const ClusterNetId*/ NetInfo *net, int& num_affected_nets) { + //Record effected nets + if (temp_net_cost[net->udata] < 0.) { + //Net not marked yet. + ts_nets_to_update.push_back(net); + num_affected_nets++; + + //Flag to say we've marked this net. + temp_net_cost[net->udata] = 1.; + } +} + +static void update_net_bb(/*const ClusterNetId*/ NetInfo* net, /*int iblk, const ClusterBlockId*/ CellInfo* blk, /*const ClusterPinId blk_pin*/ + BelId bel_from) { +// auto& cluster_ctx = g_vpr_ctx.clustering(); + + if (net->users.size() < SMALL_NET) { + //For small nets brute-force bounding box update is faster + + if(bb_updated_before[net->udata] == NOT_UPDATED_YET) { //Only once per-net + get_non_updateable_bb(net, &ts_bb_coord_new[net->udata]); + } + } else { + //For large nets, update bounding box incrementally +// int iblk_pin = cluster_ctx.clb_nlist.pin_physical_index(blk_pin); +// +// t_type_ptr blk_type = cluster_ctx.clb_nlist.block_type(blk); +// int pin_width_offset = blk_type->pin_width_offset[iblk_pin]; +// int pin_height_offset = blk_type->pin_height_offset[iblk_pin]; + + auto loc_old = npnr_ctx->getBelLocation(bel_from); + auto loc_new = npnr_ctx->getBelLocation(blk->bel); + + //Incremental bounding box update + update_bb(net, &ts_bb_coord_new[net->udata], + &ts_bb_edge_new[net->udata], + loc_old.x /* + pin_width_offset*/, + loc_old.y /* + pin_height_offset*/, + loc_new.x /* + pin_width_offset*/, + loc_new.y /* + pin_height_offset*/); + } + +} + +static void update_td_delta_costs(/*const ClusterNetId*/ NetInfo *net, const /*ClusterPinId*/ PortInfo &pin, float& delta_timing_cost, float& delta_delay_cost, + CellInfo* blk) { + auto& cluster_ctx = g_vpr_ctx.clustering(); + + if (cluster_ctx.clb_nlist.pin_type(pin) == PinType::DRIVER) { + //This pin is a net driver on a moved block. + //Re-compute all point to point connections for this net. + for (size_t ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net).size(); ipin++) { + float temp_delay = comp_td_point_to_point_delay(net, ipin); + temp_point_to_point_delay_cost[net->udata][ipin] = temp_delay; + + temp_point_to_point_timing_cost[net->udata][ipin] = get_timing_place_crit(net, ipin) * temp_delay; + delta_timing_cost += temp_point_to_point_timing_cost[net->udata][ipin] - point_to_point_timing_cost[net->udata][ipin]; + delta_delay_cost += temp_point_to_point_delay_cost[net->udata][ipin] - point_to_point_delay_cost[net->udata][ipin]; + + } + } else { + //This pin is a net sink on a moved block + VTR_ASSERT_SAFE(cluster_ctx.clb_nlist.pin_type(pin) == PinType::SINK); + + //If this net is being driven by a moved block, we do not + //need to compute the change in the timing cost (here) since it will + //be computed by the net's driver pin (since the driver block moved). + // + //Computing it here would double count the change, and mess up the + //delta_timing_cost value. + if (!driven_by_moved_block(net)) { + int net_pin = cluster_ctx.clb_nlist.pin_net_index(pin, blk); + + float temp_delay = comp_td_point_to_point_delay(net, net_pin); + temp_point_to_point_delay_cost[net->udata][net_pin] = temp_delay; + + temp_point_to_point_timing_cost[net->udata][net_pin] = get_timing_place_crit(net, net_pin) * temp_delay; + delta_timing_cost += temp_point_to_point_timing_cost[net->udata][net_pin] - point_to_point_timing_cost[net->udata][net_pin]; + delta_delay_cost += temp_point_to_point_delay_cost[net->udata][net_pin] - point_to_point_delay_cost[net->udata][net_pin]; + } + } +} + +static bool find_to(/*t_type_ptr type,*/ float rlim, + int x_from, int y_from, + int *px_to, int *py_to, int *pz_to, + CellInfo* cell_from) { + + /* Returns the point to which I want to swap, properly range limited. + * rlim must always be between 1 and device_ctx.grid.width() - 2 (inclusive) for this routine + * to work. Note -2 for no perim channels + */ + + int min_x, max_x, min_y, max_y; + int num_tries; + int active_area; + bool is_legal; +// int itype; + BelId bel_to; + + auto& grid = g_vpr_ctx.device().grid; +// auto& place_ctx = g_vpr_ctx.placement(); +// +// auto grid_type = grid[x_from][y_from].type; +// VTR_ASSERT(type == grid_type); + auto type = cell_from->type; + + int rlx = std::min(grid.width() - 1, rlim); + int rly = std::min(grid.height() - 1, rlim); /* Added rly for aspect_ratio != 1 case. */ + active_area = 4 * rlx * rly; + + min_x = std::max(0, x_from - rlx); + max_x = std::min(grid.width() - 1, x_from + rlx); + min_y = std::max(0, y_from - rly); + max_y = std::min(grid.height() - 1, y_from + rly); + + if (rlx < 1 || rlx > int(grid.width() - 1)) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__,"in find_to: rlx = %d out of range\n", rlx); + } + if (rly < 1 || rly > int(grid.height() - 1)) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__,"in find_to: rly = %d out of range\n", rly); + } + + num_tries = 0; +// itype = type->index; + + do { /* Until legal */ + is_legal = true; + + /* Limit the number of tries when searching for an alternative position */ + if(num_tries >= 2 * std::min(active_area /*/ (type->width * type->height)*/, legal_pos[type.index].size()) + 10) { + /* Tried randomly searching for a suitable position */ + return false; + } else { + num_tries++; + } + + find_to_location(type, rlim, x_from, y_from, + px_to, py_to, pz_to); + + if (*pz_to < 0) { + is_legal = false; + continue; + } + // HACK HACK HACK + // Since nextpnr operates with logic cells (not clusters) + // ensure that carry chains are swapped to identical z + // values, as find_affected_blocks() will take care of + // the rest + else { + int imacro; + get_imacro_from_iblk(&imacro, cell_from->udata, pl_macros /*, num_pl_macros*/); + if (imacro != -1) { + auto loc_from = npnr_ctx->getBelLocation(cell_from->bel); + if (size_t(loc_from.z) >= grid[*px_to][*py_to].size()) { + is_legal = false; + continue; + } + *pz_to = loc_from.z; + } + } + + bel_to = grid[*px_to][*py_to][*pz_to]; + if (bel_to == BelId()) { + is_legal = false; + continue; + } + + if((x_from == *px_to) && (y_from == *py_to)) { + is_legal = false; + } else if(*px_to > max_x || *px_to < min_x || *py_to > max_y || *py_to < min_y) { + is_legal = false; + } else if(type != npnr_ctx->getBelType(bel_to)) { + is_legal = false; + } else { + /* Find z_to and test to validate that the "to" block is *not* fixed */ + // *pz_to already set by find_to_location + + if (!npnr_ctx->isValidBelForCell(cell_from, bel_to)) { + is_legal = false; + } + else { + auto cell_to = npnr_ctx->getBoundBelCell(bel_to); + if (cell_to) { + if (cell_to->belStrength > STRENGTH_WEAK) { + is_legal = false; + } + else if (!npnr_ctx->isValidBelForCell(cell_to, cell_from->bel)) { + is_legal = false; + } + } + } + } + + VTR_ASSERT(*px_to >= 0 && *px_to < int(grid.width())); + VTR_ASSERT(*py_to >= 0 && *py_to < int(grid.height())); + } while (is_legal == false); + + if (*px_to < 0 || *px_to > int(grid.width() - 1) || *py_to < 0 || *py_to > int(grid.height() - 1)) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__,"in routine find_to: (x_to,y_to) = (%d,%d)\n", *px_to, *py_to); + } + + VTR_ASSERT(type == npnr_ctx->getBelType(bel_to)); + return true; +} + +static void find_to_location(/*t_type_ptr*/ IdString type, float rlim, + int x_from, int y_from, + int *px_to, int *py_to, int *pz_to) { + + auto& device_ctx = g_vpr_ctx.device(); + auto& grid = device_ctx.grid; + + int itype = type.index; + + + int rlx = std::min(grid.width() - 1, rlim); + int rly = std::min(grid.height() - 1, rlim); /* Added rly for aspect_ratio != 1 case. */ + int active_area = 4 * rlx * rly; + + int min_x = std::max(0, x_from - rlx); + int max_x = std::min(grid.width() - 1, x_from + rlx); + int min_y = std::max(0, y_from - rly); + int max_y = std::min(grid.height() - 1, y_from + rly); + + *pz_to = 0; + if (int(grid.width() / 4) < rlx || int(grid.height() / 4) < rly || int(legal_pos[itype].size()) < active_area) { + int ipos = vtr::irand(legal_pos[itype].size() - 1); + *px_to = legal_pos[itype][ipos].x; + *py_to = legal_pos[itype][ipos].y; + *pz_to = legal_pos[itype][ipos].z; + } else { + int x_rel = vtr::irand(std::max(0, max_x - min_x)); + int y_rel = vtr::irand(std::max(0, max_y - min_y)); + *px_to = min_x + x_rel; + *py_to = min_y + y_rel; + // Instead of computing z_to in the outer find_to function, + // do it here so we have a fully valid location + if (!grid[*px_to][*py_to].empty()) { + *pz_to = vtr::irand(grid[*px_to][*py_to].size() - 1); + } + else { + *px_to = -1; + *py_to = -1; + *pz_to = -1; + } + } +} + +static e_swap_result assess_swap(float delta_c, float t) { + + /* Returns: 1 -> move accepted, 0 -> rejected. */ + + e_swap_result accept; + float prob_fac, fnum; + + if (delta_c <= 0) { + + /* Reduce variation in final solution due to round off */ + fnum = vtr::frand(); + + accept = ACCEPTED; + return (accept); + } + + if (t == 0.) + return (REJECTED); + + fnum = vtr::frand(); + prob_fac = exp(-delta_c / t); + if (prob_fac > fnum) { + accept = ACCEPTED; + } + else { + accept = REJECTED; + } + return (accept); +} + +static float recompute_bb_cost() { + /* Recomputes the cost to eliminate roundoff that may have accrued. * + * This routine does as little work as possible to compute this new * + * cost. */ + + float cost = 0; + + auto& cluster_ctx = g_vpr_ctx.clustering(); + + for (auto& n : cluster_ctx.clb_nlist.nets()) { /* for each net ... */ + auto net_id = n.second.get(); + if (!cluster_ctx.clb_nlist.net_is_global(net_id)) { /* Do only if not global. */ + /* Bounding boxes don't have to be recomputed; they're correct. */ + cost += net_cost[net_id->udata]; + } + } + + return (cost); +} + +/*returns the delay of one point to point connection */ +static float comp_td_point_to_point_delay(/*ClusterNetId*/ NetInfo* net_id, int ipin) { + auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.placement(); + + float delay_source_to_sink = 0.; + + if (!cluster_ctx.clb_nlist.net_is_global(net_id)) { + //Only estimate delay for signals routed through the inter-block + //routing network. Global signals are assumed to have zero delay. + +// ClusterBlockId source_block = cluster_ctx.clb_nlist.net_driver_block(net_id); +// ClusterBlockId sink_block = cluster_ctx.clb_nlist.net_pin_block(net_id, ipin); +// +// VTR_ASSERT_SAFE(cluster_ctx.clb_nlist.block_type(source_block) != nullptr); +// VTR_ASSERT_SAFE(cluster_ctx.clb_nlist.block_type(sink_block) != nullptr); + + auto drv_wire = npnr_ctx->getBelPinWire(net_id->driver.cell->bel, net_id->driver.port); + auto user_wire = npnr_ctx->getBelPinWire(net_id->users[ipin-1].cell->bel, net_id->users[ipin-1].port); + +// int delta_x = abs(place_ctx.block_locs[sink_block].x - place_ctx.block_locs[source_block].x); +// int delta_y = abs(place_ctx.block_locs[sink_block].y - place_ctx.block_locs[source_block].y); +// +// /* Note: This heuristic only considers delta_x and delta_y, a much better heuristic +// * would be to to create a more comprehensive lookup table. +// * +// * In particular this aproach does not accurately capture the effect of fast +// * carry-chain connections. +// */ + delay_source_to_sink = npnr_ctx->getDelayNS(npnr_ctx->estimateDelay(drv_wire, user_wire)); + +// if (delay_source_to_sink < 0) { +// vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, +// "in comp_td_point_to_point_delay: Bad delay_source_to_sink value delta(%d, %d) delay of %g\n" +// "in comp_td_point_to_point_delay: Delay is less than 0\n", +// delta_x, delta_y, delay_source_to_sink); +// } + } + + + return (delay_source_to_sink); +} + +//Recompute all point to point delays, updating point_to_point_delay_cost +static void comp_td_point_to_point_delays() { + auto& cluster_ctx = g_vpr_ctx.clustering(); + + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + for (size_t ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net_id).size(); ++ipin) { + point_to_point_delay_cost[net_id->udata][ipin] = comp_td_point_to_point_delay(net_id, ipin); + } + } +} + +/* Update the point_to_point_timing_cost values from the temporary * +* values for all connections that have changed. */ +static void update_td_cost() { + auto& cluster_ctx = g_vpr_ctx.clustering(); + + /* Go through all the blocks moved. */ + for (const auto& b : blocks_affected) { + auto bnum = b.first; + for (const auto& pin : cluster_ctx.clb_nlist.block_pins(bnum)) { + const auto& pin_id = pin.second; + auto net_id = pin_id.net; + if (!net_id) continue; + + if (cluster_ctx.clb_nlist.net_is_global(net_id)) + continue; + + if (cluster_ctx.clb_nlist.pin_type(pin_id) == PinType::DRIVER) { + //This net is being driven by a moved block, recompute + //all point to point connections on this net. + for (size_t ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net_id).size(); ipin++) { + NPNR_ASSERT(temp_point_to_point_delay_cost[net_id->udata][ipin] >= 0); + point_to_point_delay_cost[net_id->udata][ipin] = temp_point_to_point_delay_cost[net_id->udata][ipin]; + temp_point_to_point_delay_cost[net_id->udata][ipin] = -1; + NPNR_ASSERT(temp_point_to_point_timing_cost[net_id->udata][ipin] >= 0); + point_to_point_timing_cost[net_id->udata][ipin] = temp_point_to_point_timing_cost[net_id->udata][ipin]; + temp_point_to_point_timing_cost[net_id->udata][ipin] = -1; + } + } else { + //This pin is a net sink on a moved block + VTR_ASSERT_SAFE(cluster_ctx.clb_nlist.pin_type(pin_id) == PinType::SINK); + + /* The following "if" prevents the value from being updated twice. */ + if (!driven_by_moved_block(net_id)) { + int net_pin = cluster_ctx.clb_nlist.pin_net_index(pin_id, bnum); + + NPNR_ASSERT(temp_point_to_point_delay_cost[net_id->udata][net_pin] >= 0); + point_to_point_delay_cost[net_id->udata][net_pin] = temp_point_to_point_delay_cost[net_id->udata][net_pin]; + temp_point_to_point_delay_cost[net_id->udata][net_pin] = -1; + NPNR_ASSERT(temp_point_to_point_timing_cost[net_id->udata][net_pin] >= 0); + point_to_point_timing_cost[net_id->udata][net_pin] = temp_point_to_point_timing_cost[net_id->udata][net_pin]; + temp_point_to_point_timing_cost[net_id->udata][net_pin] = -1; + } + } + } /* Finished going through all the pins in the moved block */ + } /* Finished going through all the blocks moved */ +} + +static bool driven_by_moved_block(const /*ClusterNetId*/ NetInfo* net) { + auto& cluster_ctx = g_vpr_ctx.clustering(); + + auto net_driver_block = cluster_ctx.clb_nlist.net_driver_block(net); + for (const auto& b : blocks_affected) { + auto blk = b.first; + if (net_driver_block == blk) { + return true; + } + } + return false; +} + +static void comp_td_costs(float *timing_cost, float *connection_delay_sum) { + /* Computes the cost (from scratch) due to the delays and criticalities * + * on all point to point connections, we define the timing cost of * + * each connection as criticality*delay. */ + + unsigned ipin; + float loc_timing_cost, loc_connection_delay_sum, temp_delay_cost, + temp_timing_cost; + + auto& cluster_ctx = g_vpr_ctx.clustering(); + + loc_timing_cost = 0.; + loc_connection_delay_sum = 0.; + + for (const auto& net : cluster_ctx.clb_nlist.nets()) { /* For each net ... */ + auto net_id = net.second.get(); + + if (cluster_ctx.clb_nlist.net_is_global(net_id)) { /* Do only if not global. */ + continue; + } + + for (ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net_id).size(); ipin++) { + temp_delay_cost = comp_td_point_to_point_delay(net_id, ipin); + temp_timing_cost = temp_delay_cost * get_timing_place_crit(net_id, ipin); + + loc_connection_delay_sum += temp_delay_cost; + point_to_point_delay_cost[net_id->udata][ipin] = temp_delay_cost; + temp_point_to_point_delay_cost[net_id->udata][ipin] = -1; /* Undefined */ + + point_to_point_timing_cost[net_id->udata][ipin] = temp_timing_cost; + temp_point_to_point_timing_cost[net_id->udata][ipin] = -1; /* Undefined */ + loc_timing_cost += temp_timing_cost; + } + } + + /* Make sure timing cost does not go above MIN_TIMING_COST. */ + *timing_cost = loc_timing_cost; + + *connection_delay_sum = loc_connection_delay_sum; +} + + +/* Finds the cost from scratch. Done only when the placement * +* has been radically changed (i.e. after initial placement). * +* Otherwise find the cost change incrementally. If method * +* check is NORMAL, we find bounding boxes that are updateable * +* for the larger nets. If method is CHECK, all bounding boxes * +* are found via the non_updateable_bb routine, to provide a * +* cost which can be used to check the correctness of the * +* other routine. */ +static float comp_bb_cost(e_cost_methods method) { + float cost = 0; + double expected_wirelength = 0.0; + auto& cluster_ctx = g_vpr_ctx.clustering(); + + for (auto& n : cluster_ctx.clb_nlist.nets()) { /* for each net ... */ + auto net_id = n.second.get(); + if (!cluster_ctx.clb_nlist.net_is_global(net_id)) { /* Do only if not global. */ + /* Small nets don't use incremental updating on their bounding boxes, * + * so they can use a fast bounding box calculator. */ + if (net_id->users.size() >= SMALL_NET && method == NORMAL) { + get_bb_from_scratch(net_id, &bb_coords[net_id->udata], + &bb_num_on_edges[net_id->udata]); + } + else { + get_non_updateable_bb(net_id, &bb_coords[net_id->udata]); + } + + net_cost[net_id->udata] = get_net_cost(net_id, &bb_coords[net_id->udata]); + cost += net_cost[net_id->udata]; + if (method == CHECK) + expected_wirelength += get_net_wirelength_estimate(net_id, &bb_coords[net_id->udata]); + } + } + + if (method == CHECK) { + vtr::printf_info("\n"); + vtr::printf_info("BB estimate of min-dist (placement) wire length: %.0f\n", expected_wirelength); + } + return cost; +} + + +/* Frees the major structures needed by the placer (and not needed * +* elsewhere). */ +static void free_placement_structs(t_placer_opts placer_opts) { +// int imacro; + + auto& cluster_ctx = g_vpr_ctx.clustering(); + +// free_legal_placements(); +// free_fast_cost_update(); + + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE + || placer_opts.enable_timing_computations) { + + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + /*add one to the address since it is indexed from 1 not 0 */ + point_to_point_timing_cost[net_id->udata]++; + free(point_to_point_timing_cost[net_id->udata]); + + temp_point_to_point_timing_cost[net_id->udata]++; + free(temp_point_to_point_timing_cost[net_id->udata]); + + point_to_point_delay_cost[net_id->udata]++; + free(point_to_point_delay_cost[net_id->udata]); + + temp_point_to_point_delay_cost[net_id->udata]++; + free(temp_point_to_point_delay_cost[net_id->udata]); + } + + point_to_point_timing_cost.clear(); + point_to_point_delay_cost.clear(); + temp_point_to_point_timing_cost.clear(); + temp_point_to_point_delay_cost.clear(); + +// net_pin_indices.clear(); + } + +// free_placement_macros_structs(); +// +// for (imacro = 0; imacro < num_pl_macros; imacro++) +// free(pl_macros[imacro].members); +// free(pl_macros); +// +// /* Defensive coding. */ +// pl_macros = nullptr; +// +// /* Frees up all the data structure used in vpr_utils. */ +// free_port_pin_from_blk_pin(); +// free_blk_pin_from_port_pin(); + +} + +/* Allocates the major structures needed only by the placer, primarily for * +* computing costs quickly and such. */ +static void alloc_and_load_placement_structs( + /*float place_cost_exp,*/ t_placer_opts placer_opts /*, + t_direct_inf *directs, int num_directs*/) { + +// int max_pins_per_clb, i; + unsigned int ipin; + +// auto& device_ctx = g_vpr_ctx.device(); + auto& cluster_ctx = g_vpr_ctx.clustering(); + + size_t num_nets = cluster_ctx.clb_nlist.nets().size(); + ++num_nets; // Because VPR needs it so + +// init_placement_context(); +// +// alloc_legal_placements(); + load_legal_placements(); +// +// max_pins_per_clb = 0; +// for (i = 0; i < device_ctx.num_block_types; i++) { +// max_pins_per_clb = max(max_pins_per_clb, device_ctx.block_types[i].num_pins); +// } +// + if (placer_opts.place_algorithm == PATH_TIMING_DRIVEN_PLACE + || placer_opts.enable_timing_computations) { + /* Allocate structures associated with timing driven placement */ + /* [0..cluster_ctx.clb_nlist.nets().size()-1][1..num_pins-1] */ + point_to_point_delay_cost.resize(num_nets); + temp_point_to_point_delay_cost.resize(num_nets); + + point_to_point_timing_cost.resize(num_nets); + temp_point_to_point_timing_cost.resize(num_nets); + + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + size_t num_sinks = cluster_ctx.clb_nlist.net_sinks(net_id).size(); + /* In the following, subract one so index starts at * + * 1 instead of 0 */ + point_to_point_delay_cost[net_id->udata] = (float *)malloc(num_sinks * sizeof(float)); + point_to_point_delay_cost[net_id->udata]--; + + temp_point_to_point_delay_cost[net_id->udata] = (float *)malloc(num_sinks * sizeof(float)); + temp_point_to_point_delay_cost[net_id->udata]--; + + point_to_point_timing_cost[net_id->udata] = (float *)malloc(num_sinks * sizeof(float)); + point_to_point_timing_cost[net_id->udata]--; + + temp_point_to_point_timing_cost[net_id->udata] = (float *)malloc(num_sinks * sizeof(float)); + temp_point_to_point_timing_cost[net_id->udata]--; + } + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + for (ipin = 1; ipin < cluster_ctx.clb_nlist.net_pins(net_id).size(); ipin++) { + point_to_point_delay_cost[net_id->udata][ipin] = 0; + temp_point_to_point_delay_cost[net_id->udata][ipin] = 0; + } + } + } + + net_cost.resize(num_nets, -1); + temp_net_cost.resize(num_nets, -1); + bb_coords.resize(num_nets, t_bb()); + bb_num_on_edges.resize(num_nets, t_bb()); + + /* Used to store costs for moves not yet made and to indicate when a net's * + * cost has been recomputed. temp_net_cost[inet] < 0 means net's cost hasn't * + * been recomputed. */ + bb_updated_before.resize(num_nets, NOT_UPDATED_YET); + +// alloc_and_load_for_fast_cost_update(place_cost_exp); +// +// alloc_and_load_net_pin_indices(); + + alloc_and_load_try_swap_structs(); + + /*num_pl_macros =*/ alloc_and_load_placement_macros(/*directs, num_directs, &*/ pl_macros); +} + +///* Allocates and loads net_pin_indices array, this array allows us to quickly * +//* find what pin on the net a block pin corresponds to. Returns the pointer * +//* to the 2D net_pin_indices array. */ +//static void alloc_and_load_net_pin_indices() { +// unsigned int netpin; +// int itype, max_pins_per_clb = 0; +// +// auto& device_ctx = g_vpr_ctx.device(); +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// +// /* Compute required size. */ +// for (itype = 0; itype < device_ctx.num_block_types; itype++) +// max_pins_per_clb = max(max_pins_per_clb, device_ctx.block_types[itype].num_pins); +// +// /* Allocate for maximum size. */ +// net_pin_indices.resize(cluster_ctx.clb_nlist.blocks().size()); +// +// for (auto blk_id : cluster_ctx.clb_nlist.blocks()) +// net_pin_indices[blk_id].resize(max_pins_per_clb); +// +// /* Load the values */ +// for (auto net_id : cluster_ctx.clb_nlist.nets()) { +// if (cluster_ctx.clb_nlist.net_is_global(net_id)) +// continue; +// netpin = 0; +// for (auto pin_id : cluster_ctx.clb_nlist.net_pins(net_id)) { +// int pin_index = cluster_ctx.clb_nlist.pin_physical_index(pin_id); +// ClusterBlockId block_id = cluster_ctx.clb_nlist.pin_block(pin_id); +// net_pin_indices[block_id][pin_index] = netpin; +// netpin++; +// } +// } +//} + +static void alloc_and_load_try_swap_structs() { + /* Allocate the local bb_coordinate storage, etc. only once. */ + /* Allocate with size cluster_ctx.clb_nlist.nets().size() for any number of nets affected. */ + auto& cluster_ctx = g_vpr_ctx.clustering(); + + size_t num_nets = cluster_ctx.clb_nlist.nets().size(); + ++num_nets; // Because VPR needs it so + + ts_bb_coord_new.resize(num_nets, t_bb()); + ts_bb_edge_new.resize(num_nets, t_bb()); +// ts_nets_to_update.resize(num_nets, ClusterNetId::INVALID()); + +// /* Allocate with size cluster_ctx.clb_nlist.blocks().size() for any number of moved blocks. */ +// blocks_affected.moved_blocks = (t_pl_moved_block*) vtr::calloc((int) cluster_ctx.clb_nlist.blocks().size(), sizeof(t_pl_moved_block)); +// blocks_affected.num_moved_blocks = 0; +} + +/* This routine finds the bounding box of each net from scratch (i.e. * +* from only the block location information). It updates both the * +* coordinate and number of pins on each edge information. It * +* should only be called when the bounding box information is not valid. */ +static void get_bb_from_scratch(/*ClusterNetId*/ NetInfo *net_id, t_bb *coords, + t_bb *num_on_edges) { + int /*pnum,*/ x, y, xmin, xmax, ymin, ymax; + int xmin_edge, xmax_edge, ymin_edge, ymax_edge; + +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.placement(); + auto& device_ctx = g_vpr_ctx.device(); + auto& grid = device_ctx.grid; + + auto bnum = net_id->driver.cell; +// pnum = cluster_ctx.clb_nlist.net_pin_physical_index(net_id, 0); + auto loc = npnr_ctx->getBelLocation(bnum->bel); + x = loc.x /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_width_offset[pnum]*/; + y = loc.y /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_height_offset[pnum]*/; + + x = std::max(std::min(x, grid.width() - 2), 1); + y = std::max(std::min(y, grid.height() - 2), 1); + + xmin = x; + ymin = y; + xmax = x; + ymax = y; + xmin_edge = 1; + ymin_edge = 1; + xmax_edge = 1; + ymax_edge = 1; + + for (auto pin_id : net_id->users) { + bnum = pin_id.cell; + //pnum = cluster_ctx.clb_nlist.pin_physical_index(pin_id); + auto loc = npnr_ctx->getBelLocation(bnum->bel); + x = loc.x /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_width_offset[pnum]*/; + y = loc.y /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_height_offset[pnum]*/; + + /* Code below counts IO blocks as being within the 1..grid.width()-2, 1..grid.height()-2 clb array. * + * This is because channels do not go out of the 0..grid.width()-2, 0..grid.height()-2 range, and * + * I always take all channels impinging on the bounding box to be within * + * that bounding box. Hence, this "movement" of IO blocks does not affect * + * the which channels are included within the bounding box, and it * + * simplifies the code a lot. */ + + x = std::max(std::min(x, grid.width() - 2), 1); //-2 for no perim channels + y = std::max(std::min(y, grid.height() - 2), 1); //-2 for no perim channels + + if (x == xmin) { + xmin_edge++; + } + if (x == xmax) { /* Recall that xmin could equal xmax -- don't use else */ + xmax_edge++; + } + else if (x < xmin) { + xmin = x; + xmin_edge = 1; + } + else if (x > xmax) { + xmax = x; + xmax_edge = 1; + } + + if (y == ymin) { + ymin_edge++; + } + if (y == ymax) { + ymax_edge++; + } + else if (y < ymin) { + ymin = y; + ymin_edge = 1; + } + else if (y > ymax) { + ymax = y; + ymax_edge = 1; + } + } + + /* Copy the coordinates and number on edges information into the proper * + * structures. */ + coords->xmin = xmin; + coords->xmax = xmax; + coords->ymin = ymin; + coords->ymax = ymax; + + num_on_edges->xmin = xmin_edge; + num_on_edges->xmax = xmax_edge; + num_on_edges->ymin = ymin_edge; + num_on_edges->ymax = ymax_edge; +} + +static double get_net_wirelength_estimate(/*ClusterNetId*/ NetInfo* net_id, t_bb *bbptr) { + + /* WMF: Finds the estimate of wirelength due to one net by looking at * + * its coordinate bounding box. */ + + double ncost, crossing; + auto& cluster_ctx = g_vpr_ctx.clustering(); + + /* Get the expected "crossing count" of a net, based on its number * + * of pins. Extrapolate for very large nets. */ + + if (((cluster_ctx.clb_nlist.net_pins(net_id).size()) > 50) + && ((cluster_ctx.clb_nlist.net_pins(net_id).size()) < 85)) { + crossing = 2.7933 + 0.02616 * ((cluster_ctx.clb_nlist.net_pins(net_id).size()) - 50); + } else if ((cluster_ctx.clb_nlist.net_pins(net_id).size()) >= 85) { + crossing = 2.7933 + 0.011 * (cluster_ctx.clb_nlist.net_pins(net_id).size()) + - 0.0000018 * (cluster_ctx.clb_nlist.net_pins(net_id).size()) + * (cluster_ctx.clb_nlist.net_pins(net_id).size()); + } else { + crossing = cross_count[cluster_ctx.clb_nlist.net_pins(net_id).size() - 1]; + } + + /* Could insert a check for xmin == xmax. In that case, assume * + * connection will be made with no bends and hence no x-cost. * + * Same thing for y-cost. */ + + /* Cost = wire length along channel * cross_count / average * + * channel capacity. Do this for x, then y direction and add. */ + + ncost = (bbptr->xmax - bbptr->xmin + 1) * crossing; + + ncost += (bbptr->ymax - bbptr->ymin + 1) * crossing; + + return (ncost); +} + +static float get_net_cost(/*ClusterNetId*/ NetInfo* net_id, t_bb *bbptr) { + + /* Finds the cost due to one net by looking at its coordinate bounding * + * box. */ + + float ncost, crossing; +// auto& cluster_ctx = g_vpr_ctx.clustering(); + + /* Get the expected "crossing count" of a net, based on its number * + * of pins. Extrapolate for very large nets. */ + + if (net_id->users.size() > 50) { + crossing = 2.7933 + 0.02616 * (net_id->users.size() - 50); + /* crossing = 3.0; Old value */ + } else { + crossing = cross_count[net_id->users.size() - 1]; + } + + /* Could insert a check for xmin == xmax. In that case, assume * + * connection will be made with no bends and hence no x-cost. * + * Same thing for y-cost. */ + + /* Cost = wire length along channel * cross_count / average * + * channel capacity. Do this for x, then y direction and add. */ + + ncost = (bbptr->xmax - bbptr->xmin + 1) * crossing + /** chanx_place_cost_fac[bbptr->ymax][bbptr->ymin - 1]*/; + + ncost += (bbptr->ymax - bbptr->ymin + 1) * crossing + /** chany_place_cost_fac[bbptr->xmax][bbptr->xmin - 1]*/; + + return (ncost); +} + +/* Finds the bounding box of a net and stores its coordinates in the * +* bb_coord_new data structure. This routine should only be called * +* for small nets, since it does not determine enough information for * +* the bounding box to be updated incrementally later. * +* Currently assumes channels on both sides of the CLBs forming the * +* edges of the bounding box can be used. Essentially, I am assuming * +* the pins always lie on the outside of the bounding box. */ +static void get_non_updateable_bb(/*ClusterNetId*/ NetInfo* net_id, t_bb *bb_coord_new) { + //TODO: account for multiple physical pin instances per logical pin + + int xmax, ymax, xmin, ymin, x, y; +// int pnum; + +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.placement(); + auto& device_ctx = g_vpr_ctx.device(); + + auto bnum = net_id->driver.cell; +// pnum = cluster_ctx.clb_nlist.net_pin_physical_index(net_id, 0); + auto loc = npnr_ctx->getBelLocation(bnum->bel); + x = loc.x /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_width_offset[pnum]*/; + y = loc.y /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_height_offset[pnum]*/; + + + xmin = x; + ymin = y; + xmax = x; + ymax = y; + + for (auto pin_id : net_id->users) { + bnum = pin_id.cell; + //pnum = cluster_ctx.clb_nlist.pin_physical_index(pin_id); + auto loc = npnr_ctx->getBelLocation(bnum->bel); + x = loc.x /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_width_offset[pnum]*/; + y = loc.y /*+ cluster_ctx.clb_nlist.block_type(bnum)->pin_height_offset[pnum]*/; + + if (x < xmin) { + xmin = x; + } else if (x > xmax) { + xmax = x; + } + + if (y < ymin) { + ymin = y; + } else if (y > ymax) { + ymax = y; + } + } + + /* Now I've found the coordinates of the bounding box. There are no * + * channels beyond device_ctx.grid.width()-2 and * + * device_ctx.grid.height() - 2, so I want to clip to that. As well,* + * since I'll always include the channel immediately below and the * + * channel immediately to the left of the bounding box, I want to * + * clip to 1 in both directions as well (since minimum channel index * + * is 0). See route_common.cpp for a channel diagram. */ + + bb_coord_new->xmin = std::max(std::min(xmin, device_ctx.grid.width() - 2), 1); //-2 for no perim channels + bb_coord_new->ymin = std::max(std::min(ymin, device_ctx.grid.height() - 2), 1); //-2 for no perim channels + bb_coord_new->xmax = std::max(std::min(xmax, device_ctx.grid.width() - 2), 1); //-2 for no perim channels + bb_coord_new->ymax = std::max(std::min(ymax, device_ctx.grid.height() - 2), 1); //-2 for no perim channels +} + +static void update_bb(/*ClusterNetId*/ NetInfo* net_id, t_bb *bb_coord_new, + t_bb *bb_edge_new, int xold, int yold, int xnew, int ynew) { + + /* Updates the bounding box of a net by storing its coordinates in * + * the bb_coord_new data structure and the number of blocks on each * + * edge in the bb_edge_new data structure. This routine should only * + * be called for large nets, since it has some overhead relative to * + * just doing a brute force bounding box calculation. The bounding * + * box coordinate and edge information for inet must be valid before * + * this routine is called. * + * Currently assumes channels on both sides of the CLBs forming the * + * edges of the bounding box can be used. Essentially, I am assuming * + * the pins always lie on the outside of the bounding box. * + * The x and y coordinates are the pin's x and y coordinates. */ + /* IO blocks are considered to be one cell in for simplicity. */ + //TODO: account for multiple physical pin instances per logical pin + + t_bb *curr_bb_edge, *curr_bb_coord; + + auto& device_ctx = g_vpr_ctx.device(); + + xnew = std::max(std::min(xnew, device_ctx.grid.width() - 2), 1); //-2 for no perim channels + ynew = std::max(std::min(ynew, device_ctx.grid.height() - 2), 1); //-2 for no perim channels + xold = std::max(std::min(xold, device_ctx.grid.width() - 2), 1); //-2 for no perim channels + yold = std::max(std::min(yold, device_ctx.grid.height() - 2), 1); //-2 for no perim channels + + /* Check if the net had been updated before. */ + if (bb_updated_before[net_id->udata] == GOT_FROM_SCRATCH) { + /* The net had been updated from scratch, DO NOT update again! */ + return; + } else if (bb_updated_before[net_id->udata] == NOT_UPDATED_YET) { + /* The net had NOT been updated before, could use the old values */ + curr_bb_coord = &bb_coords[net_id->udata]; + curr_bb_edge = &bb_num_on_edges[net_id->udata]; + bb_updated_before[net_id->udata] = UPDATED_ONCE; + } else { + /* The net had been updated before, must use the new values */ + curr_bb_coord = bb_coord_new; + curr_bb_edge = bb_edge_new; + } + + /* Check if I can update the bounding box incrementally. */ + + if (xnew < xold) { /* Move to left. */ + + /* Update the xmax fields for coordinates and number of edges first. */ + + if (xold == curr_bb_coord->xmax) { /* Old position at xmax. */ + if (curr_bb_edge->xmax == 1) { + get_bb_from_scratch(net_id, bb_coord_new, bb_edge_new); + bb_updated_before[net_id->udata] = GOT_FROM_SCRATCH; + return; + } else { + bb_edge_new->xmax = curr_bb_edge->xmax - 1; + bb_coord_new->xmax = curr_bb_coord->xmax; + } + } else { /* Move to left, old postion was not at xmax. */ + bb_coord_new->xmax = curr_bb_coord->xmax; + bb_edge_new->xmax = curr_bb_edge->xmax; + } + + /* Now do the xmin fields for coordinates and number of edges. */ + + if (xnew < curr_bb_coord->xmin) { /* Moved past xmin */ + bb_coord_new->xmin = xnew; + bb_edge_new->xmin = 1; + } else if (xnew == curr_bb_coord->xmin) { /* Moved to xmin */ + bb_coord_new->xmin = xnew; + bb_edge_new->xmin = curr_bb_edge->xmin + 1; + } else { /* Xmin unchanged. */ + bb_coord_new->xmin = curr_bb_coord->xmin; + bb_edge_new->xmin = curr_bb_edge->xmin; + } + /* End of move to left case. */ + + } else if (xnew > xold) { /* Move to right. */ + + /* Update the xmin fields for coordinates and number of edges first. */ + + if (xold == curr_bb_coord->xmin) { /* Old position at xmin. */ + if (curr_bb_edge->xmin == 1) { + get_bb_from_scratch(net_id, bb_coord_new, bb_edge_new); + bb_updated_before[net_id->udata] = GOT_FROM_SCRATCH; + return; + } else { + bb_edge_new->xmin = curr_bb_edge->xmin - 1; + bb_coord_new->xmin = curr_bb_coord->xmin; + } + } else { /* Move to right, old position was not at xmin. */ + bb_coord_new->xmin = curr_bb_coord->xmin; + bb_edge_new->xmin = curr_bb_edge->xmin; + } + + /* Now do the xmax fields for coordinates and number of edges. */ + + if (xnew > curr_bb_coord->xmax) { /* Moved past xmax. */ + bb_coord_new->xmax = xnew; + bb_edge_new->xmax = 1; + } else if (xnew == curr_bb_coord->xmax) { /* Moved to xmax */ + bb_coord_new->xmax = xnew; + bb_edge_new->xmax = curr_bb_edge->xmax + 1; + } else { /* Xmax unchanged. */ + bb_coord_new->xmax = curr_bb_coord->xmax; + bb_edge_new->xmax = curr_bb_edge->xmax; + } + /* End of move to right case. */ + + } else { /* xnew == xold -- no x motion. */ + bb_coord_new->xmin = curr_bb_coord->xmin; + bb_coord_new->xmax = curr_bb_coord->xmax; + bb_edge_new->xmin = curr_bb_edge->xmin; + bb_edge_new->xmax = curr_bb_edge->xmax; + } + + /* Now account for the y-direction motion. */ + + if (ynew < yold) { /* Move down. */ + + /* Update the ymax fields for coordinates and number of edges first. */ + + if (yold == curr_bb_coord->ymax) { /* Old position at ymax. */ + if (curr_bb_edge->ymax == 1) { + get_bb_from_scratch(net_id, bb_coord_new, bb_edge_new); + bb_updated_before[net_id->udata] = GOT_FROM_SCRATCH; + return; + } else { + bb_edge_new->ymax = curr_bb_edge->ymax - 1; + bb_coord_new->ymax = curr_bb_coord->ymax; + } + } else { /* Move down, old postion was not at ymax. */ + bb_coord_new->ymax = curr_bb_coord->ymax; + bb_edge_new->ymax = curr_bb_edge->ymax; + } + + /* Now do the ymin fields for coordinates and number of edges. */ + + if (ynew < curr_bb_coord->ymin) { /* Moved past ymin */ + bb_coord_new->ymin = ynew; + bb_edge_new->ymin = 1; + } else if (ynew == curr_bb_coord->ymin) { /* Moved to ymin */ + bb_coord_new->ymin = ynew; + bb_edge_new->ymin = curr_bb_edge->ymin + 1; + } else { /* ymin unchanged. */ + bb_coord_new->ymin = curr_bb_coord->ymin; + bb_edge_new->ymin = curr_bb_edge->ymin; + } + /* End of move down case. */ + + } else if (ynew > yold) { /* Moved up. */ + + /* Update the ymin fields for coordinates and number of edges first. */ + + if (yold == curr_bb_coord->ymin) { /* Old position at ymin. */ + if (curr_bb_edge->ymin == 1) { + get_bb_from_scratch(net_id, bb_coord_new, bb_edge_new); + bb_updated_before[net_id->udata] = GOT_FROM_SCRATCH; + return; + } else { + bb_edge_new->ymin = curr_bb_edge->ymin - 1; + bb_coord_new->ymin = curr_bb_coord->ymin; + } + } else { /* Moved up, old position was not at ymin. */ + bb_coord_new->ymin = curr_bb_coord->ymin; + bb_edge_new->ymin = curr_bb_edge->ymin; + } + + /* Now do the ymax fields for coordinates and number of edges. */ + + if (ynew > curr_bb_coord->ymax) { /* Moved past ymax. */ + bb_coord_new->ymax = ynew; + bb_edge_new->ymax = 1; + } else if (ynew == curr_bb_coord->ymax) { /* Moved to ymax */ + bb_coord_new->ymax = ynew; + bb_edge_new->ymax = curr_bb_edge->ymax + 1; + } else { /* ymax unchanged. */ + bb_coord_new->ymax = curr_bb_coord->ymax; + bb_edge_new->ymax = curr_bb_edge->ymax; + } + /* End of move up case. */ + + } else { /* ynew == yold -- no y motion. */ + bb_coord_new->ymin = curr_bb_coord->ymin; + bb_coord_new->ymax = curr_bb_coord->ymax; + bb_edge_new->ymin = curr_bb_edge->ymin; + bb_edge_new->ymax = curr_bb_edge->ymax; + } + + if (bb_updated_before[net_id->udata] == NOT_UPDATED_YET) { + bb_updated_before[net_id->udata] = UPDATED_ONCE; + } +} + +//static void alloc_legal_placements() { +// auto& device_ctx = g_vpr_ctx.device(); +// auto& place_ctx = g_vpr_ctx.mutable_placement(); +// +// legal_pos = (t_legal_pos **) vtr::malloc(device_ctx.num_block_types * sizeof(t_legal_pos *)); +// num_legal_pos = (int *) vtr::calloc(device_ctx.num_block_types, sizeof(int)); +// +// /* Initialize all occupancy to zero. */ +// +// for (size_t i = 0; i < device_ctx.grid.width(); i++) { +// for (size_t j = 0; j < device_ctx.grid.height(); j++) { +// place_ctx.grid_blocks[i][j].usage = 0; +// +// for (int k = 0; k < device_ctx.grid[i][j].type->capacity; k++) { +// +// if (place_ctx.grid_blocks[i][j].blocks[k] != INVALID_BLOCK_ID) { +// place_ctx.grid_blocks[i][j].blocks[k] = EMPTY_BLOCK_ID; +// if (device_ctx.grid[i][j].width_offset == 0 && device_ctx.grid[i][j].height_offset == 0) { +// num_legal_pos[device_ctx.grid[i][j].type->index]++; +// } +// } +// } +// } +// } +// +// for (int i = 0; i < device_ctx.num_block_types; i++) { +// legal_pos[i] = (t_legal_pos *) vtr::malloc(num_legal_pos[i] * sizeof(t_legal_pos)); +// } +//} + +static void load_legal_placements() { +// auto& device_ctx = g_vpr_ctx.device(); +// auto& place_ctx = g_vpr_ctx.placement(); +// +// int* index = (int *) vtr::calloc(device_ctx.num_block_types, sizeof(int)); +// +// for (size_t i = 0; i < device_ctx.grid.width(); i++) { +// for (size_t j = 0; j < device_ctx.grid.height(); j++) { +// for (int k = 0; k < device_ctx.grid[i][j].type->capacity; k++) { +// if (place_ctx.grid_blocks[i][j].blocks[k] == INVALID_BLOCK_ID) { +// continue; +// } +// if (device_ctx.grid[i][j].width_offset == 0 && device_ctx.grid[i][j].height_offset == 0) { +// int itype = device_ctx.grid[i][j].type->index; +// legal_pos[itype][index[itype]].x = i; +// legal_pos[itype][index[itype]].y = j; +// legal_pos[itype][index[itype]].z = k; +// index[itype]++; +// } +// } +// } +// } +// free(index); + + for (auto bel : npnr_ctx->getBels()) { + auto type = npnr_ctx->getBelType(bel); + int itype = type.index; + if (itype >= int(legal_pos.size())) + legal_pos.resize(itype+1); + legal_pos[itype].push_back(npnr_ctx->getBelLocation(bel)); + } +} + +//static void free_legal_placements() { +// auto& device_ctx = g_vpr_ctx.device(); +// +// for (int i = 0; i < device_ctx.num_block_types; i++) { +// free(legal_pos[i]); +// } +// free(legal_pos); /* Free the mapping list */ +// free(num_legal_pos); +//} + + + +static int check_macro_can_be_placed(int imacro, int itype, int x, int y, int z) { + + int imember; + size_t member_x, member_y, member_z; + + auto& device_ctx = g_vpr_ctx.device(); +// auto& place_ctx = g_vpr_ctx.placement(); + + // Every macro can be placed until proven otherwise + int macro_can_be_placed = true; + + // Check whether all the members can be placed + for (imember = 0; imember < int(pl_macros[imacro].members.size()); imember++) { + member_x = x + pl_macros[imacro].members[imember].x_offset; + member_y = y + pl_macros[imacro].members[imember].y_offset; + member_z = z + pl_macros[imacro].members[imember].z_offset; + + // Check whether the location could accept block of this type + // Then check whether the location could still accomodate more blocks + // Also check whether the member position is valid, that is the member's location + // still within the chip's dimemsion and the member_z is allowed at that location on the grid + if (member_x < device_ctx.grid.width() && member_y < device_ctx.grid.height() + && member_z < device_ctx.grid[member_x][member_y].size() + && npnr_ctx->getBelType(device_ctx.grid[member_x][member_y][member_z]) == itype + && npnr_ctx->checkBelAvail(device_ctx.grid[member_x][member_y][member_z]) + && npnr_ctx->isValidBelForCell(pl_macros[imacro].members[imember].blk_index, device_ctx.grid[member_x][member_y][member_z])) { + // Can still accomodate blocks here, check the next position + continue; + } else { + // Cant be placed here - skip to the next try + macro_can_be_placed = false; + break; + } + } + + return (macro_can_be_placed); +} + + +static int try_place_macro(int itype, /*int*/ Loc ipos, int imacro) { + + int x, y, z, member_x, member_y, member_z, imember; + +// auto& place_ctx = g_vpr_ctx.mutable_placement(); + auto& device_ctx = g_vpr_ctx.device(); + auto& grid = device_ctx.grid; + + int macro_placed = false; + + // Choose a random position for the head + x = ipos.x; + y = ipos.y; + z = ipos.z; + + // If that location is occupied, do nothing. + if (!npnr_ctx->checkBelAvail(grid[x][y][z])) { + return (macro_placed); + } + + if (!npnr_ctx->isValidBelForCell(pl_macros[imacro].members[0].blk_index, grid[x][y][z])) + return macro_placed; + + int macro_can_be_placed = check_macro_can_be_placed(imacro, itype, x, y, z); + + if (macro_can_be_placed) { + + // Place down the macro + macro_placed = true; + for (imember = 0; imember < int(pl_macros[imacro].members.size()); imember++) { + + member_x = x + pl_macros[imacro].members[imember].x_offset; + member_y = y + pl_macros[imacro].members[imember].y_offset; + member_z = z + pl_macros[imacro].members[imember].z_offset; + + auto iblk = pl_macros[imacro].members[imember].blk_index; + auto bel = grid[member_x][member_y][member_z]; + + npnr_ctx->bindBel(bel, iblk, STRENGTH_WEAK); + + // Could not ensure that the randomiser would not pick this location again + // So, would have to do a lazy removal - whenever I come across a block that could not be placed, + // go ahead and remove it from the legal_pos[][] array + + } // Finish placing all the members in the macro + + } // End of this choice of legal_pos + + return (macro_placed); +} + +static void initial_placement_pl_macros(int macros_max_num_tries, /*int * */ std::vector> &free_locations) { + + int macro_placed; + int imacro, itype, itry /*, ipos*/; + /*ClusterBlockId*/ CellInfo *blk_id; + Loc ipos; + + auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& device_ctx = g_vpr_ctx.device(); + + /* Macros are harder to place. Do them first */ + for (imacro = 0; imacro < int(pl_macros.size()); imacro++) { + + // Every macro are not placed in the beginnning + macro_placed = false; + + blk_id = pl_macros[imacro].members[0].blk_index; + + // Assume that all the blocks in the macro are of the same type + itype = cluster_ctx.clb_nlist.block_type(blk_id).index; + if (free_locations[itype].size() < pl_macros[imacro].members.size()) { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "Initial placement failed.\n" + "Could not place macro length %d with head block %s (#%zu); not enough free locations of type %s (#%d).\n" + "VPR cannot auto-size for your circuit, please resize the FPGA manually.\n", + int(pl_macros[imacro].members.size()), cluster_ctx.clb_nlist.block_name(blk_id).c_str(), size_t(blk_id->udata), cluster_ctx.clb_nlist.block_type(blk_id).c_str(npnr_ctx), itype); + } + + // Try to place the macro first, if can be placed - place them, otherwise try again + for (itry = 0; itry < macros_max_num_tries && macro_placed == false; itry++) { + + // Choose a random position for the head + ipos = free_locations[itype][vtr::irand(free_locations[itype].size() - 1)]; + + // Try to place the macro + macro_placed = try_place_macro(itype, ipos, imacro); + + } // Finished all tries + + if (macro_placed == false){ + // if a macro still could not be placed after macros_max_num_tries times, + // go through the chip exhaustively to find a legal placement for the macro + // place the macro on the first location that is legal + // then set macro_placed = true; + // if there are no legal positions, error out + + // Exhaustive placement of carry macros + for (auto ipos : free_locations[itype]) { + + // Try to place the macro + macro_placed = try_place_macro(itype, ipos, imacro); + + if (macro_placed == true) break; + + } // Exhausted all the legal placement position for this macro + + // If macro could not be placed after exhaustive placement, error out + if (macro_placed == false) { + // Error out + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "Initial placement failed.\n" + "Could not place macro length %d with head block %s (#%zu); not enough free locations of type %s (#%d).\n" + "Please manually size the FPGA because VPR can't do this yet.\n", + int(pl_macros[imacro].members.size()), cluster_ctx.clb_nlist.block_name(blk_id).c_str(), size_t(blk_id->udata), blk_id->type.c_str(npnr_ctx), itype); + } + + } else { + // This macro has been placed successfully, proceed to place the next macro + continue; + } + } // Finish placing all the pl_macros successfully +} + +/* Place blocks that are NOT a part of any macro. +* We'll randomly place each block in the clustered netlist, one by one. */ +static void initial_placement_blocks(/*int * free_locations, enum e_pad_loc_type pad_loc_type*/ + std::vector>& free_locations) { + int itype, /*ipos,*/ x, y, z; + auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.mutable_placement(); + auto& device_ctx = g_vpr_ctx.device(); + auto& grid = device_ctx.grid; + + for (auto& b : cluster_ctx.clb_nlist.blocks()) { + auto blk_id = b.second.get(); +// if (place_ctx.block_locs[blk_id].x != -1) { // -1 is a sentinel for an empty block +// // block placed. +// continue; +// } + + if (blk_id->bel != BelId()) + continue; + +// /* Don't do IOs if the user specifies IOs; we'll read those locations later. */ +// if (!(is_io_type(cluster_ctx.clb_nlist.block_type(blk_id)) && pad_loc_type == USER)) { +// +// /* Randomly select a free location of the appropriate type for blk_id. +// * We have a linearized list of all the free locations that can +// * accomodate a block of that type in free_locations[itype]. +// * Choose one randomly and put blk_id there. Then we don't want to pick +// * that location again, so remove it from the free_locations array. +// */ + itype = cluster_ctx.clb_nlist.block_type(blk_id).index; +// if (free_locations[itype] <= 0) { +// vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, +// "Initial placement failed.\n" +// "Could not place block %s (#%zu); no free locations of type %s (#%d).\n", +// cluster_ctx.clb_nlist.block_name(blk_id).c_str(), size_t(blk_id), device_ctx.block_types[itype].name, itype); +// } +// + initial_placement_location(free_locations, blk_id, /*&ipos,*/ &x, &y, &z); +// +// // Make sure that the position is EMPTY_BLOCK before placing the block down +// VTR_ASSERT(place_ctx.grid_blocks[x][y].blocks[z] == EMPTY_BLOCK_ID); + VTR_ASSERT(npnr_ctx->checkBelAvail(grid[x][y][z])); + +// place_ctx.grid_blocks[x][y].blocks[z] = blk_id; +// place_ctx.grid_blocks[x][y].usage++; +// +// place_ctx.block_locs[blk_id].x = x; +// place_ctx.block_locs[blk_id].y = y; +// place_ctx.block_locs[blk_id].z = z; +// +// //Mark IOs as fixed if specifying a (fixed) random placement +// if(is_io_type(cluster_ctx.clb_nlist.block_type(blk_id)) && pad_loc_type == RANDOM) { +// place_ctx.block_locs[blk_id].is_fixed = true; +// } + + if (npnr_ctx->isIO(blk_id)) + npnr_ctx->bindBel(grid[x][y][z], blk_id, STRENGTH_USER); + else + npnr_ctx->bindBel(grid[x][y][z], blk_id, STRENGTH_WEAK); + //NPNR_ASSERT(npnr_ctx->isBelLocationValid(bel)); + +// /* Ensure randomizer doesn't pick this location again, since it's occupied. Could shift all the +// * legal positions in legal_pos to remove the entry (choice) we just used, but faster to +// * just move the last entry in legal_pos to the spot we just used and decrement the +// * count of free_locations. */ +// legal_pos[itype][ipos] = legal_pos[itype][free_locations[itype] - 1]; /* overwrite used block position */ +// free_locations[itype]--; + + free_locations.at(itype).pop_back(); + +// } + } +} + +static void initial_placement_location(/*int **/ std::vector> &free_locations, /*ClusterBlockId*/ CellInfo *blk_id, + /*int *pipos,*/ int *px, int *py, int *pz) { + + auto& cluster_ctx = g_vpr_ctx.clustering(); + auto& grid = g_vpr_ctx.device().grid; + + int itype = cluster_ctx.clb_nlist.block_type(blk_id).index; + + auto it = free_locations.at(itype).rbegin(); + auto ie = free_locations.at(itype).rend(); + for (; it != ie; ++it) { + *px = it->x; + *py = it->y; + *pz = it->z; + + if (!npnr_ctx->isValidBelForCell(blk_id, grid[*px][*py][*pz])) + continue; + + std::swap(*it, free_locations.at(itype).back()); + return; + } + throw; +} + +static void initial_placement(/*enum e_pad_loc_type pad_loc_type, + const char *pad_loc_file*/) { + +// /* Randomly places the blocks to create an initial placement. We rely on +// * the legal_pos array already being loaded. That legal_pos[itype] is an +// * array that gives every legal value of (x,y,z) that can accomodate a block. +// * The number of such locations is given by num_legal_pos[itype]. +// */ +// int itype, x, y, z, ipos; +// int *free_locations; /* [0..device_ctx.num_block_types-1]. +// * Stores how many locations there are for this type that *might* still be free. +// * That is, this stores the number of entries in legal_pos[itype] that are worth considering +// * as you look for a free location. +// */ + std::vector> free_locations; + auto& device_ctx = g_vpr_ctx.device(); +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.mutable_placement(); + auto& grid = device_ctx.grid; + + free_locations.resize(legal_pos.size()); + + // HACK HACK HACK + // Initially, populate free_locations with just z == 0 cells + // for carry chain placement + for (auto it = legal_pos.begin(); it != legal_pos.end(); it++) { + for (auto jt = it->begin(); jt != it->end(); jt++) { + if (jt->z == 0) + free_locations[it - legal_pos.begin()].push_back(*jt); + } + } + + initial_placement_pl_macros(MAX_NUM_TRIES_TO_PLACE_MACROS_RANDOMLY, free_locations); + + // All the macros are placed, update the legal_pos[][] array + for (auto it = legal_pos.begin(); it != legal_pos.end(); it++) { + it->erase(remove_if(it->begin(), it->end(), [&grid](const Loc& loc) { + auto cell = npnr_ctx->getBoundBelCell(grid[loc.x][loc.y][loc.z]); + if (!cell) return false; + return cell->belStrength > STRENGTH_WEAK; + }), it->end()); + } + for (auto it = free_locations.begin(); it != free_locations.end(); it++) { + const auto& src = legal_pos.at(it - free_locations.begin()); + if (src.empty()) continue; + it->clear(); + it->reserve(src.size()); + for (auto& loc : src) { + auto cell = npnr_ctx->getBoundBelCell(grid[loc.x][loc.y][loc.z]); + if (!cell) + it->push_back(loc); + } + npnr_ctx->shuffle(*it); + } + + initial_placement_blocks(free_locations); + + // All constraints (including user pads) are placed + // before try_place() +// if (pad_loc_type == USER) { +// read_user_pad_loc(pad_loc_file); +// } + +// /* Restore legal_pos */ +// load_legal_placements(); +// +//#ifdef VERBOSE +// vtr::printf_info("At end of initial_placement.\n"); +// if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_INITIAL_CLB_PLACEMENT)) { +// print_clb_placement(getEchoFileName(E_ECHO_INITIAL_CLB_PLACEMENT)); +// } +//#endif +// free(free_locations); + + assign_budget(npnr_ctx); +} + +//static void free_fast_cost_update() { +// auto& device_ctx = g_vpr_ctx.device(); +// +// for (size_t i = 0; i < device_ctx.grid.height(); i++) { +// free(chanx_place_cost_fac[i]); +// } +// free(chanx_place_cost_fac); +// chanx_place_cost_fac = nullptr; +// +// for (size_t i = 0; i < device_ctx.grid.width(); i++) { +// free(chany_place_cost_fac[i]); +// } +// free(chany_place_cost_fac); +// chany_place_cost_fac = nullptr; +//} +// +//static void alloc_and_load_for_fast_cost_update(float place_cost_exp) { +// +// /* Allocates and loads the chanx_place_cost_fac and chany_place_cost_fac * +// * arrays with the inverse of the average number of tracks per channel * +// * between [subhigh] and [sublow]. This is only useful for the cost * +// * function that takes the length of the net bounding box in each * +// * dimension divided by the average number of tracks in that direction. * +// * For other cost functions, you don't have to bother calling this * +// * routine; when using the cost function described above, however, you * +// * must always call this routine after you call init_chan and before * +// * you do any placement cost determination. The place_cost_exp factor * +// * specifies to what power the width of the channel should be taken -- * +// * larger numbers make narrower channels more expensive. */ +// +// auto& device_ctx = g_vpr_ctx.device(); +// +// /* Access arrays below as chan?_place_cost_fac[subhigh][sublow]. Since * +// * subhigh must be greater than or equal to sublow, we only need to * +// * allocate storage for the lower half of a matrix. */ +// +// chanx_place_cost_fac = (float **) vtr::malloc((device_ctx.grid.height()) * sizeof(float *)); +// for (size_t i = 0; i < device_ctx.grid.height(); i++) +// chanx_place_cost_fac[i] = (float *) vtr::malloc((i + 1) * sizeof(float)); +// +// chany_place_cost_fac = (float **) vtr::malloc((device_ctx.grid.width() + 1) * sizeof(float *)); +// for (size_t i = 0; i < device_ctx.grid.width(); i++) +// chany_place_cost_fac[i] = (float *) vtr::malloc((i + 1) * sizeof(float)); +// +// /* First compute the number of tracks between channel high and channel * +// * low, inclusive, in an efficient manner. */ +// +// chanx_place_cost_fac[0][0] = device_ctx.chan_width.x_list[0]; +// +// for (size_t high = 1; high < device_ctx.grid.height(); high++) { +// chanx_place_cost_fac[high][high] = device_ctx.chan_width.x_list[high]; +// for (size_t low = 0; low < high; low++) { +// chanx_place_cost_fac[high][low] = +// chanx_place_cost_fac[high - 1][low] + device_ctx.chan_width.x_list[high]; +// } +// } +// +// /* Now compute the inverse of the average number of tracks per channel * +// * between high and low. The cost function divides by the average * +// * number of tracks per channel, so by storing the inverse I convert * +// * this to a faster multiplication. Take this final number to the * +// * place_cost_exp power -- numbers other than one mean this is no * +// * longer a simple "average number of tracks"; it is some power of * +// * that, allowing greater penalization of narrow channels. */ +// +// for (size_t high = 0; high < device_ctx.grid.height(); high++) +// for (size_t low = 0; low <= high; low++) { +// chanx_place_cost_fac[high][low] = (high - low + 1.) +// / chanx_place_cost_fac[high][low]; +// chanx_place_cost_fac[high][low] = pow( +// (double) chanx_place_cost_fac[high][low], +// (double) place_cost_exp); +// } +// +// /* Now do the same thing for the y-directed channels. First get the * +// * number of tracks between channel high and channel low, inclusive. */ +// +// chany_place_cost_fac[0][0] = device_ctx.chan_width.y_list[0]; +// +// for (size_t high = 1; high < device_ctx.grid.width(); high++) { +// chany_place_cost_fac[high][high] = device_ctx.chan_width.y_list[high]; +// for (size_t low = 0; low < high; low++) { +// chany_place_cost_fac[high][low] = +// chany_place_cost_fac[high - 1][low] + device_ctx.chan_width.y_list[high]; +// } +// } +// +// /* Now compute the inverse of the average number of tracks per channel * +// * between high and low. Take to specified power. */ +// +// for (size_t high = 0; high < device_ctx.grid.width(); high++) +// for (size_t low = 0; low <= high; low++) { +// chany_place_cost_fac[high][low] = (high - low + 1.) +// / chany_place_cost_fac[high][low]; +// chany_place_cost_fac[high][low] = pow( +// (double) chany_place_cost_fac[high][low], +// (double) place_cost_exp); +// } +//} + +static void check_place(float bb_cost, float timing_cost, + enum e_place_algorithm place_algorithm, + float delay_cost) { + +// /* Checks that the placement has not confused our data structures. * +// * i.e. the clb and block structures agree about the locations of * +// * every block, blocks are in legal spots, etc. Also recomputes * +// * the final placement cost from scratch and makes sure it is * +// * within roundoff of what we think the cost is. */ +// +// vtr::vector bdone; + int error = 0; + /*ClusterBlockId*/ CellInfo* /*bnum,*/ head_iblk, *member_iblk; + float bb_cost_check; +// int usage_check; + float timing_cost_check, delay_cost_check; + int imacro, imember, member_x, member_y, member_z; + + bb_cost_check = comp_bb_cost(CHECK); + //vtr::printf_info("bb_cost recomputed from scratch: %g\n", bb_cost_check); + if (fabs(bb_cost_check - bb_cost) > bb_cost * ERROR_TOL) { + vtr::printf_error(__FILE__, __LINE__, + "bb_cost_check: %g and bb_cost: %g differ in check_place.\n", + bb_cost_check, bb_cost); + error++; + } + + if (place_algorithm == PATH_TIMING_DRIVEN_PLACE) { + comp_td_costs(&timing_cost_check, &delay_cost_check); + //vtr::printf_info("timing_cost recomputed from scratch: %g\n", timing_cost_check); + if (fabs(timing_cost_check - timing_cost) > timing_cost * ERROR_TOL) { + vtr::printf_error(__FILE__, __LINE__, + "timing_cost_check: %g and timing_cost: %g differ in check_place.\n", + timing_cost_check, timing_cost); + error++; + } + //vtr::printf_info("delay_cost recomputed from scratch: %g\n", delay_cost_check); + if (fabs(delay_cost_check - delay_cost) > delay_cost * ERROR_TOL) { + vtr::printf_error(__FILE__, __LINE__, + "delay_cost_check: %g and delay_cost: %g differ in check_place.\n", + delay_cost_check, delay_cost); + error++; + } + } + +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.placement(); +// auto& device_ctx = g_vpr_ctx.device(); +// +// bdone.resize(cluster_ctx.clb_nlist.blocks().size(), 0); +// +// /* Step through device grid and placement. Check it against blocks */ +// for (size_t i = 0; i < device_ctx.grid.width(); i++) +// for (size_t j = 0; j < device_ctx.grid.height(); j++) { +// if (place_ctx.grid_blocks[i][j].usage > device_ctx.grid[i][j].type->capacity) { +// vtr::printf_error(__FILE__, __LINE__, +// "Block at grid location (%zu,%zu) overused. Usage is %d.\n", +// i, j, place_ctx.grid_blocks[i][j].usage); +// error++; +// } +// usage_check = 0; +// for (int k = 0; k < device_ctx.grid[i][j].type->capacity; k++) { +// bnum = place_ctx.grid_blocks[i][j].blocks[k]; +// if (EMPTY_BLOCK_ID == bnum || INVALID_BLOCK_ID == bnum) +// continue; +// +// if (cluster_ctx.clb_nlist.block_type(bnum) != device_ctx.grid[i][j].type) { +// vtr::printf_error(__FILE__, __LINE__, +// "Block %zu type (%s) does not match grid location (%zu,%zu) type (%s).\n", +// size_t(bnum), cluster_ctx.clb_nlist.block_type(bnum)->name, i, j, device_ctx.grid[i][j].type->name); +// error++; +// } +// if ((place_ctx.block_locs[bnum].x != int(i)) || (place_ctx.block_locs[bnum].y != int(j))) { +// vtr::printf_error(__FILE__, __LINE__, +// "Block %zu location conflicts with grid(%zu,%zu) data.\n", +// size_t(bnum), i, j); +// error++; +// } +// ++usage_check; +// bdone[bnum]++; +// } +// if (usage_check != place_ctx.grid_blocks[i][j].usage) { +// vtr::printf_error(__FILE__, __LINE__, +// "Location (%zu,%zu) usage is %d, but has actual usage %d.\n", +// i, j, place_ctx.grid_blocks[i][j].usage, usage_check); +// error++; +// } +// } +// +// /* Check that every block exists in the device_ctx.grid and cluster_ctx.blocks arrays somewhere. */ +// for (auto blk_id : cluster_ctx.clb_nlist.blocks()) +// if (bdone[blk_id] != 1) { +// vtr::printf_error(__FILE__, __LINE__, +// "Block %zu listed %d times in data structures.\n", +// size_t(blk_id), bdone[blk_id]); +// error++; +// } +// bdone.clear(); + + /* Check the pl_macro placement are legal - blocks are in the proper relative position. */ + for (imacro = 0; imacro < int(pl_macros.size()); imacro++) { + + head_iblk = pl_macros[imacro].members[0].blk_index; + + for (imember = 0; imember < int(pl_macros[imacro].members.size()); imember++) { + + member_iblk = pl_macros[imacro].members[imember].blk_index; + + // Compute the suppossed member's x,y,z location + auto head_loc = npnr_ctx->getBelLocation(head_iblk->bel); + member_x = head_loc.x + pl_macros[imacro].members[imember].x_offset; + member_y = head_loc.y + pl_macros[imacro].members[imember].y_offset; + member_z = head_loc.z + pl_macros[imacro].members[imember].z_offset; + + // Check the place_ctx.block_locs data structure first + auto member_loc = npnr_ctx->getBelLocation(member_iblk->bel); + if (member_loc.x != member_x + || member_loc.y != member_y + || member_loc.z != member_z) { + vtr::printf_error(__FILE__, __LINE__, + "Block %zu in pl_macro #%d is not placed in the proper orientation.\n", + size_t(member_iblk), imacro); + error++; + } + +// // Then check the place_ctx.grid data structure +// if (place_ctx.grid_blocks[member_x][member_y].blocks[member_z] != member_iblk) { +// vtr::printf_error(__FILE__, __LINE__, +// "Block %zu in pl_macro #%d is not placed in the proper orientation.\n", +// size_t(member_iblk), imacro); +// error++; +// } + } // Finish going through all the members + } // Finish going through all the macros + + if (error == 0) { + vtr::printf_info("\n"); + vtr::printf_info("Completed placement consistency check successfully.\n"); + + } else { + vpr_throw(VPR_ERROR_PLACE, __FILE__, __LINE__, + "\nCompleted placement consistency check, %d errors found.\n" + "Aborting program.\n", error); + } + +} + +//#ifdef VERBOSE +//static void print_clb_placement(const char *fname) { +// +// /* Prints out the clb placements to a file. */ +// FILE *fp; +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// auto& place_ctx = g_vpr_ctx.placement(); +// +// fp = vtr::fopen(fname, "w"); +// fprintf(fp, "Complex block placements:\n\n"); +// +// fprintf(fp, "Block #\tName\t(X, Y, Z).\n"); +// for(auto i : cluster_ctx.clb_nlist.blocks()) { +// fprintf(fp, "#%d\t%s\t(%d, %d, %d).\n", i, cluster_ctx.clb_nlist.block_name(i), place_ctx.block_locs[i].x, place_ctx.block_locs[i].y, place_ctx.block_locs[i].z); +// } +// +// fclose(fp); +//} +//#endif +// +//static void free_try_swap_arrays() { +// if(blocks_affected.moved_blocks != nullptr) { +// free(blocks_affected.moved_blocks); +// +// blocks_affected.moved_blocks = nullptr; +// blocks_affected.num_moved_blocks = 0; +// } +//} diff --git a/vpr/place/place_macro.cpp b/vpr/place/place_macro.cpp new file mode 100644 index 0000000000..e5ce95d44e --- /dev/null +++ b/vpr/place/place_macro.cpp @@ -0,0 +1,418 @@ +//#include +//#include +//#include +//#include +//#include +//using namespace std; + +//#include "vtr_assert.h" +//#include "vtr_memory.h" +//#include "vtr_util.h" +// +//#include "vpr_types.h" +//#include "vpr_error.h" +//#include "physical_types.h" +//#include "globals.h" +//#include "place.h" +//#include "read_xml_arch_file.h" +#include "place_macro.h" +//#include "vpr_utils.h" +//#include "echo_files.h" + + +///******************** File-scope variables declarations **********************/ +// +///* f_idirect_from_blk_pin array allow us to quickly find pins that could be in a * +// * direct connection. Values stored is the index of the possible direct connection * +// * as specified in the arch file, OPEN (-1) is stored for pins that could not be * +// * part of a direct chain conneciton. * +// * [0...device_ctx.num_block_types-1][0...num_pins-1] */ +//static int ** f_idirect_from_blk_pin = nullptr; +// +///* f_direct_type_from_blk_pin array stores the value SOURCE if the pin is the * +// * from_pin, SINK if the pin is the to_pin in the direct connection as specified in * +// * the arch file, OPEN (-1) is stored for pins that could not be part of a direct * +// * chain conneciton. * +// * [0...device_ctx.num_block_types-1][0...num_pins-1] */ +//static int ** f_direct_type_from_blk_pin = nullptr; + +/* f_imacro_from_blk_pin maps a blk_num to the corresponding macro index. * + * If the block is not part of a macro, the value OPEN (-1) is stored. * + * [0...cluster_ctx.clb_nlist.blocks().size()-1] */ +static /*vtr::vector_map*/ std::vector f_imacro_from_iblk; + + +///******************** Subroutine declarations ********************************/ +// +//static void find_all_the_macro (int * num_of_macro, std::vector &pl_macro_member_blk_num_of_this_blk, +// std::vector &pl_macro_idirect, std::vector &pl_macro_num_members, std::vector> &pl_macro_member_blk_num); + +static void alloc_and_load_imacro_from_iblk(/*t_pl_macro **/ std::vector ¯os /*, int num_macros*/); + +//static void write_place_macros(std::string filename, const t_pl_macro* macros, int num_macros); +// +//static bool is_constant_clb_net(ClusterNetId clb_net); +// +//static bool net_is_driven_by_direct(ClusterNetId clb_net); +// +//static void validate_macros(t_pl_macro* macros, int num_macro); +///******************** Subroutine definitions *********************************/ +// +// +//static void find_all_the_macro (int * num_of_macro, std::vector &pl_macro_member_blk_num_of_this_blk, +// std::vector &pl_macro_idirect, std::vector &pl_macro_num_members, std::vector> &pl_macro_member_blk_num) { +// +// /* Compute required size: * +// * Go through all the pins with possible direct connections in * +// * f_idirect_from_blk_pin. Count the number of heads (which is the same * +// * as the number macros) and also the length of each macro * +// * Head - blocks with to_pin OPEN and from_pin connected * +// * Tail - blocks with to_pin connected and from_pin OPEN */ +// +// int from_iblk_pin, to_iblk_pin, from_idirect, to_idirect, +// from_src_or_sink, to_src_or_sink; +// ClusterNetId to_net_id, from_net_id, next_net_id, curr_net_id; +// ClusterBlockId next_blk_id; +// int num_blk_pins, num_macro; +// int imember; +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// +// num_macro = 0; +// for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { +// num_blk_pins = cluster_ctx.clb_nlist.block_type(blk_id)->num_pins; +// for (to_iblk_pin = 0; to_iblk_pin < num_blk_pins; to_iblk_pin++) { +// +// to_net_id = cluster_ctx.clb_nlist.block_net(blk_id, to_iblk_pin); +// to_idirect = f_idirect_from_blk_pin[cluster_ctx.clb_nlist.block_type(blk_id)->index][to_iblk_pin]; +// to_src_or_sink = f_direct_type_from_blk_pin[cluster_ctx.clb_nlist.block_type(blk_id)->index][to_iblk_pin]; +// +// // Identify potential macro head blocks (i.e. start of a macro) +// // +// // The input SINK (to_pin) of a potential HEAD macro will have either: +// // * no connection to any net (OPEN), or +// // * a connection to a constant net (e.g. gnd/vcc) which is not driven by a direct +// // +// // Note that the restriction that constant nets are not driven from another direct ensures that +// // blocks in the middle of a chain with internal constant signals are not detected has potential +// // head blocks. +// if (to_src_or_sink == SINK && to_idirect != OPEN +// && (to_net_id == ClusterNetId::INVALID() +// || (is_constant_clb_net(to_net_id) +// && !net_is_driven_by_direct(to_net_id)))) { +// +// for (from_iblk_pin = 0; from_iblk_pin < num_blk_pins; from_iblk_pin++) { +// from_net_id = cluster_ctx.clb_nlist.block_net(blk_id, from_iblk_pin; +// from_idirect = f_idirect_from_blk_pin[cluster_ctx.clb_nlist.block_type(blk_id)->index][from_iblk_pin]; +// from_src_or_sink = f_direct_type_from_blk_pin[cluster_ctx.clb_nlist.block_type(blk_id)->index][from_iblk_pin]; +// +// // Confirm whether this is a head macro +// // +// // The output SOURCE (from_pin) of a true head macro will: +// // * drive another block with the same direct connection +// if (from_src_or_sink == SOURCE && to_idirect == from_idirect && from_net_id != ClusterNetId::INVALID()) { +// +// // Mark down that this is the first block in the macro +// pl_macro_member_blk_num_of_this_blk[0] = blk_id; +// pl_macro_idirect[num_macro] = to_idirect; +// +// // Increment the num_member count. +// pl_macro_num_members[num_macro]++; +// +// // Also find out how many members are in the macros, +// // there are at least 2 members - 1 head and 1 tail. +// +// // Initialize the variables +// next_net_id = from_net_id; +// next_blk_id = blk_id; +// +// // Start finding the other members +// while (next_net_id != ClusterNetId::INVALID()) { +// curr_net_id = next_net_id; +// +// // Assume that carry chains only has 1 sink - direct connection +// VTR_ASSERT(cluster_ctx.clb_nlist.net_sinks(curr_net_id).size() == 1); +// next_blk_id = cluster_ctx.clb_nlist.net_pin_block(curr_net_id, 1); +// +// // Assume that the from_iblk_pin index is the same for the next block +// VTR_ASSERT(f_idirect_from_blk_pin[cluster_ctx.clb_nlist.block_type(next_blk_id)->index][from_iblk_pin] == from_idirect +// && f_direct_type_from_blk_pin[cluster_ctx.clb_nlist.block_type(next_blk_id)->index][from_iblk_pin] == SOURCE); +// next_net_id = cluster_ctx.clb_nlist.block_net(next_blk_id, from_iblk_pin); +// +// // Mark down this block as a member of the macro +// imember = pl_macro_num_members[num_macro]; +// pl_macro_member_blk_num_of_this_blk[imember] = next_blk_id; +// +// // Increment the num_member count. +// pl_macro_num_members[num_macro]++; +// +// } // Found all the members of this macro at this point +// +// // Allocate the second dimension of the blk_num array since I now know the size +// pl_macro_member_blk_num[num_macro].resize(pl_macro_num_members[num_macro]); +// // Copy the data from the temporary array to the newly allocated array. +// for (imember = 0; imember < pl_macro_num_members[num_macro]; imember++) +// pl_macro_member_blk_num[num_macro][imember] = pl_macro_member_blk_num_of_this_blk[imember]; +// +// // Increment the macro count +// num_macro ++; +// +// } // Do nothing if the from_pins does not have same possible direct connection. +// } // Finish going through all the pins for from_pins. +// } // Do nothing if the to_pins does not have same possible direct connection. +// } // Finish going through all the pins for to_pins. +// } // Finish going through all blocks. +// +// // Now, all the data is readily stored in the temporary data structures. +// *num_of_macro = num_macro; +//} + + +int alloc_and_load_placement_macros(/*t_direct_inf* directs, int num_segments,*/ /*t_pl_macro ***/ std::vector ¯os) { +// +// /* This function allocates and loads the macros placement macros * +// * and returns the total number of macros in 2 steps. * +// * 1) Allocate temporary data structure for maximum possible * +// * size and loops through all the blocks storing the data * +// * relevant to the carry chains. At the same time, also count * +// * the amount of memory required for the actual variables. * +// * 2) Allocate the actual variables with the exact amount of * +// * memory. Then loads the data from the temporary data * +// * structures before freeing them. * +// * * +// * For pl_macro_member_blk_num, allocate for the first dimension * +// * only at first. Allocate for the second dimemsion when I know * +// * the size. Otherwise, the array is going to be of size * +// * cluster_ctx.clb_nlist.blocks().size()^2 (There are big * +// * benckmarks VPR that have cluster_ctx.clb_nlist.blocks().size() * +// * in the 100k's range). * +// * * +// * The placement macro array is freed by the caller(s). */ + + /* Declaration of local variables */ + int imacro, imember /*, num_macro*/; + auto& cluster_ctx = g_vpr_ctx.clustering(); + +// /* Allocate maximum memory for temporary variables. */ +// std::vector pl_macro_idirect(cluster_ctx.clb_nlist.blocks().size()); +// std::vector pl_macro_num_members(cluster_ctx.clb_nlist.blocks().size()); +// std::vector> pl_macro_member_blk_num(cluster_ctx.clb_nlist.blocks().size()); +// std::vector pl_macro_member_blk_num_of_this_blk(cluster_ctx.clb_nlist.blocks().size()); +// +// t_pl_macro * macro = nullptr; +// +// /* Sets up the required variables. */ +// alloc_and_load_idirect_from_blk_pin(directs, num_directs, +// &f_idirect_from_blk_pin, &f_direct_type_from_blk_pin); +// +// +// /* Compute required size: * +// * Go through all the pins with possible direct connections in * +// * f_idirect_from_blk_pin. Count the number of heads (which is the same * +// * as the number macros) and also the length of each macro * +// * Head - blocks with to_pin OPEN and from_pin connected * +// * Tail - blocks with to_pin connected and from_pin OPEN */ +// num_macro = 0; +// find_all_the_macro (&num_macro, pl_macro_member_blk_num_of_this_blk, +// pl_macro_idirect, pl_macro_num_members, pl_macro_member_blk_num); +// +// /* Allocate the memories for the macro. */ +// macro = (t_pl_macro *) vtr::malloc (num_macro * sizeof(t_pl_macro)); + + /* Allocate the memories for the chaim members. * + * Load the values from the temporary data structures. */ + for (auto &b : cluster_ctx.clb_nlist.blocks()) { + auto head = b.second.get(); + const auto& children = head->constr_children; + if (children.empty()) continue; + imacro = macros.size(); + + macros.emplace_back(); + macros[imacro].members.resize(children.size()+1); + macros[imacro].members[0].x_offset = 0; + macros[imacro].members[0].y_offset = 0; + macros[imacro].members[0].z_offset = 0; + macros[imacro].members[0].blk_index = head; + + /* Load the values for each member of the macro */ + for (imember = 1; imember <= int(children.size()); imember++) { + macros[imacro].members[imember].x_offset = children[imember-1]->constr_x; + macros[imacro].members[imember].y_offset = children[imember-1]->constr_y; + macros[imacro].members[imember].z_offset = children[imember-1]->constr_z; + macros[imacro].members[imember].blk_index = children[imember-1]; + } + } + +// /* Returns the pointer to the macro by reference. */ +// *macros = macro; +// +// if(isEchoFileEnabled(E_ECHO_PLACE_MACROS)) { +// write_place_macros(getEchoFileName(E_ECHO_PLACE_MACROS), *macros, num_macro); +// } +// +// validate_macros(*macros, num_macro); + + return macros.size(); +} + +void get_imacro_from_iblk(int *imacro, ClusterBlockId iblk, std::vector ¯os /*, int num_macros*/) { + + /* This mapping is needed for fast lookup's whether the block with index * + * iblk belongs to a placement macro or not. * + * * + * The array f_imacro_from_iblk is used for the mapping for speed reason * + * [0...cluster_ctx.clb_nlist.blocks().size()-1] */ + + /* If the array is not allocated and loaded, allocate it. */ + if (f_imacro_from_iblk.size() == 0) { + alloc_and_load_imacro_from_iblk(macros /*, num_macros*/); + } + + /* Return the imacro for the block. */ + *imacro = f_imacro_from_iblk[iblk]; + +} + +/* Allocates and loads imacro_from_iblk array. */ +static void alloc_and_load_imacro_from_iblk(/*t_pl_macro **/ std::vector ¯os /*, int num_macros*/) { + int imacro, imember; + auto& cluster_ctx = g_vpr_ctx.clustering(); + + f_imacro_from_iblk.resize(cluster_ctx.clb_nlist.blocks().size(), OPEN); + + /* Allocate and initialize the values to OPEN (-1). */ + //for (const auto& b : cluster_ctx.clb_nlist.blocks()) { + // auto blk_id = b.second.get(); + // f_imacro_from_iblk.insert(blk_id, OPEN); + //} + + /* Load the values */ + for (imacro = 0; imacro < int(macros.size()); imacro++) { + for (imember = 0; imember < int(macros[imacro].members.size()); imember++) { + /*ClusterBlockId*/ auto blk_id = macros[imacro].members[imember].blk_index; + f_imacro_from_iblk[blk_id->udata] = imacro; + } + } +} + +//void free_placement_macros_structs() { +// +// /* This function frees up all the static data structures used. */ +// +// // This frees up the two arrays and set the pointers to NULL +// auto& device_ctx = g_vpr_ctx.device(); +// int itype; +// if ( f_idirect_from_blk_pin != nullptr ) { +// for (itype = 1; itype < device_ctx.num_block_types; itype++) { +// free(f_idirect_from_blk_pin[itype]); +// } +// free(f_idirect_from_blk_pin); +// f_idirect_from_blk_pin = nullptr; +// } +// +// if ( f_direct_type_from_blk_pin != nullptr ) { +// for (itype = 1; itype < device_ctx.num_block_types; itype++) { +// free(f_direct_type_from_blk_pin[itype]); +// } +// free(f_direct_type_from_blk_pin); +// f_direct_type_from_blk_pin = nullptr; +// } +//} +// +//static void write_place_macros(std::string filename, const t_pl_macro *macros, int num_macros) { +// +// FILE* f = vtr::fopen(filename.c_str(), "w"); +// +// fprintf(f, "#Identified Placement macros\n"); +// fprintf(f, "Num_Macros: %d\n", num_macros); +// for (int imacro = 0; imacro < num_macros; ++imacro) { +// const t_pl_macro* macro = ¯os[imacro]; +// fprintf(f, "Macro_Id: %d, Num_Blocks: %d\n", imacro, macro->num_blocks); +// fprintf(f, "------------------------------------------------------\n"); +// for (int imember = 0; imember < macro->num_blocks; ++imember) { +// const t_pl_macro_member* macro_memb = ¯o->members[imember]; +// fprintf(f, "Block_Id: %zu, x_offset: %d, y_offset: %d, z_offset: %d\n", +// size_t(macro_memb->blk_index), +// macro_memb->x_offset, +// macro_memb->y_offset, +// macro_memb->z_offset); +// } +// fprintf(f, "\n"); +// } +// +// fprintf(f, "\n"); +// +// fprintf(f, "#Macro-related direct connections\n"); +// fprintf(f, "type type_pin is_direct direct_type\n"); +// fprintf(f, "------------------------------------------\n"); +// auto& device_ctx = g_vpr_ctx.device(); +// for (int itype = 0; itype < device_ctx.num_block_types; ++itype) { +// t_type_descriptor* type = &device_ctx.block_types[itype]; +// +// for (int ipin = 0; ipin < type->num_pins; ++ipin) { +// if (f_idirect_from_blk_pin[itype][ipin] != OPEN) { +// if (f_direct_type_from_blk_pin[itype][ipin] == SOURCE) { +// fprintf(f, "%-9s %-9d true SOURCE \n", type->name, ipin); +// } else { +// VTR_ASSERT(f_direct_type_from_blk_pin[itype][ipin] == SINK); +// fprintf(f, "%-9s %-9d true SINK \n", type->name, ipin); +// } +// } else { +// VTR_ASSERT(f_direct_type_from_blk_pin[itype][ipin] == OPEN); +// } +// } +// +// } +// +// fclose(f); +//} +// +//static bool is_constant_clb_net(ClusterNetId clb_net) { +// auto& atom_ctx = g_vpr_ctx.atom(); +// AtomNetId atom_net = atom_ctx.lookup.atom_net(clb_net); +// +// return atom_ctx.nlist.net_is_constant(atom_net); +//} +// +//static bool net_is_driven_by_direct(ClusterNetId clb_net) { +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// +// ClusterBlockId block_id = cluster_ctx.clb_nlist.net_driver_block(clb_net); +// int pin_index = cluster_ctx.clb_nlist.net_pin_physical_index(clb_net, 0); +// +// auto direct = f_idirect_from_blk_pin[cluster_ctx.clb_nlist.block_type(block_id)->index][pin_index]; +// +// return direct != OPEN; +//} +// +//static void validate_macros(t_pl_macro* macros, int num_macros) { +// //Perform sanity checks on macros +// auto& cluster_ctx = g_vpr_ctx.clustering(); +// +// //Verify that blocks only appear in a single macro +// std::multimap block_to_macro; +// for (int imacro = 0; imacro < num_macros; ++imacro) { +// for (int imember = 0; imember < macros[imacro].num_blocks; ++imember) { +// ClusterBlockId iblk = macros[imacro].members[imember].blk_index; +// +// block_to_macro.emplace(iblk, imacro); +// } +// } +// +// for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { +// auto range = block_to_macro.equal_range(blk_id); +// +// int blk_macro_cnt = std::distance(range.first, range.second); +// if (blk_macro_cnt > 1) { +// std::stringstream msg; +// msg << "Block #" << size_t(blk_id) << " '" << cluster_ctx.clb_nlist.block_name(blk_id) << "'" +// << " appears in " << blk_macro_cnt << " placement macros (should appear in at most one). Related Macros:\n"; +// +// for (auto iter = range.first; iter != range.second; ++iter) { +// int imacro = iter->second; +// msg << " Macro #: " << imacro << "\n"; +// } +// +// VPR_THROW(VPR_ERROR_PLACE, msg.str().c_str()); +// } +// } +//} diff --git a/vpr/place/place_macro.h b/vpr/place/place_macro.h new file mode 100644 index 0000000000..d5f4e9a96c --- /dev/null +++ b/vpr/place/place_macro.h @@ -0,0 +1,168 @@ +/**************************************************************************************** + Y.G.THIEN + 29 AUG 2012 + + This file contains functions related to placement macros. The term "placement macros" + refers to a structure that contains information on blocks that need special treatment + during placement and possibly routing. + + An example of placement macros is a carry chain. Blocks in a carry chain have to be + placed in a specific orientation or relative placement so that the carry_in's and the + carry_out's are properly aligned. With that, the carry chains would be able to use the + direct connections specified in the arch file. Direct connections with the pin's + fc_value 0 would be treated specially in routing where the whole carry chain would be + treated as a unit and regular routing would not be used to connect the carry_in's and + carry_out's. Floorplanning constraints may also be an example of placement macros. + + The function alloc_and_load_placement_macros allocates and loads the placement + macros in the following steps: + (1) First, go through all the block types and mark down the pins that could possibly + be part of a placement macros. + (2) Then, go through the netlist of all the pins marked in (1) to find out all the + heads of the placement macros using criteria depending on the type of placement + macros. For carry chains, the heads of the placement macros are blocks with + carry_in's not connected to any nets (OPEN) while the carry_out's connected to the + netlist with only 1 SINK. + (3) Traverse from the heads to the tails of the placement macros and load the + information in the t_pl_macro data structure. Similar to (2), tails are identified + with criteria depending on the type of placement macros. For carry chains, the + tails are blocks with carry_out's not connected to any nets (OPEN) while the + carry_in's is connected to the netlist which has only 1 SINK. + + The only placement macros supported at the moment are the carry chains with limited + functionality. + + Current support for placement macros are: + (1) The arch parser for direct connections is working. The specifications of the direct + connections are specified in sample_adder_arch.xml and also in the + VPR_User_Manual.doc + (2) The placement macros allocator and loader is working. + (3) The initial placement of placement macros that respects the restrictions of the + placement macros is working. + (4) The post-placement legality check for placement macros is working. + + Current limitations on placement macros are: + (1) One block could only be a part of a carry chain. In the future, if a block is part + of multiple placement macros, we should load 1 huge placement macro instead of + multiple placement macros that contain the same block. + (2) Bus direct connections (direct connections with multiple bits) are supported. + However, a 2-bit carry chain when loaded would become 2 1-bit carry chains. + And because of (1), only 1 1-bit carry chain would be loaded. In the future, + placement macros with multiple-bit connections or multiple 1-bit connections + should be allowed. + (3) Placement macros that span longer or wider than the chip would cause an error. + In the future, we *might* expand the size of the chip to accommodate such + placement macros that are crucial. + + In order for the carry chain support to work, two changes are required in the + arch file. + (1) For carry chain support, added in a new child in called . + specifies a list of available direct connections on the FPGA chip + that are necessary for direct carry chain connections. These direct connections + would be treated specially in routing if the fc_value for the pins is specified + as 0. Note that only direct connections that has fc_value 0 could be used as a + carry chain. + + A may have 0 or more children called . For each , + there are the following fields: + 1) name: This specifies the name given to this particular direct connection. + 2) from_pin: This specifies the SOURCEs for this direct connection. The format + could be as following: + a) type_name.port_name, for all the pins in this port. + b) type_name.port_name [end_pin_index:start_pin_index], for a + single pin, the end_pin_index and start_pin_index could be + the same. + 3) to_pin: This specifies the SINKs for this direct connection. The format is + the same as from_pin. + Note that the width of the from_pin and to_pin has to match. + 4) x_offset: This specifies the x direction that this connection is going from + SOURCEs to SINKs. + 5) y_offset: This specifies the y direction that this connection is going from + SOURCEs to SINKs. + Note that the x_offset and y_offset could not both be 0. + 6) z_offset: This specifies the z sublocations that all the blocks in this + direct connection to be at. + + The example of a direct connection specification below shows a possible carry chain + connection going north on the FPGA chip: + _______________________________________________________________________________ + | | + | | + | | + |_______________________________________________________________________________| + A corresponding arch file that has this direct connection is sample_adder_arch.xml + A corresponding blif file that uses this direct connection is adder.blif + + (2) As mentioned in (1), carry chain connections using the directs would only be + recognized if the pin's fc_value is 0. In order to achieve this, pin-based fc_value + is required. Hence, the new tag replaces both and tags. + + A tag may have 0 or more children called . For each , there are the + following fields: + 1) in_type: This specifies the default fc_type for input pins. They could + be "frac", "abs" or "full". + 2) in_val: This specifies the default fc_value for input pins. + 3) out_type: This specifies the default fc_type for output pins. They could + be "frac", "abs" or "full". + 4) out_val: This specifies the default fc_value for output pins. + + As for the children, there are the following fields: + 1) name: This specifies the name of the port/pin that the fc_type and fc_value + apply to. The name have to be in the format "port_name" or + "port_name [end_pin_index:start_pin_index]" where port_name is the name + of the port it apply to while end_pin_index and start_pin_index could + be specified to apply the fc_type and fc_value that follows to part of + a bus (multi-pin) port. + 2) fc_type: This specifies the fc_type that would be applied to the specified pins. + 3) fc_val: This specifies the fc_value that would be applied to the specified pins. + + The example of a pin-based fc_value specification below shows that the fc_values for + the cout and the cin ports are 0: + _______________________________________________________________________________ + | | + | | + | | + | | + |_______________________________________________________________________________| + A corresponding arch file that has this direct connection is sample_adder_arch.xml + A corresponding blif file that uses this direct connection is adder.blif + +****************************************************************************************/ + + +#ifndef PLACE_MACRO_H +#define PLACE_MACRO_H + +/* These are the placement macro structure. + * It is in the form of array of structs instead of + * structs of arrays for cache efficiency. + * Could have more data members for other macro type. + * blk_index: The cluster_ctx.blocks index of this block. + * x_offset: The x_offset of the previous block to this cluster_ctx.blocks. + * y_offset: The y_offset of the previous block to this cluster_ctx.blocks. + */ +struct t_pl_macro_member{ + /*ClusterBlockId*/ CellInfo *blk_index; + int x_offset; + int y_offset; + int z_offset; +}; + +/* num_blocks: The number of blocks this macro contains. + * members: An array of blocks in this macro [0:num_macro-1]. + * idirect: The direct index as specified in the arch file + */ +struct t_pl_macro { + //int num_blocks; + /*t_pl_macro_member**/ std::vector members; +}; + + +/* These are the function declarations. */ +int alloc_and_load_placement_macros(/*t_direct_inf* directs, int num_segments,*/ /*t_pl_macro ***/ std::vector &chains); +void get_imacro_from_iblk(int *imacro, ClusterBlockId iblk, /*t_pl_macro **/ std::vector ¯os /*, int num_macros*/); +//void free_placement_macros_structs(); + +#endif diff --git a/vpr/place/timing_place.cpp b/vpr/place/timing_place.cpp new file mode 100644 index 0000000000..dbe5d8b3f0 --- /dev/null +++ b/vpr/place/timing_place.cpp @@ -0,0 +1,113 @@ +//#include +//#include +//using namespace std; +// +//#include "vtr_util.h" +//#include "vtr_memory.h" +//#include "vtr_log.h" +// +//#include "vpr_types.h" +//#include "vpr_utils.h" +//#include "globals.h" +//#include "path_delay.h" +//#include "path_delay2.h" +//#include "net_delay.h" +//#include "timing_place_lookup.h" +//#include "timing_place.h" +// +//#include "timing_info.h" + +static /*vtr*/std::vector f_timing_place_crit; /* [0..cluster_ctx.clb_nlist.nets().size()-1][1..num_pins-1] */ + +//static vtr::t_chunk f_timing_place_crit_ch; + +/******** prototypes ******************/ +static void alloc_crit(/*vtr::t_chunk *chunk_list_ptr*/); + +static void free_crit(/*vtr::t_chunk *chunk_list_ptr*/); + +/**************************************/ + +/* Allocates space for the f_timing_place_crit data structure * +* I chunk the data to save space on large problems. */ +static void alloc_crit(/*vtr::t_chunk *chunk_list_ptr*/) { + auto& cluster_ctx = g_vpr_ctx.clustering(); + float *tmp_ptr; + + f_timing_place_crit.resize(cluster_ctx.clb_nlist.nets().size()); + + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + //tmp_ptr = (float *) vtr::chunk_malloc( + // (cluster_ctx.clb_nlist.net_sinks(net_id).size()) * sizeof(float), chunk_list_ptr); + tmp_ptr = (float *) malloc((cluster_ctx.clb_nlist.net_sinks(net_id).size()) * sizeof(float)); + f_timing_place_crit[net_id->udata] = tmp_ptr - 1; /* [1..num_sinks] */ + } +} + +///**************************************/ +static void free_crit(/*vtr::t_chunk *chunk_list_ptr*/){ +// vtr::free_chunk_memory(chunk_list_ptr); + for (auto i : f_timing_place_crit) + free(i+1); +} + +/**************************************/ +void load_criticalities(SetupTimingInfo& timing_info, float crit_exponent /*, const ClusteredPinAtomPinsLookup& pin_lookup*/) { + /* Performs a 1-to-1 mapping from criticality to f_timing_place_crit. + For every pin on every net (or, equivalently, for every tedge ending + in that pin), f_timing_place_crit = criticality^(criticality exponent) */ + + auto& cluster_ctx = g_vpr_ctx.clustering(); + for (const auto& net : cluster_ctx.clb_nlist.nets()) { + auto net_id = net.second.get(); + if (cluster_ctx.clb_nlist.net_is_global(net_id)) + continue; + + int ipin = 1; + for (const auto& clb_pin : cluster_ctx.clb_nlist.net_sinks(net_id)) { + //int ipin = cluster_ctx.clb_nlist.pin_net_index(clb_pin); + + float clb_pin_crit = calculate_clb_net_pin_criticality(timing_info, /*pin_lookup,*/ clb_pin, net_id); + + /* The placer likes a great deal of contrast between criticalities. + Since path criticality varies much more than timing, we "sharpen" timing + criticality by taking it to some power, crit_exponent (between 1 and 8 by default). */ + f_timing_place_crit[net_id->udata][ipin] = pow(clb_pin_crit, crit_exponent); + + ++ipin; + } + } +} + + +float get_timing_place_crit(/*ClusterNetId*/ NetInfo* net_id, int ipin) { + return f_timing_place_crit[net_id->udata][ipin]; +} + +void set_timing_place_crit(/*ClusterNetId*/ NetInfo* net_id, int ipin, float val) { + f_timing_place_crit[net_id->udata][ipin] = val; +} + +/**************************************/ +void alloc_lookups_and_criticalities(/*t_chan_width_dist chan_width_dist, + t_router_opts router_opts, + t_det_routing_arch *det_routing_arch, t_segment_inf * segment_inf, + const t_direct_inf *directs, + const int num_directs*/) { + +// compute_delay_lookup_tables(router_opts, det_routing_arch, segment_inf, +// chan_width_dist, directs, num_directs); + + alloc_crit(/*&f_timing_place_crit_ch*/); +} + +/**************************************/ +void free_lookups_and_criticalities() { +// //TODO: May need to free f_timing_place_crit ? + free_crit(/*&f_timing_place_crit_ch*/); + +// free_place_lookup_structs(); +} + +/**************************************/