diff --git a/vpr/src/analytical_place/ap_netlist.cpp b/vpr/src/analytical_place/ap_netlist.cpp new file mode 100644 index 00000000000..98b1026038e --- /dev/null +++ b/vpr/src/analytical_place/ap_netlist.cpp @@ -0,0 +1,200 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief The definitions of the APNetlist methods + */ + +#include "ap_netlist.h" +#include +#include "netlist_fwd.h" +#include "netlist_utils.h" +#include "vpr_types.h" +#include "vtr_assert.h" + +/* + * Blocks + */ +const t_pack_molecule* APNetlist::block_molecule(const APBlockId id) const { + VTR_ASSERT_SAFE(valid_block_id(id)); + + return block_molecules_[id]; +} + +APBlockMobility APNetlist::block_mobility(const APBlockId id) const { + VTR_ASSERT_SAFE(valid_block_id(id)); + + return block_mobilities_[id]; +} + +const APFixedBlockLoc& APNetlist::block_loc(const APBlockId id) const { + VTR_ASSERT_SAFE(valid_block_id(id)); + VTR_ASSERT(block_mobility(id) == APBlockMobility::FIXED); + + return block_locs_[id]; +} + +/* + * Mutators + */ +APBlockId APNetlist::create_block(const std::string& name, const t_pack_molecule* mol) { + APBlockId blk_id = Netlist::create_block(name); + + // Initialize the data + block_molecules_.insert(blk_id, mol); + block_mobilities_.insert(blk_id, APBlockMobility::MOVEABLE); + block_locs_.insert(blk_id, APFixedBlockLoc()); + + // Check post-conditions: size + VTR_ASSERT(validate_block_sizes()); + + // Check post-conditions: values + VTR_ASSERT(block_molecule(blk_id) == mol); + VTR_ASSERT(block_mobility(blk_id) == APBlockMobility::MOVEABLE); + + return blk_id; +} + +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) + return; + + block_locs_[id] = loc; + block_mobilities_[id] = APBlockMobility::FIXED; +} + +APPortId APNetlist::create_port(const APBlockId blk_id, const std::string& name, BitIndex width, PortType type) { + APPortId port_id = find_port(blk_id, name); + if (!port_id) { + port_id = Netlist::create_port(blk_id, name, width, type); + associate_port_with_block(port_id, type, blk_id); + } + + // Check post-conditions: size + VTR_ASSERT(validate_port_sizes()); + + // Check post-conditions: values + VTR_ASSERT(port_name(port_id) == name); + VTR_ASSERT(find_port(blk_id, name) == port_id); + + return port_id; +} + +APPinId APNetlist::create_pin(const APPortId port_id, BitIndex port_bit, const APNetId net_id, const PinType pin_type_, bool is_const) { + APPinId pin_id = Netlist::create_pin(port_id, port_bit, net_id, pin_type_, is_const); + + // Check post-conditions: size + VTR_ASSERT(validate_pin_sizes()); + + // Check post-conditions: values + VTR_ASSERT(pin_type(pin_id) == pin_type_); + VTR_ASSERT(pin_port(pin_id) == port_id); + VTR_ASSERT(pin_port_type(pin_id) == port_type(port_id)); + + return pin_id; +} + +APNetId APNetlist::create_net(const std::string& name) { + APNetId net_id = Netlist::create_net(name); + + // Check post-conditions: size + VTR_ASSERT(validate_net_sizes()); + + return net_id; +} + +/* + * Internal utilities + */ +void APNetlist::clean_blocks_impl(const vtr::vector_map& block_id_map) { + // Update all the block molecules + block_molecules_ = clean_and_reorder_values(block_molecules_, block_id_map); + // Update all the block mobilities + block_mobilities_ = clean_and_reorder_values(block_mobilities_, block_id_map); + // Update the fixed block locations + block_locs_ = clean_and_reorder_values(block_locs_, block_id_map); +} + +void APNetlist::clean_ports_impl(const vtr::vector_map& /*port_id_map*/) { + // Unused +} + +void APNetlist::clean_pins_impl(const vtr::vector_map& /*pin_id_map*/) { + // Unused +} + +void APNetlist::clean_nets_impl(const vtr::vector_map& /*net_id_map*/) { + // Unused +} + +void APNetlist::rebuild_block_refs_impl(const vtr::vector_map& /*pin_id_map*/, + const vtr::vector_map& /*port_id_map*/) { + // Unused +} + +void APNetlist::rebuild_port_refs_impl(const vtr::vector_map& /*block_id_map*/, const vtr::vector_map& /*pin_id_map*/) { + // Unused +} + +void APNetlist::rebuild_pin_refs_impl(const vtr::vector_map& /*port_id_map*/, const vtr::vector_map& /*net_id_map*/) { + // Unused +} + +void APNetlist::rebuild_net_refs_impl(const vtr::vector_map& /*pin_id_map*/) { + // Unused +} + +void APNetlist::shrink_to_fit_impl() { + // Block data + block_molecules_.shrink_to_fit(); + block_mobilities_.shrink_to_fit(); + block_locs_.shrink_to_fit(); +} + +void APNetlist::remove_block_impl(const APBlockId /*blk_id*/) { + // Unused +} + +void APNetlist::remove_port_impl(const APPortId /*port_id*/) { + // Unused +} + +void APNetlist::remove_pin_impl(const APPinId /*pin_id*/) { + // Unused +} + +void APNetlist::remove_net_impl(const APNetId /*net_id*/) { + // Unused +} + +/* + * Sanity Checks + */ +bool APNetlist::validate_block_sizes_impl(size_t num_blocks) const { + if (block_molecules_.size() != num_blocks) + return false; + if (block_mobilities_.size() != num_blocks) + return false; + if (block_locs_.size() != num_blocks) + return false; + return true; +} + +bool APNetlist::validate_port_sizes_impl(size_t /*num_ports*/) const { + // No AP-specific port data to check + return true; +} + +bool APNetlist::validate_pin_sizes_impl(size_t /*num_pins*/) const { + // No AP-specific pin data to check + return true; +} + +bool APNetlist::validate_net_sizes_impl(size_t /*num_nets*/) const { + // No AP-specific net data to check + return true; +} + diff --git a/vpr/src/analytical_place/ap_netlist.h b/vpr/src/analytical_place/ap_netlist.h new file mode 100644 index 00000000000..570543df490 --- /dev/null +++ b/vpr/src/analytical_place/ap_netlist.h @@ -0,0 +1,189 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief Defines the APNetlist class used to store the connectivity of + * primitives in the Analytical Placement context. + * + * In the context of Analytical Placement, a block is a collection of atoms + * (primitives) which want to move together. For example, if one atom in a block + * should be moved one unit to the left, all atoms in the block want to move one + * unit to the left. + * + * An example of a block is pack molecules, which are atoms which were prepacked + * together. + * + * The nets in the netlist represent the logical connections between AP + * blocks (inferred from the atom block connectivity), where nets which are + * unused by Analytical Placement are ignored. + */ + +#pragma once + +#include +#include "netlist.h" +#include "ap_netlist_fwd.h" + +// Forward declarations +class t_pack_molecule; + +/** + * @brief Struct to store fixed block location information + * + * Currently assumes that blocks are fixed to single locations (not ranges). + * TODO: This assumption could be relaxed and allow fixing a range of locations. + * + * -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; +}; + +/** + * @brief The mobility of a block in the APNetlist + * TODO: It would be nice if the netlist contained lists of moveable and fixed + * block ids. + */ +enum class APBlockMobility : bool { + MOVEABLE, // The block is not constrained in any dimension. + FIXED // The block is fixed. +}; + +/** + * @brief The netlist used during Analytical Placement + * + * This class abstracts the placeable blocks and connections between the blocks + * away from the atom netlist. An APBlock is assumed to be some collection of + * primitive blocks and an APNet is assumed to be some connection between the + * APBlocks. These need not have physical meaning. + */ +class APNetlist : public Netlist { +public: + /** + * @brief Constructs a netlist + * + * @param name The name of the netlist (e.g. top-level module) + * @param id A unique identifier for the netlist (e.g. a secure digest of + * the input file) + */ + APNetlist(std::string name = "", std::string id = "") : Netlist(name, id) {} + + APNetlist(const APNetlist& rhs) = default; + APNetlist& operator=(const APNetlist& rhs) = default; + +public: // Public Accessors + /* + * Blocks + */ + + /// @brief Returns the molecule that this block represents. + const t_pack_molecule* block_molecule(const APBlockId id) const; + + /// @brief Returns the mobility of this block. + APBlockMobility block_mobility(const APBlockId id) const; + + /// @brief Returns the location of this block, if the block is fixed. + /// This method should not be used if the block is moveable. + const APFixedBlockLoc& block_loc(const APBlockId id) const; + +public: // Public Mutators + /* + * Note: all create_*() functions will silently return the appropriate ID + * if it has already been created. + */ + + /** + * @brief Create or return an existing block in the netlist + * + * @param name The unique name of the block + * @param mol The molecule the block represents + */ + APBlockId create_block(const std::string& name, const t_pack_molecule* mol); + + /** + * @brief Fixes a block at the given location + * + * @param id The block to fix + * @param loc The location to fix the block to + */ + void set_block_loc(const APBlockId id, const APFixedBlockLoc& loc); + + /** + * @brief Create or return an existing port in the netlist + * + * @param blk_id The block the port is associated with + * @param name The name of the port + * @param width The width (number of bits) of the port + * @param type The type of the port (INPUT, OUTPUT, or CLOCK) + */ + APPortId create_port(const APBlockId blk_id, const std::string& name, BitIndex width, PortType type); + + /** + * @brief Create or return an existing pin in the netlist + * + * @param port_id The port this pin is associated with + * @param port_bit The bit index of the pin in the port + * @param net_id The net the pin drives/sinks + * @param pin_type The type of the pin (driver/sink) + * @param is_const Indicates whether the pin holds a constant value (e.g. + * vcc/gnd) + */ + APPinId create_pin(const APPortId port_id, BitIndex port_bit, const APNetId net_id, const PinType pin_type, bool is_const = false); + + /** + * @brief Create an empty, or return an existing net in the netlist + * + * @param name The unique name of the net + */ + APNetId create_net(const std::string& name); + +private: // Private Members + /* + * Netlist compression / optimization + */ + + /// @brief Removes invalid components and reorders them + void clean_blocks_impl(const vtr::vector_map& block_id_map) override; + void clean_ports_impl(const vtr::vector_map& port_id_map) override; + void clean_pins_impl(const vtr::vector_map& pin_id_map) override; + void clean_nets_impl(const vtr::vector_map& net_id_map) override; + + void rebuild_block_refs_impl(const vtr::vector_map& pin_id_map, const vtr::vector_map& port_id_map) override; + void rebuild_port_refs_impl(const vtr::vector_map& block_id_map, const vtr::vector_map& pin_id_map) override; + void rebuild_pin_refs_impl(const vtr::vector_map& port_id_map, const vtr::vector_map& net_id_map) override; + void rebuild_net_refs_impl(const vtr::vector_map& pin_id_map) override; + + /// @brief Shrinks internal data structures to required size to reduce + /// memory consumption + void shrink_to_fit_impl() override; + + /* + * Component removal + */ + void remove_block_impl(const APBlockId blk_id) override; + void remove_port_impl(const APPortId port_id) override; + void remove_pin_impl(const APPinId pin_id) override; + void remove_net_impl(const APNetId net_id) override; + + /* + * Sanity checks + */ + // Verify the internal data structure sizes match + bool validate_block_sizes_impl(size_t num_blocks) const override; + bool validate_port_sizes_impl(size_t num_ports) const override; + bool validate_pin_sizes_impl(size_t num_pins) const override; + bool validate_net_sizes_impl(size_t num_nets) const override; + +private: // Private Data + /// @brief Molecule of each block + vtr::vector_map block_molecules_; + /// @brief Type of each block + vtr::vector_map block_mobilities_; + /// @brief Location of each block (if fixed). + /// NOTE: This vector will likely be quite sparse. + vtr::vector_map block_locs_; +}; + diff --git a/vpr/src/analytical_place/ap_netlist_fwd.h b/vpr/src/analytical_place/ap_netlist_fwd.h new file mode 100644 index 00000000000..3fc9e87dea7 --- /dev/null +++ b/vpr/src/analytical_place/ap_netlist_fwd.h @@ -0,0 +1,104 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief Forward declarations of the APNetlist class + * + * Forward declares the APNetlist class, and defines common types used by it. + */ + +#pragma once + +#include "netlist_fwd.h" +#include "vtr_strong_id.h" + +// Forward declaration +class APNetlist; + +/* + * Ids + * + * The APNetlist uses unique IDs to identify any component of the netlist. + * To avoid type-conversion errors, we use vtr::StrongIds. + */ + +/** + * @brief A unique identifier for a block in the AP netlist + */ +class APBlockId : public ParentBlockId { +public: + static constexpr APBlockId INVALID() { return APBlockId(); } + using ParentBlockId::ParentBlockId; + + friend std::hash; +}; + +/** + * @brief A unique identifier for a net in the AP netlist + */ +class APNetId : public ParentNetId { +public: + static constexpr APNetId INVALID() { return APNetId(); } + using ParentNetId::ParentNetId; + + friend std::hash; +}; + +/** + * @brief A unique identifier for a port in the AP netlist + */ +class APPortId : public ParentPortId { +public: + static constexpr APPortId INVALID() { return APPortId(); } + using ParentPortId::ParentPortId; + + friend std::hash; +}; + +/** + * @brief A unique identifier for a pin in the AP netlist + */ +class APPinId : public ParentPinId { +public: + static constexpr APPinId INVALID() { return APPinId(); } + using ParentPinId::ParentPinId; + + friend std::hash; +}; + +/** + * @brief Specialized std::hash for StrongIds + * + * This is needed for std::unordered_map-like containers. + */ +namespace std { +template<> +struct hash { + std::hash parent_hash; + std::size_t operator()(const APBlockId k) const noexcept { + return parent_hash(k); // Hash with the underlying type + } +}; +template<> +struct hash { + std::hash parent_hash; + std::size_t operator()(const APNetId k) const noexcept { + return parent_hash(k); // Hash with the underlying type + } +}; +template<> +struct hash { + std::hash parent_hash; + std::size_t operator()(const APPortId k) const noexcept { + return parent_hash(k); // Hash with the underlying type + } +}; +template<> +struct hash { + std::hash parent_hash; + std::size_t operator()(const APPinId k) const noexcept { + return parent_hash(k); // Hash with the underlying type + } +}; +} // namespace std + diff --git a/vpr/test/test_ap_netlist.cpp b/vpr/test/test_ap_netlist.cpp new file mode 100644 index 00000000000..9dad87819ca --- /dev/null +++ b/vpr/test/test_ap_netlist.cpp @@ -0,0 +1,77 @@ +/** + * @file + * @author Alex Singer + * @date September 2024 + * @brief Unit tests for the APNetlist class. + * + * These are very quick functionality checks to ensure that the APNetlist is + * working as intended. These tests mainly focus on new features in the + * APNetlist which are not in the base Netlist class. + */ +#include "catch2/catch_test_macros.hpp" + +#include "ap_netlist.h" +#include "vpr_types.h" + +namespace { + +TEST_CASE("test_ap_netlist_data_storage", "[vpr_ap_netlist]") { + // Create a test netlist object. + APNetlist test_netlist("test_netlist"); + // Create a few molecules. + t_pack_molecule mol_a; + t_pack_molecule mol_b; + t_pack_molecule mol_c; + // Create blocks for these molecules. + APBlockId block_id_a = test_netlist.create_block("BlockA", &mol_a); + APBlockId block_id_b = test_netlist.create_block("BlockB", &mol_b); + APBlockId block_id_c = test_netlist.create_block("BlockC", &mol_c); + + SECTION("Test block_molecule returns the correct molecule after creation") { + REQUIRE(test_netlist.block_molecule(block_id_a) == &mol_a); + REQUIRE(test_netlist.block_molecule(block_id_b) == &mol_b); + REQUIRE(test_netlist.block_molecule(block_id_c) == &mol_c); + } + + // Delete block B to reorganize the blocks internally. + test_netlist.remove_block(block_id_b); + test_netlist.compress(); + // Get the new block ids (invalidated by compress). + block_id_a = test_netlist.find_block("BlockA"); + block_id_b = APBlockId::INVALID(); + block_id_c = test_netlist.find_block("BlockC"); + + SECTION("Test block_molecule returns the correct molecule after compression") { + REQUIRE(test_netlist.block_molecule(block_id_a) == &mol_a); + REQUIRE(test_netlist.block_molecule(block_id_c) == &mol_c); + } + + // Create a new block, and fix its location. + t_pack_molecule fixed_mol; + APBlockId fixed_block_id = test_netlist.create_block("FixedBlock", &fixed_mol); + APFixedBlockLoc fixed_block_loc; + fixed_block_loc.x = 12; + fixed_block_loc.y = 42; + fixed_block_loc.layer_num = 2; + fixed_block_loc.sub_tile = 1; + test_netlist.set_block_loc(fixed_block_id, fixed_block_loc); + + SECTION("Test block_type returns the correct block type") { + // Make sure the default block mobility is moveable. + REQUIRE(test_netlist.block_mobility(block_id_a) == APBlockMobility::MOVEABLE); + REQUIRE(test_netlist.block_mobility(block_id_c) == APBlockMobility::MOVEABLE); + // Make sure the fixed block has a fixed mobility. + REQUIRE(test_netlist.block_mobility(fixed_block_id) == APBlockMobility::FIXED); + } + + SECTION("Test block_loc returns the correct location") { + const APFixedBlockLoc& stored_loc = test_netlist.block_loc(fixed_block_id); + REQUIRE(stored_loc.x == fixed_block_loc.x); + REQUIRE(stored_loc.y == fixed_block_loc.y); + REQUIRE(stored_loc.layer_num == fixed_block_loc.layer_num); + REQUIRE(stored_loc.sub_tile == fixed_block_loc.sub_tile); + } +} + +} // namespace +