diff --git a/vpr/src/base/CheckSetup.cpp b/vpr/src/base/CheckSetup.cpp index 6b980612417..18d44534e98 100644 --- a/vpr/src/base/CheckSetup.cpp +++ b/vpr/src/base/CheckSetup.cpp @@ -23,6 +23,18 @@ void CheckSetup(const t_packer_opts& PackerOpts, "Packing cannot be timing driven without timing analysis enabled\n"); } + if (PackerOpts.load_flat_placement) { + if (PackerOpts.device_layout == "auto") { + VPR_FATAL_ERROR(VPR_ERROR_OTHER, + "Legalization requires a fixed device layout.\n"); + } + if (!PlacerOpts.constraints_file.empty()) { + VPR_FATAL_ERROR(VPR_ERROR_OTHER, + "Cannot specify a fixed clusters file when running legalization.\n"); + } + } + + if ((GLOBAL == RouterOpts.route_type) && (PlacerOpts.place_algorithm.is_timing_driven())) { /* Works, but very weird. Can't optimize timing well, since you're diff --git a/vpr/src/base/SetupGrid.h b/vpr/src/base/SetupGrid.h index 977ce2f51e2..da9b2631c94 100644 --- a/vpr/src/base/SetupGrid.h +++ b/vpr/src/base/SetupGrid.h @@ -13,10 +13,11 @@ #include "physical_types.h" ///@brief Find the device satisfying the specified minimum resources +/// minimum_instance_counts and target_device_utilization are not required when specifying a fixed layout DeviceGrid create_device_grid(const std::string& layout_name, const std::vector& grid_layouts, - const std::map& minimum_instance_counts, - float target_device_utilization); + const std::map& minimum_instance_counts = {}, + float target_device_utilization = 0.0); ///@brief Find the device close in size to the specified dimensions DeviceGrid create_device_grid(const std::string& layout_name, diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index af73c8816a1..d05f7d5d7da 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -128,6 +128,7 @@ void SetupVPR(const t_options* Options, FileNameOpts->ArchFile = Options->ArchFile; FileNameOpts->CircuitFile = Options->CircuitFile; FileNameOpts->NetFile = Options->NetFile; + FileNameOpts->FlatPlaceFile = Options->FlatPlaceFile; FileNameOpts->PlaceFile = Options->PlaceFile; FileNameOpts->RouteFile = Options->RouteFile; FileNameOpts->ActFile = Options->ActFile; @@ -136,6 +137,8 @@ void SetupVPR(const t_options* Options, FileNameOpts->out_file_prefix = Options->out_file_prefix; FileNameOpts->read_vpr_constraints_file = Options->read_vpr_constraints_file; FileNameOpts->write_vpr_constraints_file = Options->write_vpr_constraints_file; + FileNameOpts->write_constraints_file = Options->write_constraints_file; + FileNameOpts->write_flat_place_file = Options->write_flat_place_file; FileNameOpts->write_block_usage = Options->write_block_usage; FileNameOpts->verify_file_digests = Options->verify_file_digests; @@ -239,6 +242,7 @@ void SetupVPR(const t_options* Options, //Setup the default flow, if no specific stages specified //do all if (!Options->do_packing + && !Options->do_legalize && !Options->do_placement && !Options->do_routing && !Options->do_analysis) { @@ -275,6 +279,11 @@ void SetupVPR(const t_options* Options, if (Options->do_packing) { PackerOpts->doPacking = STAGE_DO; } + + if (Options->do_legalize) { + PackerOpts->doPacking = STAGE_LOAD; + PackerOpts->load_flat_placement = true; + } } ShowSetup(*vpr_setup); diff --git a/vpr/src/base/echo_files.cpp b/vpr/src/base/echo_files.cpp index 9627ba84f96..ae606aa7453 100644 --- a/vpr/src/base/echo_files.cpp +++ b/vpr/src/base/echo_files.cpp @@ -80,6 +80,9 @@ void alloc_and_load_echo_file_info() { //Packing setEchoFileName(E_ECHO_CLUSTERS, "clusters.echo"); + //Legalizer + setEchoFileName(E_ECHO_FLAT_PLACE, "post_legalizer_flat_placement.echo"); + //Intra-block routing setEchoFileName(E_ECHO_INTRA_LB_FAILED_ROUTE, "intra_lb_failed_route.echo"); diff --git a/vpr/src/base/echo_files.h b/vpr/src/base/echo_files.h index ea02d58bc92..0ebd371fa04 100644 --- a/vpr/src/base/echo_files.h +++ b/vpr/src/base/echo_files.h @@ -16,6 +16,9 @@ enum e_echo_files { //Packing E_ECHO_CLUSTERS, + //Legalizer + E_ECHO_FLAT_PLACE, + // Intra-block routing E_ECHO_INTRA_LB_FAILED_ROUTE, diff --git a/vpr/src/base/load_flat_place.cpp b/vpr/src/base/load_flat_place.cpp new file mode 100644 index 00000000000..01b2978a310 --- /dev/null +++ b/vpr/src/base/load_flat_place.cpp @@ -0,0 +1,55 @@ +#include "globals.h" +#include "load_flat_place.h" +#include "clustered_netlist_utils.h" + + +/* @brief Prints flat placement file entries for the atoms in one placed cluster. */ +static void print_flat_cluster(FILE* fp, ClusterBlockId iblk, + std::vector& atoms); + +static void print_flat_cluster(FILE* fp, ClusterBlockId iblk, + std::vector& atoms) { + + auto& atom_ctx = g_vpr_ctx.atom(); + t_pl_loc loc = g_vpr_ctx.placement().block_locs[iblk].loc; + size_t bnum = size_t(iblk); + + for (auto atom : atoms) { + t_pb_graph_node* atom_pbgn = atom_ctx.lookup.atom_pb(atom)->pb_graph_node; + fprintf(fp, "%s %d %d %d %d #%zu %s\n", atom_ctx.nlist.block_name(atom).c_str(), + loc.x, loc.y, loc.sub_tile, + atom_pbgn->flat_site_index, + bnum, + atom_pbgn->pb_type->name); + } +} + +/* prints a flat placement file */ +void print_flat_placement(const char* flat_place_file) { + + FILE* fp; + + ClusterAtomsLookup atoms_lookup; + auto& cluster_ctx = g_vpr_ctx.clustering(); + + if (!g_vpr_ctx.placement().block_locs.empty()) { + fp = fopen(flat_place_file, "w"); + for (auto iblk : cluster_ctx.clb_nlist.blocks()) { + auto atoms = atoms_lookup.atoms_in_cluster(iblk); + print_flat_cluster(fp, iblk, atoms); + } + fclose(fp); + } + +} + +/* ingests and legalizes a flat placement file */ +bool load_flat_placement(t_vpr_setup& vpr_setup, const t_arch& arch) { + VTR_LOG("load_flat_placement(); when implemented, this function:"); + VTR_LOG("\n\tLoads flat placement file: %s, ", vpr_setup.FileNameOpts.FlatPlaceFile.c_str()); + VTR_LOG("\n\tArch id: %s, ", arch.architecture_id); + VTR_LOG("\n\tPrints clustered netlist file: %s, ", vpr_setup.FileNameOpts.NetFile.c_str()); + VTR_LOG("\n\tPrints fix clusters file: %s\n", vpr_setup.FileNameOpts.write_constraints_file.c_str()); + + return false; +} diff --git a/vpr/src/base/load_flat_place.h b/vpr/src/base/load_flat_place.h new file mode 100644 index 00000000000..17a52715b79 --- /dev/null +++ b/vpr/src/base/load_flat_place.h @@ -0,0 +1,16 @@ +#ifndef LOAD_FLAT_PLACE_H +#define LOAD_FLAT_PLACE_H + +#include "vpr_types.h" + +/** + * @brief A function that prints a flat placement file + */ +void print_flat_placement(const char* flat_place_file); + +/** + * @brief A function that loads and legalizes a flat placement file + */ +bool load_flat_placement(t_vpr_setup& vpr_setup, const t_arch& arch); + +#endif diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index b4f33cf8025..454caae26cb 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1310,6 +1310,11 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .action(argparse::Action::STORE_TRUE) .default_value("off"); + stage_grp.add_argument(args.do_legalize, "--legalize") + .help("Legalize a flat placement, i.e. reconstruct and place clusters based on a flat placement file, which lists cluster and intra-cluster placement coordinates for each primitive.") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + stage_grp.add_argument(args.do_placement, "--place") .help("Run placement") .action(argparse::Action::STORE_TRUE) @@ -1590,6 +1595,10 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .help("Path to packed netlist file") .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.FlatPlaceFile, "--flat_place_file") + .help("Path to input flat placement file") + .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.PlaceFile, "--place_file") .help("Path to placement file") .show_in(argparse::ShowIn::HELP_ONLY); @@ -1627,6 +1636,17 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .help("Writes out new floorplanning constraints based on current placement to the specified XML file.") .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.write_constraints_file, "--write_fix_clusters") + .help( + "Output file containing fixed locations of legalized input clusters - does not include clusters without placement coordinates; this file is used during post-legalization placement in order to hold input placement coordinates fixed while VPR places legalizer-generated orphan clusters.") + .default_value("fix_clusters.out") + .show_in(argparse::ShowIn::HELP_ONLY); + + file_grp.add_argument(args.write_flat_place_file, "--write_flat_place") + .help( + "VPR's (or reconstructed external) placement solution in flat placement file format; this file lists cluster and intra-cluster placement coordinates for each atom and can be used to reconstruct a clustering and placement solution.") + .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.read_router_lookahead, "--read_router_lookahead") .help( "Reads the lookahead data from the specified file instead of computing it.") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 7de7f2535b2..a2dcc49d244 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -12,6 +12,7 @@ struct t_options { argparse::ArgValue ArchFile; argparse::ArgValue CircuitName; argparse::ArgValue NetFile; + argparse::ArgValue FlatPlaceFile; argparse::ArgValue PlaceFile; argparse::ArgValue RouteFile; argparse::ArgValue CircuitFile; @@ -30,6 +31,8 @@ struct t_options { argparse::ArgValue write_initial_place_file; argparse::ArgValue read_vpr_constraints_file; argparse::ArgValue write_vpr_constraints_file; + argparse::ArgValue write_constraints_file; + argparse::ArgValue write_flat_place_file; argparse::ArgValue write_placement_delay_lookup; argparse::ArgValue read_placement_delay_lookup; @@ -44,6 +47,7 @@ struct t_options { /* Stage Options */ argparse::ArgValue do_packing; + argparse::ArgValue do_legalize; argparse::ArgValue do_placement; argparse::ArgValue do_routing; argparse::ArgValue do_analysis; diff --git a/vpr/src/base/read_place.cpp b/vpr/src/base/read_place.cpp index 2fc99696484..130ec5469e7 100644 --- a/vpr/src/base/read_place.cpp +++ b/vpr/src/base/read_place.cpp @@ -309,7 +309,8 @@ void read_place_body(std::ifstream& placement_file, */ void print_place(const char* net_file, const char* net_id, - const char* place_file) { + const char* place_file, + bool is_place_file) { FILE* fp; auto& device_ctx = g_vpr_ctx.device(); @@ -318,15 +319,21 @@ void print_place(const char* net_file, fp = fopen(place_file, "w"); - fprintf(fp, "Netlist_File: %s Netlist_ID: %s\n", - net_file, - net_id); - fprintf(fp, "Array size: %zu x %zu logic blocks\n\n", device_ctx.grid.width(), device_ctx.grid.height()); - fprintf(fp, "#block name\tx\ty\tsubblk\tlayer\tblock number\n"); - fprintf(fp, "#----------\t--\t--\t------\t-----\t------------\n"); + if (is_place_file) { + fprintf(fp, "Netlist_File: %s Netlist_ID: %s\n", + net_file, + net_id); + fprintf(fp, "Array size: %zu x %zu logic blocks\n\n", device_ctx.grid.width(), device_ctx.grid.height()); + fprintf(fp, "#block name\tx\ty\tsubblk\tlayer\tblock number\n"); + fprintf(fp, "#----------\t--\t--\t------\t-----\t------------\n"); + } if (!place_ctx.block_locs.empty()) { //Only if placement exists for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { + // if block is not placed, skip (useful for printing legalizer output) + if (!is_place_file && (place_ctx.block_locs[blk_id].loc.x == INVALID_X)) { + continue; + } fprintf(fp, "%s\t", cluster_ctx.clb_nlist.block_pb(blk_id)->name); if (strlen(cluster_ctx.clb_nlist.block_pb(blk_id)->name) < 8) fprintf(fp, "\t"); diff --git a/vpr/src/base/read_place.h b/vpr/src/base/read_place.h index 36740a5dc5d..4dfd9aef82e 100644 --- a/vpr/src/base/read_place.h +++ b/vpr/src/base/read_place.h @@ -17,8 +17,19 @@ void read_place( */ void read_constraints(const char* constraints_file); +/** + * This function prints out a place file. + * @param is_place_file: defaults to true. If false, does not print file header; this is useful if + * the output will be used as a constraints file. If is_place_file is false, + * net_file and net_id parameters are not used and can be set to nullptr. + * Note: if false, only placed clusters are printed - clusters without + * placement coordinates (e.g. orphan clusters created during legalization + * will not be included; this file is used as a placement constraints + * file when running placement in order to place orphan clusters. + */ void print_place(const char* net_file, const char* net_id, - const char* place_file); + const char* place_file, + bool is_place_file = true); #endif diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 2fa98ab8b50..1294cc80836 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -96,6 +96,8 @@ #include "log.h" #include "iostream" +#include "load_flat_place.h" + #ifdef VPR_USE_TBB # define TBB_PREVIEW_GLOBAL_CONTROL 1 /* Needed for compatibility with old TBB versions */ # include @@ -393,6 +395,7 @@ bool vpr_flow(t_vpr_setup& vpr_setup, t_arch& arch) { return false; //Unimplementable } } + // For the time being, we decided to create the flat graph after placement is done. Thus, the is_flat parameter for this function //, since it is called before routing, should be false. vpr_create_device(vpr_setup, arch, false); @@ -595,6 +598,9 @@ bool vpr_pack_flow(t_vpr_setup& vpr_setup, const t_arch& arch) { if (packer_opts.doPacking == STAGE_DO) { //Do the actual packing status = vpr_pack(vpr_setup, arch); + if (!status) { + return status; + } //TODO: to be consistent with placement/routing vpr_pack should really // load the netlist data structures itself, instead of re-loading @@ -604,10 +610,25 @@ bool vpr_pack_flow(t_vpr_setup& vpr_setup, const t_arch& arch) { vpr_load_packing(vpr_setup, arch); } else { VTR_ASSERT(packer_opts.doPacking == STAGE_LOAD); - //Load a previous packing from the .net file - vpr_load_packing(vpr_setup, arch); - //Load cluster_constraints data structure here since loading pack file - load_cluster_constraints(); + + // generate a .net file by legalizing an input flat placement file + if (packer_opts.load_flat_placement) { + + //Load and legalizer flat placement file + vpr_load_flat_placement(vpr_setup, arch); + + //Load the result from the .net file + vpr_load_packing(vpr_setup, arch); + + } else { + + //Load a previous packing from the .net file + vpr_load_packing(vpr_setup, arch); + + //Load cluster_constraints data structure here since loading pack file + load_cluster_constraints(); + } + } /* Sanity check the resulting netlist */ @@ -709,6 +730,37 @@ void vpr_load_packing(t_vpr_setup& vpr_setup, const t_arch& arch) { } } +bool vpr_load_flat_placement(t_vpr_setup& vpr_setup, const t_arch& arch) { + + // set up the device grid for the legalizer + auto& device_ctx = g_vpr_ctx.mutable_device(); + device_ctx.arch = &arch; + device_ctx.grid = create_device_grid(vpr_setup.device_layout, arch.grid_layouts); + if (device_ctx.grid.get_num_layers() > 1) { + VPR_FATAL_ERROR(VPR_ERROR_PACK, "Legalizer currently only supports single layer devices.\n"); + } + + // load and legalize flat placement file, print .net and fix clusters files + bool status = load_flat_placement(vpr_setup, arch); + if (!status) { + return status; + } + + // echo flat placement (orphan clusters will have -1 for X, Y, subtile coordinates) + if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_FLAT_PLACE)) { + print_flat_placement(getEchoFileName(E_ECHO_FLAT_PLACE)); + } + + // reset the device grid + device_ctx.grid.clear(); + + // if running placement, use the fix clusters file produced by the legalizer + if (vpr_setup.PlacerOpts.doPlacement) { + vpr_setup.PlacerOpts.constraints_file = vpr_setup.FileNameOpts.write_constraints_file; + } + return true; +} + bool vpr_place_flow(const Netlist<>& net_list, t_vpr_setup& vpr_setup, const t_arch& arch) { VTR_LOG("\n"); const auto& placer_opts = vpr_setup.PlacerOpts; @@ -737,6 +789,13 @@ bool vpr_place_flow(const Netlist<>& net_list, t_vpr_setup& vpr_setup, const t_a placer_opts.floorplan_num_horizontal_partitions, placer_opts.floorplan_num_vertical_partitions); } + // Write out a flat placement file if the option is specified + // A flat placement file includes cluster and intra-cluster placement coordinates for + // each primitive and can be used to reconstruct a clustering and placement solution. + if (!filename_opts.write_flat_place_file.empty()) { + print_flat_placement(vpr_setup.FileNameOpts.write_flat_place_file.c_str()); + } + return true; } diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 89a2a204752..5efbceaecfa 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -65,6 +65,9 @@ bool vpr_pack(t_vpr_setup& vpr_setup, const t_arch& arch); ///@brief Loads a previous packing void vpr_load_packing(t_vpr_setup& vpr_setup, const t_arch& arch); +///@brief Reconstructs a packing and placement solution from a flat placement file +bool vpr_load_flat_placement(t_vpr_setup& vpr_setup, const t_arch& arch); + /* Placement */ ///@brief Perform, load or skip the placement stage diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 7e231136aad..2f22bc2d1d2 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -715,6 +715,9 @@ struct hash { }; } // namespace std +/// @brief Sentinel value for indicating that a block does not have a valid x location, used to check whether a block has been placed +static constexpr int INVALID_X = -1; + /** * @brief A placement location coordinate * @@ -903,6 +906,7 @@ struct t_file_name_opts { std::string CircuitName; std::string CircuitFile; std::string NetFile; + std::string FlatPlaceFile; std::string PlaceFile; std::string RouteFile; std::string FPGAInterchangePhysicalFile; @@ -912,6 +916,8 @@ struct t_file_name_opts { std::string out_file_prefix; std::string read_vpr_constraints_file; std::string write_vpr_constraints_file; + std::string write_constraints_file; + std::string write_flat_place_file; std::string write_block_usage; bool verify_file_digests; }; @@ -976,6 +982,7 @@ struct t_packer_opts { bool use_attraction_groups; int pack_num_moves; std::string pack_move_type; + bool load_flat_placement; }; /** diff --git a/vpr/src/place/initial_placement.cpp b/vpr/src/place/initial_placement.cpp index 50d65da2aa9..109dd16013c 100644 --- a/vpr/src/place/initial_placement.cpp +++ b/vpr/src/place/initial_placement.cpp @@ -12,7 +12,7 @@ #include "move_utils.h" #include "region.h" #include "directed_moves_util.h" - +#include "vpr_types.h" #include "echo_files.h" #include @@ -23,9 +23,6 @@ void print_clb_placement(const char* fname); #endif -/// @brief Sentinel value for indicating that a block does not have a valid x location, used to check whether a block has been placed -static constexpr int INVALID_X = -1; - // Number of iterations that initial placement tries to place all blocks before throwing an error static constexpr int MAX_INIT_PLACE_ATTEMPTS = 2;