From 86f8af1c1b7ebdbdd43975d25a0f17404777c415 Mon Sep 17 00:00:00 2001 From: AlexandreSinger Date: Thu, 26 Sep 2024 10:04:42 -0400 Subject: [PATCH] [AP] Added Ability to Read The Atom Netlist This method reads the atom netlist and uses the results of the prepacker to generate an APNetlist. It also fixes the block locations based on the user place constraints. --- .../analytical_placement_flow.cpp | 10 + vpr/src/analytical_place/ap_netlist.cpp | 14 +- vpr/src/analytical_place/ap_netlist.h | 11 +- .../gen_ap_netlist_from_atoms.cpp | 175 ++++++++++++++++++ .../gen_ap_netlist_from_atoms.h | 30 +++ 5 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 vpr/src/analytical_place/gen_ap_netlist_from_atoms.cpp create mode 100644 vpr/src/analytical_place/gen_ap_netlist_from_atoms.h diff --git a/vpr/src/analytical_place/analytical_placement_flow.cpp b/vpr/src/analytical_place/analytical_placement_flow.cpp index f4d3872e1d6..7df79bf16e6 100644 --- a/vpr/src/analytical_place/analytical_placement_flow.cpp +++ b/vpr/src/analytical_place/analytical_placement_flow.cpp @@ -6,9 +6,12 @@ */ #include "analytical_placement_flow.h" +#include "ap_netlist.h" #include "atom_netlist.h" +#include "gen_ap_netlist_from_atoms.h" #include "globals.h" #include "prepack.h" +#include "user_place_constraints.h" #include "vpr_context.h" #include "vpr_error.h" #include "vpr_types.h" @@ -22,11 +25,18 @@ void run_analytical_placement_flow(t_vpr_setup& vpr_setup) { // The global state used/modified by this flow. const AtomNetlist& atom_nlist = g_vpr_ctx.atom().nlist; const DeviceContext& device_ctx = g_vpr_ctx.device(); + const UserPlaceConstraints& constraints = g_vpr_ctx.floorplanning().constraints; // Run the prepacker Prepacker prepacker; prepacker.init(atom_nlist, device_ctx.logical_block_types); + // Create the ap netlist from the atom netlist using the result from the + // prepacker. + APNetlist ap_netlist = gen_ap_netlist_from_atoms(atom_nlist, + prepacker, + constraints); + // AP is currently under-construction. Fail gracefully just in case this // is somehow being called. VPR_FATAL_ERROR(VPR_ERROR_AP, diff --git a/vpr/src/analytical_place/ap_netlist.cpp b/vpr/src/analytical_place/ap_netlist.cpp index 98b1026038e..6ae59e596b4 100644 --- a/vpr/src/analytical_place/ap_netlist.cpp +++ b/vpr/src/analytical_place/ap_netlist.cpp @@ -58,10 +58,20 @@ APBlockId APNetlist::create_block(const std::string& name, const t_pack_molecule void APNetlist::set_block_loc(const APBlockId id, const APFixedBlockLoc& loc) { VTR_ASSERT_SAFE(valid_block_id(id)); - // Check that the location is fixed, if all values are -1 then it is not fixed. - if (loc.x == -1 && loc.y == -1 && loc.sub_tile == -1 && loc.layer_num == -1) + // Check that the location is fixed; if all dims are unfixed then it is not fixed. + if (loc.x == APFixedBlockLoc::UNFIXED_DIM && + loc.y == APFixedBlockLoc::UNFIXED_DIM && + loc.sub_tile == APFixedBlockLoc::UNFIXED_DIM && + loc.layer_num == APFixedBlockLoc::UNFIXED_DIM) return; + // Ensure that the block is fixed to a single position on the grid (x, y, layer). + // sub-tile is allowed to be unfixed. + VTR_ASSERT(loc.x != APFixedBlockLoc::UNFIXED_DIM && + loc.y != APFixedBlockLoc::UNFIXED_DIM && + loc.layer_num != APFixedBlockLoc::UNFIXED_DIM && + "AP: Currently, AP assumes block is locked down to a single position on the device grid."); + block_locs_[id] = loc; block_mobilities_[id] = APBlockMobility::FIXED; } diff --git a/vpr/src/analytical_place/ap_netlist.h b/vpr/src/analytical_place/ap_netlist.h index 570543df490..ec64ae38a9b 100644 --- a/vpr/src/analytical_place/ap_netlist.h +++ b/vpr/src/analytical_place/ap_netlist.h @@ -36,10 +36,13 @@ class t_pack_molecule; * -1 implies that the block is not fixed in that dimension. */ struct APFixedBlockLoc { - int x = -1; - int y = -1; - int layer_num = -1; - int sub_tile = -1; + // Value that represents an unfixed dimension. + static constexpr int UNFIXED_DIM = -1; + // The dimensions to fix. + int x = UNFIXED_DIM; + int y = UNFIXED_DIM; + int layer_num = UNFIXED_DIM; + int sub_tile = UNFIXED_DIM; }; /** diff --git a/vpr/src/analytical_place/gen_ap_netlist_from_atoms.cpp b/vpr/src/analytical_place/gen_ap_netlist_from_atoms.cpp new file mode 100644 index 00000000000..34207e88145 --- /dev/null +++ b/vpr/src/analytical_place/gen_ap_netlist_from_atoms.cpp @@ -0,0 +1,175 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief Definition of the gen_ap_netlist_from_atoms method, used for + * generating an APNetlist from the results of the Prepacker. + */ + +#include "gen_ap_netlist_from_atoms.h" +#include "ap_netlist.h" +#include "atom_netlist.h" +#include "atom_netlist_fwd.h" +#include "netlist_fwd.h" +#include "partition.h" +#include "partition_region.h" +#include "prepack.h" +#include "region.h" +#include "user_place_constraints.h" +#include "vpr_types.h" +#include "vtr_assert.h" +#include "vtr_geometry.h" +#include "vtr_time.h" +#include +#include + +APNetlist gen_ap_netlist_from_atoms(const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + const UserPlaceConstraints& constraints) { + // Create a scoped timer for reading the atom netlist. + vtr::ScopedStartFinishTimer timer("Read Atom Netlist to AP Netlist"); + + // FIXME: What to do about the name and ID in this context? For now just + // using empty strings. + APNetlist ap_netlist; + + // Add the APBlocks based on the atom block molecules. This essentially + // creates supernodes. + // Each AP block has the name of the first atom block in the molecule. + // Each port is named "_" + // Each net has the exact same name as in the atom netlist + for (AtomBlockId atom_blk_id : atom_netlist.blocks()) { + // Get the molecule of this block + t_pack_molecule* mol = prepacker.get_atom_molecule(atom_blk_id); + // Create the AP block (if not already done) + const std::string& first_blk_name = atom_netlist.block_name(mol->atom_block_ids[0]); + APBlockId ap_blk_id = ap_netlist.create_block(first_blk_name, mol); + // Add the ports and pins of this block to the supernode + for (AtomPortId atom_port_id : atom_netlist.block_ports(atom_blk_id)) { + BitIndex port_width = atom_netlist.port_width(atom_port_id); + PortType port_type = atom_netlist.port_type(atom_port_id); + const std::string& port_name = atom_netlist.port_name(atom_port_id); + const std::string& block_name = atom_netlist.block_name(atom_blk_id); + // The port name needs to be made unique for the supernode (two + // joined blocks may have the same port name) + std::string ap_port_name = block_name + "_" + port_name; + APPortId ap_port_id = ap_netlist.create_port(ap_blk_id, ap_port_name, port_width, port_type); + for (AtomPinId atom_pin_id : atom_netlist.port_pins(atom_port_id)) { + BitIndex port_bit = atom_netlist.pin_port_bit(atom_pin_id); + PinType pin_type = atom_netlist.pin_type(atom_pin_id); + bool pin_is_const = atom_netlist.pin_is_constant(atom_pin_id); + AtomNetId pin_atom_net_id = atom_netlist.pin_net(atom_pin_id); + const std::string& pin_atom_net_name = atom_netlist.net_name(pin_atom_net_id); + APNetId pin_ap_net_id = ap_netlist.create_net(pin_atom_net_name); + ap_netlist.create_pin(ap_port_id, port_bit, pin_ap_net_id, pin_type, pin_is_const); + } + } + } + + // Fix the block locations given by the VPR constraints + for (APBlockId ap_blk_id : ap_netlist.blocks()) { + const t_pack_molecule* mol = ap_netlist.block_molecule(ap_blk_id); + for (AtomBlockId mol_atom_blk_id : mol->atom_block_ids) { + PartitionId part_id = constraints.get_atom_partition(mol_atom_blk_id); + if (!part_id.is_valid()) + continue; + // We should not fix a block twice. This would imply that a molecule + // contains two fixed blocks. This would only make sense if the blocks + // were fixed to the same location. I am not sure if that is even + // possible. + VTR_ASSERT(ap_netlist.block_mobility(ap_blk_id) == APBlockMobility::MOVEABLE); + // Get the partition region. + const PartitionRegion& partition_pr = constraints.get_partition_pr(part_id); + // TODO: Either handle the union of legal locations or turn into a + // proper error. + VTR_ASSERT(partition_pr.get_regions().size() == 1 && "AP: Each partition should contain only one region for AP right now."); + const Region& region = partition_pr.get_regions()[0]; + // Get the x and y. + const vtr::Rect& region_rect = region.get_rect(); + VTR_ASSERT(region_rect.xmin() == region_rect.xmax() && "AP: Expect each region to be a single point in x!"); + VTR_ASSERT(region_rect.ymin() == region_rect.ymax() && "AP: Expect each region to be a single point in y!"); + int blk_x_loc = region_rect.xmin(); + int blk_y_loc = region_rect.ymin(); + // Get the layer. + VTR_ASSERT(region.get_layer_range().first == region.get_layer_range().second && "AP: Expect each region to be a single point in layer!"); + int blk_layer_num = region.get_layer_range().first; + // Get the sub_tile (if fixed). + int blk_sub_tile = APFixedBlockLoc::UNFIXED_DIM; + if (region.get_sub_tile() != NO_SUBTILE) + blk_sub_tile = region.get_sub_tile(); + // Set the fixed block location. + APFixedBlockLoc loc = {blk_x_loc, blk_y_loc, blk_layer_num, blk_sub_tile}; + ap_netlist.set_block_loc(ap_blk_id, loc); + } + } + + // Cleanup the netlist by removing undesirable nets. + // Currently undesirable nets are nets that are: + // - ignored for placement + // - a global net + // - connected to 1 or fewer unique blocks + // - connected to only fixed blocks + // TODO: Allow the user to pass a threshold so we can remove high-fanout nets. + auto remove_net = [&](APNetId net_id) { + // Remove all pins associated with this net + for (APPinId pin_id : ap_netlist.net_pins(net_id)) + ap_netlist.remove_pin(pin_id); + // Remove the net + ap_netlist.remove_net(net_id); + }; + for (APNetId ap_net_id : ap_netlist.nets()) { + // Is the net ignored for placement, if so remove + const std::string& net_name = ap_netlist.net_name(ap_net_id); + AtomNetId atom_net_id = atom_netlist.find_net(net_name); + VTR_ASSERT(atom_net_id.is_valid()); + if (atom_netlist.net_is_ignored(atom_net_id)) { + remove_net(ap_net_id); + continue; + } + // Is the net global, if so remove + if (atom_netlist.net_is_global(atom_net_id)) { + remove_net(ap_net_id); + continue; + } + // Get the unique blocks connectioned to this net + std::unordered_set net_blocks; + for (APPinId ap_pin_id : ap_netlist.net_pins(ap_net_id)) { + net_blocks.insert(ap_netlist.pin_block(ap_pin_id)); + } + // If connected to 1 or fewer unique blocks, remove + if (net_blocks.size() <= 1) { + remove_net(ap_net_id); + continue; + } + // If all the connected blocks are fixed, remove + bool is_all_fixed = true; + for (APBlockId ap_blk_id : net_blocks) { + if (ap_netlist.block_mobility(ap_blk_id) == APBlockMobility::MOVEABLE) { + is_all_fixed = false; + break; + } + } + if (is_all_fixed) { + remove_net(ap_net_id); + continue; + } + } + ap_netlist.remove_and_compress(); + + // TODO: Should we cleanup the blocks? For example if there is no path + // from a fixed block to a given moveable block, then that moveable + // block can be removed (since it can literally go anywhere). + // - This would be useful to detect and use throughout; but may cause some + // issues if we just remove them. When and where will they eventually + // be placed? + // - Perhaps we can add a flag in the netlist to each of these blocks and + // during the solving stage we can ignore them. + // For now, leave this alone; but should check if the matrix becomes + // ill-formed and causes problems. + + // Verify that the netlist was created correctly. + VTR_ASSERT(ap_netlist.verify()); + + return ap_netlist; +} + diff --git a/vpr/src/analytical_place/gen_ap_netlist_from_atoms.h b/vpr/src/analytical_place/gen_ap_netlist_from_atoms.h new file mode 100644 index 00000000000..f03055a826d --- /dev/null +++ b/vpr/src/analytical_place/gen_ap_netlist_from_atoms.h @@ -0,0 +1,30 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief Declaration of the gen_ap_netlist_from_atoms function which uses the + * results of the prepacker to generate an APNetlist. + */ + +#pragma once + +// Forward declarations +class APNetlist; +class AtomNetlist; +class Prepacker; +class UserPlaceConstraints; + +/** + * @brief Use the results from prepacking the atom netlist to generate an APNetlist. + * + * @param atom_netlist The atom netlist for the input design. + * @param prepacker The prepacker, initialized on the provided atom netlist. + * @param constraints The placement constraints on the Atom blocks, provided + * by the user. + * + * @return An APNetlist object, generated from the prepacker results. + */ +APNetlist gen_ap_netlist_from_atoms(const AtomNetlist& atom_netlist, + const Prepacker& prepacker, + const UserPlaceConstraints& constraints); +