OpenFPGA/vpr/src/base/netlist.tpp

1990 lines
72 KiB
C++

#include <algorithm>
#include <numeric>
#include "vtr_assert.h"
#include "vtr_log.h"
#include "vpr_error.h"
/*
*
* NetlistIdRemapper class implementation
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId NetlistIdRemapper<BlockId, PortId, PinId, NetId>::new_block_id(BlockId old_id) const {
return block_id_map_[old_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PortId NetlistIdRemapper<BlockId, PortId, PinId, NetId>::new_port_id(PortId old_id) const {
return port_id_map_[old_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinId NetlistIdRemapper<BlockId, PortId, PinId, NetId>::new_pin_id(PinId old_id) const {
return pin_id_map_[old_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId NetlistIdRemapper<BlockId, PortId, PinId, NetId>::new_net_id(NetId old_id) const {
return net_id_map_[old_id];
}
/*
*
* Netlist class implementation
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
Netlist<BlockId, PortId, PinId, NetId>::Netlist(std::string name, std::string id)
: netlist_name_(name)
, netlist_id_(id) {}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
Netlist<BlockId, PortId, PinId, NetId>::~Netlist() = default;
/*
*
* Netlist
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
const std::string& Netlist<BlockId, PortId, PinId, NetId>::netlist_name() const {
return netlist_name_;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
const std::string& Netlist<BlockId, PortId, PinId, NetId>::netlist_id() const {
return netlist_id_;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::is_dirty() const {
return dirty_;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::is_compressed() const {
return !is_dirty();
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::print_stats() const {
VTR_LOG("Blocks %zu capacity/size: %.2f\n", block_ids_.size(), float(block_ids_.capacity()) / block_ids_.size());
VTR_LOG("Ports %zu capacity/size: %.2f\n", port_ids_.size(), float(port_ids_.capacity()) / port_ids_.size());
VTR_LOG("Pins %zu capacity/size: %.2f\n", pin_ids_.size(), float(pin_ids_.capacity()) / pin_ids_.size());
VTR_LOG("Nets %zu capacity/size: %.2f\n", net_ids_.size(), float(net_ids_.capacity()) / net_ids_.size());
VTR_LOG("Strings %zu capacity/size: %.2f\n", string_ids_.size(), float(string_ids_.capacity()) / string_ids_.size());
}
/*
*
* Blocks
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
const std::string& Netlist<BlockId, PortId, PinId, NetId>::block_name(const BlockId blk_id) const {
StringId str_id = block_names_[blk_id];
return strings_[str_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::block_is_combinational(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
return block_clock_pins(blk_id).size() == 0;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::attr_range Netlist<BlockId, PortId, PinId, NetId>::block_attrs(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
return vtr::make_range(block_attrs_[blk_id].begin(), block_attrs_[blk_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::param_range Netlist<BlockId, PortId, PinId, NetId>::block_params(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
return vtr::make_range(block_params_[blk_id].begin(), block_params_[blk_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::block_pins(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
return vtr::make_range(block_pins_[blk_id].begin(), block_pins_[blk_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::block_input_pins(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
auto begin = block_pins_[blk_id].begin();
auto end = block_pins_[blk_id].begin() + block_num_input_pins_[blk_id];
return vtr::make_range(begin, end);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::block_output_pins(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
auto begin = block_pins_[blk_id].begin() + block_num_input_pins_[blk_id];
auto end = begin + block_num_output_pins_[blk_id];
return vtr::make_range(begin, end);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::block_clock_pins(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
auto begin = block_pins_[blk_id].begin()
+ block_num_input_pins_[blk_id]
+ block_num_output_pins_[blk_id];
auto end = begin + block_num_clock_pins_[blk_id];
VTR_ASSERT_SAFE(end == block_pins_[blk_id].end());
return vtr::make_range(begin, end);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::port_range Netlist<BlockId, PortId, PinId, NetId>::block_ports(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
return vtr::make_range(block_ports_[blk_id].begin(), block_ports_[blk_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::port_range Netlist<BlockId, PortId, PinId, NetId>::block_input_ports(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
auto begin = block_ports_[blk_id].begin();
auto end = block_ports_[blk_id].begin() + block_num_input_ports_[blk_id];
return vtr::make_range(begin, end);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::port_range Netlist<BlockId, PortId, PinId, NetId>::block_output_ports(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
auto begin = block_ports_[blk_id].begin() + block_num_input_ports_[blk_id];
auto end = begin + block_num_output_ports_[blk_id];
return vtr::make_range(begin, end);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::port_range Netlist<BlockId, PortId, PinId, NetId>::block_clock_ports(const BlockId blk_id) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
auto begin = block_ports_[blk_id].begin()
+ block_num_input_ports_[blk_id]
+ block_num_output_ports_[blk_id];
auto end = begin + block_num_clock_ports_[blk_id];
VTR_ASSERT_SAFE(end == block_ports_[blk_id].end());
return vtr::make_range(begin, end);
}
/*
*
* Ports
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
const std::string& Netlist<BlockId, PortId, PinId, NetId>::port_name(const PortId port_id) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
StringId str_id = port_names_[port_id];
return strings_[str_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::port_block(const PortId port_id) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
return port_blocks_[port_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::port_pins(const PortId port_id) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
return vtr::make_range(port_pins_[port_id].begin(), port_pins_[port_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinId Netlist<BlockId, PortId, PinId, NetId>::port_pin(const PortId port_id, const BitIndex port_bit) const {
//Convenience look-up bypassing port
return find_pin(port_id, port_bit);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId Netlist<BlockId, PortId, PinId, NetId>::port_net(const PortId port_id, const BitIndex port_bit) const {
//port_pin() will validate that port_bit and port_id are valid so don't
//check redundently here
//Convenience look-up bypassing port and pin
PinId pin_id = port_pin(port_id, port_bit);
if (pin_id) {
return pin_net(pin_id);
} else {
return NetId::INVALID();
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BitIndex Netlist<BlockId, PortId, PinId, NetId>::port_width(const PortId port_id) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
return port_widths_[port_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PortType Netlist<BlockId, PortId, PinId, NetId>::port_type(const PortId port_id) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
return port_types_[port_id];
}
/*
*
* Pins
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
std::string Netlist<BlockId, PortId, PinId, NetId>::pin_name(const PinId pin_id) const {
BlockId blk = pin_block(pin_id);
PortId port = pin_port(pin_id);
return block_name(blk) + "." + port_name(port) + "[" + std::to_string(pin_port_bit(pin_id)) + "]";
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinType Netlist<BlockId, PortId, PinId, NetId>::pin_type(const PinId pin_id) const {
auto port_id = pin_port(pin_id);
PinType type = PinType::OPEN;
switch (port_type(port_id)) {
case PortType::INPUT: /*fallthrough */;
case PortType::CLOCK:
type = PinType::SINK;
break;
case PortType::OUTPUT:
type = PinType::DRIVER;
break;
default:
VTR_ASSERT_OPT_MSG(false, "Valid port type");
}
return type;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId Netlist<BlockId, PortId, PinId, NetId>::pin_net(const PinId pin_id) const {
VTR_ASSERT_SAFE(valid_pin_id(pin_id));
return pin_nets_[pin_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
int Netlist<BlockId, PortId, PinId, NetId>::pin_net_index(const PinId pin_id) const {
VTR_ASSERT_SAFE(valid_pin_id(pin_id));
return pin_net_indices_[pin_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PortId Netlist<BlockId, PortId, PinId, NetId>::pin_port(const PinId pin_id) const {
VTR_ASSERT_SAFE(valid_pin_id(pin_id));
return pin_ports_[pin_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BitIndex Netlist<BlockId, PortId, PinId, NetId>::pin_port_bit(const PinId pin_id) const {
VTR_ASSERT_SAFE(valid_pin_id(pin_id));
return pin_port_bits_[pin_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::pin_block(const PinId pin_id) const {
//Convenience lookup bypassing the port
VTR_ASSERT_SAFE(valid_pin_id(pin_id));
PortId port_id = pin_port(pin_id);
return port_block(port_id);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PortType Netlist<BlockId, PortId, PinId, NetId>::pin_port_type(const PinId pin_id) const {
PortId port_id = pin_port(pin_id);
return port_type(port_id);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::pin_is_constant(const PinId pin_id) const {
VTR_ASSERT_SAFE(valid_pin_id(pin_id));
return pin_is_constant_[pin_id];
}
/*
*
* Nets
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
const std::string& Netlist<BlockId, PortId, PinId, NetId>::net_name(const NetId net_id) const {
VTR_ASSERT_SAFE(valid_net_id(net_id));
StringId str_id = net_names_[net_id];
return strings_[str_id];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::net_pins(const NetId net_id) const {
VTR_ASSERT_SAFE(valid_net_id(net_id));
return vtr::make_range(net_pins_[net_id].begin(), net_pins_[net_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinId Netlist<BlockId, PortId, PinId, NetId>::net_pin(const NetId net_id, int net_pin_index) const {
VTR_ASSERT_SAFE(valid_net_id(net_id));
VTR_ASSERT_SAFE_MSG(net_pin_index >= 0 && size_t(net_pin_index) < net_pins_[net_id].size(), "Pin index must be in range");
return net_pins_[net_id][net_pin_index];
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::net_pin_block(const NetId net_id, int net_pin_index) const {
auto pin_id = net_pin(net_id, net_pin_index);
return pin_block(pin_id);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinId Netlist<BlockId, PortId, PinId, NetId>::net_driver(const NetId net_id) const {
VTR_ASSERT_SAFE(valid_net_id(net_id));
if (net_pins_[net_id].size() > 0) {
return net_pins_[net_id][0];
} else {
return PinId::INVALID();
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::net_driver_block(const NetId net_id) const {
auto driver_pin_id = net_driver(net_id);
if (driver_pin_id) {
return pin_block(driver_pin_id);
}
return BlockId::INVALID();
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::net_sinks(const NetId net_id) const {
VTR_ASSERT_SAFE(valid_net_id(net_id));
return vtr::make_range(++net_pins_[net_id].begin(), net_pins_[net_id].end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::net_is_constant(const NetId net_id) const {
VTR_ASSERT_SAFE(valid_net_id(net_id));
//Look-up the driver
auto driver_pin_id = net_driver(net_id);
if (driver_pin_id) {
//Valid driver, see it is constant
return pin_is_constant(driver_pin_id);
}
//No valid driver so can't be const
return false;
}
/*
*
* Aggregates
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::block_range Netlist<BlockId, PortId, PinId, NetId>::blocks() const {
return vtr::make_range(block_ids_.begin(), block_ids_.end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::port_range Netlist<BlockId, PortId, PinId, NetId>::ports() const {
return vtr::make_range(port_ids_.begin(), port_ids_.end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::pin_range Netlist<BlockId, PortId, PinId, NetId>::pins() const {
return vtr::make_range(pin_ids_.begin(), pin_ids_.end());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::net_range Netlist<BlockId, PortId, PinId, NetId>::nets() const {
return vtr::make_range(net_ids_.begin(), net_ids_.end());
}
/*
*
* Lookups
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::find_block(const std::string& name) const {
auto str_id = find_string(name);
if (!str_id) {
return BlockId::INVALID();
} else {
return find_block(str_id);
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PortId Netlist<BlockId, PortId, PinId, NetId>::find_port(const BlockId blk_id, const std::string& name) const {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
//Since we only know the port name, we must search all the ports
for (auto port_id : block_ports(blk_id)) {
if (port_name(port_id) == name) {
return port_id;
}
}
return PortId::INVALID();
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId Netlist<BlockId, PortId, PinId, NetId>::find_net(const std::string& name) const {
auto str_id = find_string(name);
if (!str_id) {
return NetId::INVALID();
} else {
return find_net(str_id);
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinId Netlist<BlockId, PortId, PinId, NetId>::find_pin(const PortId port_id, BitIndex port_bit) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
VTR_ASSERT_SAFE(valid_port_bit(port_id, port_bit));
//Pins are stored in ascending order of bit index,
//so we can binary search for the specific bit
auto port_bit_cmp = [&](const PinId pin_id, BitIndex bit_index) {
return pin_port_bit(pin_id) < bit_index;
};
auto pins_rng = port_pins(port_id);
//Finds the location where the pin with bit index port_bit should be located (if it exists)
auto iter = std::lower_bound(pins_rng.begin(), pins_rng.end(), port_bit, port_bit_cmp);
if (iter == pins_rng.end() || pin_port_bit(*iter) != port_bit) {
//Either the end of the pins (i.e. not found), or
//the value does not match (indicating a gap in the indicies, so also not found)
return PinId::INVALID();
} else {
//Found it
VTR_ASSERT_SAFE(pin_port_bit(*iter) == port_bit);
return *iter;
}
}
/*
*
* Validation
*
*/
//Top-level verification method, checks that the sizes
//references, and lookups are all consistent.
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::verify() const {
bool valid = true;
//Verify data structure consistency
valid &= verify_sizes();
valid &= verify_refs();
valid &= verify_lookups();
//Verify logical consistency
valid &= verify_block_invariants();
return valid;
}
//Checks that the sizes of internal data structures
//are consistent. Should take constant time.
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::verify_sizes() const {
bool valid = true;
valid &= validate_block_sizes();
valid &= validate_port_sizes();
valid &= validate_pin_sizes();
valid &= validate_net_sizes();
valid &= validate_string_sizes();
return valid;
}
//Checks that all cross-references are consistent.
//Should take linear time.
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::verify_refs() const {
bool valid = true;
valid &= validate_block_port_refs();
valid &= validate_port_pin_refs();
valid &= validate_block_pin_refs();
valid &= validate_net_pin_refs();
valid &= validate_string_refs();
return valid;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::verify_lookups() const {
//Verify that fast look-ups are consistent
//Blocks
for (auto blk_id : blocks()) {
const auto& name = block_name(blk_id);
if (find_block(name) != blk_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block lookup by name mismatch");
}
}
//Ports
for (auto port_id : port_ids_) {
auto blk_id = port_block(port_id);
const auto& name = port_name(port_id);
if (find_port(blk_id, name) != port_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Port lookup by name mismatch");
}
}
//Pins
for (auto pin_id : pin_ids_) {
auto port_id = pin_port(pin_id);
auto bit = pin_port_bit(pin_id);
if (find_pin(port_id, bit) != pin_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Pin lookup by name mismatch");
}
}
//Nets
for (auto net_id : nets()) {
const auto& name = net_name(net_id);
if (find_net(name) != net_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Net lookup by name mismatch");
}
}
//Strings
for (auto str_id : string_ids_) {
const auto& name = strings_[str_id];
if (find_string(name) != str_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "String lookup by name mismatch");
}
}
return true;
}
/*
*
* Mutators
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::create_block(const std::string name) {
//Must have a non-empty name
VTR_ASSERT_MSG(!name.empty(), "Non-Empty block name");
//Check if the block has already been created
StringId name_id = create_string(name);
BlockId blk_id = find_block(name_id);
if (blk_id == BlockId::INVALID()) {
//Not found, create it
//Reserve an id
blk_id = BlockId(block_ids_.size());
block_ids_.push_back(blk_id);
//Initialize the data
block_names_.push_back(name_id);
block_attrs_.emplace_back();
block_params_.emplace_back();
//Initialize the look-ups
block_name_to_block_id_.insert(name_id, blk_id);
block_pins_.emplace_back();
block_num_input_pins_.push_back(0);
block_num_output_pins_.push_back(0);
block_num_clock_pins_.push_back(0);
block_ports_.emplace_back();
block_num_input_ports_.push_back(0);
block_num_output_ports_.push_back(0);
block_num_clock_ports_.push_back(0);
}
//Check post-conditions: values
VTR_ASSERT(valid_block_id(blk_id));
VTR_ASSERT(block_name(blk_id) == name);
VTR_ASSERT_SAFE(find_block(name) == blk_id);
return blk_id;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PortId Netlist<BlockId, PortId, PinId, NetId>::create_port(const BlockId blk_id, const std::string name, BitIndex width, PortType type) {
//Check pre-conditions
VTR_ASSERT_MSG(valid_block_id(blk_id), "Valid block id");
//See if the port already exists
StringId name_id = create_string(name);
PortId port_id = find_port(blk_id, name);
if (!port_id) { //Not found, create it
//Reserve an id
port_id = PortId(port_ids_.size());
port_ids_.push_back(port_id);
//Initialize the per-port-instance data
port_blocks_.push_back(blk_id);
port_names_.push_back(name_id);
port_widths_.push_back(width);
port_types_.push_back(type);
//Allocate the pins, initialize to invalid Ids
port_pins_.emplace_back();
}
//Check post-conditions: values
VTR_ASSERT(valid_port_id(port_id));
VTR_ASSERT(port_block(port_id) == blk_id);
VTR_ASSERT(port_width(port_id) == width);
return port_id;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
PinId Netlist<BlockId, PortId, PinId, NetId>::create_pin(const PortId port_id, BitIndex port_bit, const NetId net_id, const PinType type, bool is_const) {
//Check pre-conditions (valid ids)
VTR_ASSERT_MSG(valid_port_id(port_id), "Valid port id");
VTR_ASSERT_MSG(valid_port_bit(port_id, port_bit), "Valid port bit");
VTR_ASSERT_MSG(valid_net_id(net_id), "Valid net id");
//See if the pin already exists
PinId pin_id = find_pin(port_id, port_bit);
if (!pin_id) {
//Not found, create it
//Reserve an id
pin_id = PinId(pin_ids_.size());
pin_ids_.push_back(pin_id);
//Initialize the pin data
pin_ports_.push_back(port_id);
pin_port_bits_.push_back(port_bit);
pin_nets_.push_back(net_id);
pin_is_constant_.push_back(is_const);
//Add the pin to the net
int pins_net_index = associate_pin_with_net(pin_id, type, net_id);
//Save the net index of the pin
pin_net_indices_.push_back(pins_net_index);
//Add the pin to the port
associate_pin_with_port(pin_id, port_id);
//Add the pin to the block
associate_pin_with_block(pin_id, port_type(port_id), port_block(port_id));
}
//Check post-conditions: values
VTR_ASSERT(valid_pin_id(pin_id));
VTR_ASSERT(pin_port(pin_id) == port_id);
VTR_ASSERT(pin_port_bit(pin_id) == port_bit);
VTR_ASSERT(pin_net(pin_id) == net_id);
VTR_ASSERT(pin_is_constant(pin_id) == is_const);
VTR_ASSERT_SAFE(find_pin(port_id, port_bit) == pin_id);
return pin_id;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId Netlist<BlockId, PortId, PinId, NetId>::create_net(const std::string name) {
//Creates an empty net (or returns an existing one)
VTR_ASSERT_MSG(!name.empty(), "Valid net name");
//Check if the net has already been created
StringId name_id = create_string(name);
NetId net_id = find_net(name_id);
if (net_id == NetId::INVALID()) {
//Not found, create it
//Reserve an id
net_id = NetId(net_ids_.size());
net_ids_.push_back(net_id);
//Initialize the data
net_names_.push_back(name_id);
//Initialize the look-ups
net_name_to_net_id_.insert(name_id, net_id);
//Initialize with no driver
net_pins_.emplace_back();
net_pins_[net_id].emplace_back(PinId::INVALID());
VTR_ASSERT(net_pins_[net_id].size() == 1);
VTR_ASSERT(net_pins_[net_id][0] == PinId::INVALID());
}
//Check post-conditions: values
VTR_ASSERT(valid_net_id(net_id));
VTR_ASSERT(net_name(net_id) == name);
VTR_ASSERT(find_net(name) == net_id);
return net_id;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId Netlist<BlockId, PortId, PinId, NetId>::add_net(const std::string name, PinId driver, std::vector<PinId> sinks) {
//Creates a net with a full set of pins
VTR_ASSERT_MSG(!find_net(name), "Net should not exist");
//Create the empty net
NetId net_id = create_net(name);
//Set the driver and sinks of the net
auto& dest_pins = net_pins_[net_id];
dest_pins[0] = driver;
dest_pins.insert(dest_pins.end(),
std::make_move_iterator(sinks.begin()),
std::make_move_iterator(sinks.end()));
//Associate each pin with the net
int net_index = 0;
pin_nets_[driver] = net_id;
pin_net_indices_[driver] = net_index++;
for (auto sink : sinks) {
pin_nets_[sink] = net_id;
pin_net_indices_[sink] = net_index++;
}
return net_id;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::set_pin_is_constant(const PinId pin_id, const bool value) {
VTR_ASSERT(valid_pin_id(pin_id));
pin_is_constant_[pin_id] = value;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::set_pin_net(const PinId pin, PinType type, const NetId net) {
VTR_ASSERT(valid_pin_id(pin));
VTR_ASSERT((type == PinType::DRIVER && pin_port_type(pin) == PortType::OUTPUT)
|| (type == PinType::SINK && (pin_port_type(pin) == PortType::INPUT || pin_port_type(pin) == PortType::CLOCK)));
NetId orig_net = pin_net(pin);
if (orig_net) {
//Clean up the pin reference on the original net
remove_net_pin(orig_net, pin);
}
//Mark the pin's net
pin_nets_[pin] = net;
//Add the pin to the net
int pins_net_index = associate_pin_with_net(pin, type, net);
//Save the pin's index within the net
pin_net_indices_[pin] = pins_net_index;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::set_block_name(const BlockId blk_id, const std::string new_name) {
VTR_ASSERT(valid_block_id(blk_id));
//Names must be unique -- no duplicates allowed
BlockId existing_blk_id = find_block(new_name);
if (existing_blk_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Can not re-name block '%s' to '%s' (a block named '%s' already exists).",
block_name(blk_id).c_str(),
new_name.c_str(),
new_name.c_str());
}
//Remove old name-look-up
{
StringId old_string = find_string(block_name(blk_id));
block_name_to_block_id_[old_string] = BlockId::INVALID();
}
//Re-name the block
StringId new_string = create_string(new_name);
block_names_[blk_id] = new_string;
//Update name-look-up
block_name_to_block_id_.insert(new_string, blk_id);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::set_block_attr(const BlockId blk_id, const std::string& name, const std::string& value) {
VTR_ASSERT(valid_block_id(blk_id));
block_attrs_[blk_id][name] = value;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::set_block_param(const BlockId blk_id, const std::string& name, const std::string& value) {
VTR_ASSERT(valid_block_id(blk_id));
block_params_[blk_id][name] = value;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::merge_nets(const NetId driver_net, const NetId sink_net) {
VTR_ASSERT(valid_net_id(driver_net));
VTR_ASSERT(valid_net_id(sink_net));
//Sink net must not have a driver pin
PinId sink_driver = net_driver(sink_net);
if (sink_driver) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Can not merge nets '%s' and '%s' (sink net '%s' should have no driver, but is driven by pin '%s')",
net_name(driver_net).c_str(),
net_name(sink_net).c_str(),
net_name(sink_net).c_str(),
pin_name(sink_driver).c_str());
}
//We allow the driver net to (potentially) have no driver yet,
//so we don't check to ensure it exists
//
//Merge the nets
//
//Move the sinks to the driver net
for (PinId sink_pin : net_sinks(sink_net)) {
//Update pin -> net references, also adds pins to driver_net
set_pin_net(sink_pin, pin_type(sink_pin), driver_net);
}
//Remove the sink net
// Note that we drop the sink net's pin references first,
// this ensures remove_net() will only clean-up the net
// data, and will not modify the (already moved) pins
net_pins_[sink_net].clear(); //Drop sink_net's pin references
remove_net(sink_net);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::remove_block(const BlockId blk_id) {
VTR_ASSERT(valid_block_id(blk_id));
//Remove the ports
for (PortId block_port : block_ports(blk_id)) {
remove_port(block_port);
}
//Invalidate look-up
StringId name_id = block_names_[blk_id];
block_name_to_block_id_.insert(name_id, BlockId::INVALID());
//Mark as invalid
block_ids_[blk_id] = BlockId::INVALID();
//Call derived class' remove()
remove_block_impl(blk_id);
//Mark netlist dirty
dirty_ = true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::remove_port(const PortId port_id) {
VTR_ASSERT(valid_port_id(port_id));
//Remove the pins
for (auto pin : port_pins(port_id)) {
if (valid_pin_id(pin)) {
remove_pin(pin);
}
}
//Mark as invalid
port_ids_[port_id] = PortId::INVALID();
//Call derived class' remove()
remove_port_impl(port_id);
//Mark netlist dirty
dirty_ = true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::remove_pin(const PinId pin_id) {
VTR_ASSERT(valid_pin_id(pin_id));
//Find the associated net
NetId net = pin_net(pin_id);
//Remove the pin from the associated net/port/block
remove_net_pin(net, pin_id);
//Mark as invalid
pin_ids_[pin_id] = PinId::INVALID();
//Call derived class' remove()
remove_pin_impl(pin_id);
//Mark netlist dirty
dirty_ = true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::remove_net(const NetId net_id) {
VTR_ASSERT(valid_net_id(net_id));
//Disassociate the pins from the net
for (auto pin_id : net_pins(net_id)) {
if (pin_id) {
pin_nets_[pin_id] = NetId::INVALID();
pin_net_indices_[pin_id] = INVALID_INDEX;
}
}
//Invalidate look-up
StringId name_id = net_names_[net_id];
net_name_to_net_id_.insert(name_id, NetId::INVALID());
//Mark as invalid
net_ids_[net_id] = NetId::INVALID();
//Call derived class' remove()
remove_net_impl(net_id);
//Mark netlist dirty
dirty_ = true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::remove_net_pin(const NetId net_id, const PinId pin_id) {
//Remove a net-pin connection
//
//Note that during sweeping either the net or pin could be invalid (i.e. already swept)
//so we check before trying to use them
if (valid_net_id(net_id)) {
//Warning: this is slow!
auto iter = std::find(net_pins_[net_id].begin(), net_pins_[net_id].end(), pin_id); //Linear search
VTR_ASSERT(iter != net_pins_[net_id].end());
if (net_driver(net_id) == pin_id) {
//Mark no driver
net_pins_[net_id][0] = PinId::INVALID();
} else {
//Remove sink
net_pins_[net_id].erase(iter); //Linear remove
}
//Note: since we fully update the net we don't need to mark the netlist dirty_
}
//Disassociate the pin with the net
if (valid_pin_id(pin_id)) {
pin_nets_[pin_id] = NetId::INVALID();
pin_net_indices_[pin_id] = INVALID_INDEX;
//Mark netlist dirty, since we are leaving an invalid net id
dirty_ = true;
}
}
/*
*
* Internal utilities
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::IdRemapper Netlist<BlockId, PortId, PinId, NetId>::remove_and_compress() {
remove_unused();
return compress();
}
//Compress the various netlist components to remove invalid entries
// Note: this invalidates all Ids
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::IdRemapper Netlist<BlockId, PortId, PinId, NetId>::compress() {
//Build the mappings from old to new id's, potentially
//re-ordering for improved cache locality
//
//The vectors passed as parameters are initialized as a mapping from old to new index
// e.g. block_id_map[old_id] == new_id
IdRemapper id_remapper = build_id_maps();
clean_nets(id_remapper.net_id_map_);
clean_pins(id_remapper.pin_id_map_);
clean_ports(id_remapper.port_id_map_);
clean_blocks(id_remapper.block_id_map_);
//TODO: clean strings
//TODO: iterative cleaning?
//Now we re-build all the cross references
// Note: net references must be rebuilt (to remove pins) before
// the pin references can be rebuilt (to account for index changes
// due to pins being removed from the net)
rebuild_block_refs(id_remapper.pin_id_map_, id_remapper.port_id_map_);
rebuild_port_refs(id_remapper.block_id_map_, id_remapper.pin_id_map_);
rebuild_net_refs(id_remapper.pin_id_map_);
rebuild_pin_refs(id_remapper.port_id_map_, id_remapper.net_id_map_);
//Re-build the lookups
rebuild_lookups();
//Resize containers to exact size
shrink_to_fit();
//Netlist is now clean
dirty_ = false;
return id_remapper;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::remove_unused() {
//Mark any nets/pins/ports/blocks which are not in use as invalid
//so they will be removed
bool found_unused;
do {
found_unused = false;
//Nets with no connected pins
for (auto net_id : net_ids_) {
if (net_id
&& !net_driver(net_id)
&& net_sinks(net_id).size() == 0) {
remove_net(net_id);
found_unused = true;
}
}
//Pins with no connected nets
for (auto pin_id : pin_ids_) {
if (pin_id && !pin_net(pin_id)) {
remove_pin(pin_id);
found_unused = true;
}
}
//Empty ports
for (auto port_id : port_ids_) {
if (port_id && port_pins(port_id).size() == 0) {
remove_port(port_id);
found_unused = true;
}
}
//Blocks with no pins
for (auto blk_id : block_ids_) {
if (blk_id && block_pins(blk_id).size() == 0) {
remove_block(blk_id);
found_unused = true;
}
}
} while (found_unused);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::IdRemapper Netlist<BlockId, PortId, PinId, NetId>::build_id_maps() {
IdRemapper id_remapper;
id_remapper.block_id_map_ = compress_ids(block_ids_);
id_remapper.port_id_map_ = compress_ids(port_ids_);
id_remapper.pin_id_map_ = compress_ids(pin_ids_);
id_remapper.net_id_map_ = compress_ids(net_ids_);
return id_remapper;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::clean_blocks(const vtr::vector_map<BlockId, BlockId>& block_id_map) {
//Update all the block values
block_ids_ = clean_and_reorder_ids(block_id_map);
block_names_ = clean_and_reorder_values(block_names_, block_id_map);
block_pins_ = clean_and_reorder_values(block_pins_, block_id_map);
block_num_input_pins_ = clean_and_reorder_values(block_num_input_pins_, block_id_map);
block_num_output_pins_ = clean_and_reorder_values(block_num_output_pins_, block_id_map);
block_num_clock_pins_ = clean_and_reorder_values(block_num_clock_pins_, block_id_map);
block_ports_ = clean_and_reorder_values(block_ports_, block_id_map);
block_num_input_ports_ = clean_and_reorder_values(block_num_input_ports_, block_id_map);
block_num_output_ports_ = clean_and_reorder_values(block_num_output_ports_, block_id_map);
block_num_clock_ports_ = clean_and_reorder_values(block_num_clock_ports_, block_id_map);
block_attrs_ = clean_and_reorder_values(block_attrs_, block_id_map);
block_params_ = clean_and_reorder_values(block_params_, block_id_map);
clean_blocks_impl(block_id_map);
VTR_ASSERT(validate_block_sizes());
VTR_ASSERT_MSG(are_contiguous(block_ids_), "Ids should be contiguous");
VTR_ASSERT_MSG(all_valid(block_ids_), "All Ids should be valid");
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::clean_ports(const vtr::vector_map<PortId, PortId>& port_id_map) {
//Update all the port values
port_ids_ = clean_and_reorder_ids(port_id_map);
port_names_ = clean_and_reorder_values(port_names_, port_id_map);
port_blocks_ = clean_and_reorder_values(port_blocks_, port_id_map);
port_widths_ = clean_and_reorder_values(port_widths_, port_id_map);
port_types_ = clean_and_reorder_values(port_types_, port_id_map);
port_pins_ = clean_and_reorder_values(port_pins_, port_id_map);
clean_ports_impl(port_id_map);
VTR_ASSERT(validate_port_sizes());
VTR_ASSERT_MSG(are_contiguous(port_ids_), "Ids should be contiguous");
VTR_ASSERT_MSG(all_valid(port_ids_), "All Ids should be valid");
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::clean_pins(const vtr::vector_map<PinId, PinId>& pin_id_map) {
//Update all the pin values
pin_ids_ = clean_and_reorder_ids(pin_id_map);
pin_ports_ = clean_and_reorder_values(pin_ports_, pin_id_map);
//It is sufficient to merely clean and re-order the pin_port_bits_, since
//the size of a port (and hence the pin's bit index in the port) does not change
//during cleaning
pin_port_bits_ = clean_and_reorder_values(pin_port_bits_, pin_id_map);
pin_nets_ = clean_and_reorder_values(pin_nets_, pin_id_map);
//Note that clean and re-order serves only to resize pin_net_indices_, the
//actual net references are wrong (since net size may have changed during
//cleaning). They will be updated in rebuild_pin_refs()
pin_net_indices_ = clean_and_reorder_values(pin_net_indices_, pin_id_map);
pin_is_constant_ = clean_and_reorder_values(pin_is_constant_, pin_id_map);
clean_pins_impl(pin_id_map);
VTR_ASSERT(validate_pin_sizes());
VTR_ASSERT_MSG(are_contiguous(pin_ids_), "Ids should be contiguous");
VTR_ASSERT_MSG(all_valid(pin_ids_), "All Ids should be valid");
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::clean_nets(const vtr::vector_map<NetId, NetId>& net_id_map) {
//Update all the net values
net_ids_ = clean_and_reorder_ids(net_id_map);
net_names_ = clean_and_reorder_values(net_names_, net_id_map);
net_pins_ = clean_and_reorder_values(net_pins_, net_id_map); //Note: actual pin refs are updated in rebuild_net_refs()
clean_nets_impl(net_id_map);
VTR_ASSERT(validate_net_sizes());
VTR_ASSERT_MSG(are_contiguous(net_ids_), "Ids should be contiguous");
VTR_ASSERT_MSG(all_valid(net_ids_), "All Ids should be valid");
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::rebuild_lookups() {
//We iterate through the reverse-lookups and update the values (i.e. ids)
//to the new id values
//Blocks
block_name_to_block_id_.clear();
for (auto blk_id : blocks()) {
const auto& key = block_names_[blk_id];
block_name_to_block_id_.insert(key, blk_id);
}
//Nets
net_name_to_net_id_.clear();
for (auto net_id : nets()) {
const auto& key = net_names_[net_id];
net_name_to_net_id_.insert(key, net_id);
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::rebuild_block_refs(const vtr::vector_map<PinId, PinId>& pin_id_map,
const vtr::vector_map<PortId, PortId>& port_id_map) {
//Update the pin id references held by blocks
for (auto blk_id : blocks()) {
//Before update the references, we need to know how many are valid,
//so we can also update the numbers of input/output/clock pins
//Note that we take special care to not modify the pin counts until after
//they have all been counted (since the block_*_pins() functions depend on
//the counts).
size_t num_input_pins = count_valid_refs(block_input_pins(blk_id), pin_id_map);
size_t num_output_pins = count_valid_refs(block_output_pins(blk_id), pin_id_map);
size_t num_clock_pins = count_valid_refs(block_clock_pins(blk_id), pin_id_map);
std::vector<PinId>& pin_collection = block_pins_[blk_id];
pin_collection = update_valid_refs(pin_collection, pin_id_map);
block_num_input_pins_[blk_id] = num_input_pins;
block_num_output_pins_[blk_id] = num_output_pins;
block_num_clock_pins_[blk_id] = num_clock_pins;
VTR_ASSERT_SAFE_MSG(all_valid(pin_collection), "All Ids should be valid");
VTR_ASSERT(pin_collection.size() == size_t(block_num_input_pins_[blk_id] + block_num_output_pins_[blk_id] + block_num_clock_pins_[blk_id]));
//Similarily for ports
size_t num_input_ports = count_valid_refs(block_input_ports(blk_id), port_id_map);
size_t num_output_ports = count_valid_refs(block_output_ports(blk_id), port_id_map);
size_t num_clock_ports = count_valid_refs(block_clock_ports(blk_id), port_id_map);
std::vector<PortId>& blk_ports = block_ports_[blk_id];
blk_ports = update_valid_refs(blk_ports, port_id_map);
block_num_input_ports_[blk_id] = num_input_ports;
block_num_output_ports_[blk_id] = num_output_ports;
block_num_clock_ports_[blk_id] = num_clock_ports;
VTR_ASSERT_SAFE_MSG(all_valid(blk_ports), "All Ids should be valid");
VTR_ASSERT(blk_ports.size() == size_t(block_num_input_ports_[blk_id] + block_num_output_ports_[blk_id] + block_num_clock_ports_[blk_id]));
}
rebuild_block_refs_impl(pin_id_map, port_id_map);
VTR_ASSERT(validate_block_sizes());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::rebuild_port_refs(const vtr::vector_map<BlockId, BlockId>& block_id_map,
const vtr::vector_map<PinId, PinId>& pin_id_map) {
//Update block and pin references held by ports
port_blocks_ = update_valid_refs(port_blocks_, block_id_map);
VTR_ASSERT_SAFE_MSG(all_valid(port_blocks_), "All Ids should be valid");
VTR_ASSERT(port_blocks_.size() == port_ids_.size());
for (auto& pin_collection : port_pins_) {
pin_collection = update_valid_refs(pin_collection, pin_id_map);
VTR_ASSERT_SAFE_MSG(all_valid(pin_collection), "All Ids should be valid");
}
rebuild_port_refs_impl(block_id_map, pin_id_map);
VTR_ASSERT(validate_port_sizes());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::rebuild_pin_refs(const vtr::vector_map<PortId, PortId>& port_id_map,
const vtr::vector_map<NetId, NetId>& net_id_map) {
//Update port and net references held by pins
pin_ports_ = update_all_refs(pin_ports_, port_id_map);
VTR_ASSERT_SAFE_MSG(all_valid(pin_ports_), "All Ids should be valid");
//NOTE: we do not need to update pin_port_bits_ since a pin's index within it's
//port will not change (since the port width's don't change when cleaned)
pin_nets_ = update_all_refs(pin_nets_, net_id_map);
VTR_ASSERT_SAFE_MSG(all_valid(pin_nets_), "All Ids should be valid");
//We must carefully re-build the pin net indices from scratch, since cleaning
//may have changed the index of the pin within the net (e.g. if invalid pins
//were removed)
//
//Note that for this to work correctly, the net references must have already been re-built!
for (auto net : nets()) {
int i = 0;
for (auto pin : net_pins(net)) {
pin_net_indices_[pin] = i;
++i;
}
}
rebuild_pin_refs_impl(port_id_map, net_id_map);
VTR_ASSERT(validate_pin_sizes());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::rebuild_net_refs(const vtr::vector_map<PinId, PinId>& pin_id_map) {
//Update pin references held by nets
for (auto& pin_collection : net_pins_) {
//We take special care to preserve the driver index, since an INVALID id is used
//to indicate an undriven net it should not be dropped during the update
pin_collection = update_valid_refs(pin_collection, pin_id_map, {NET_DRIVER_INDEX});
VTR_ASSERT_SAFE_MSG(all_valid(pin_collection), "All sinks should be valid");
}
rebuild_net_refs_impl(pin_id_map);
VTR_ASSERT(validate_net_sizes());
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::shrink_to_fit() {
//Block data
block_ids_.shrink_to_fit();
block_names_.shrink_to_fit();
block_pins_.shrink_to_fit();
for (std::vector<PinId>& pin_collection : block_pins_) {
pin_collection.shrink_to_fit();
}
block_num_input_pins_.shrink_to_fit();
block_num_output_pins_.shrink_to_fit();
block_num_clock_pins_.shrink_to_fit();
block_ports_.shrink_to_fit();
for (auto& blk_ports : block_ports_) {
blk_ports.shrink_to_fit();
}
block_num_input_ports_.shrink_to_fit();
block_num_output_ports_.shrink_to_fit();
block_num_clock_ports_.shrink_to_fit();
VTR_ASSERT(validate_block_sizes());
//Port data
port_ids_.shrink_to_fit();
port_blocks_.shrink_to_fit();
port_widths_.shrink_to_fit();
port_types_.shrink_to_fit();
port_pins_.shrink_to_fit();
for (auto& pin_collection : port_pins_) {
pin_collection.shrink_to_fit();
}
VTR_ASSERT(validate_port_sizes());
//Pin data
pin_ids_.shrink_to_fit();
pin_ports_.shrink_to_fit();
pin_port_bits_.shrink_to_fit();
pin_nets_.shrink_to_fit();
pin_is_constant_.shrink_to_fit();
VTR_ASSERT(validate_pin_sizes());
//Net data
net_ids_.shrink_to_fit();
net_names_.shrink_to_fit();
net_pins_.shrink_to_fit();
VTR_ASSERT(validate_net_sizes());
//String data
string_ids_.shrink_to_fit();
strings_.shrink_to_fit();
VTR_ASSERT(validate_string_sizes());
}
/*
*
* Sanity Checks
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::valid_block_id(BlockId block_id) const {
if (block_id == BlockId::INVALID()
|| !block_ids_.contains(block_id)
|| block_ids_[block_id] != block_id) {
return false;
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::valid_port_id(PortId port_id) const {
if (port_id == PortId::INVALID()
|| !port_ids_.contains(port_id)
|| port_ids_[port_id] != port_id) {
return false;
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::valid_port_bit(PortId port_id, BitIndex port_bit) const {
VTR_ASSERT_SAFE(valid_port_id(port_id));
if (port_bit >= port_width(port_id)) {
return false;
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::valid_pin_id(PinId pin_id) const {
if (pin_id == PinId::INVALID()
|| !pin_ids_.contains(pin_id)
|| pin_ids_[pin_id] != pin_id) {
return false;
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::valid_net_id(NetId net_id) const {
if (net_id == NetId::INVALID()
|| !net_ids_.contains(net_id)
|| net_ids_[net_id] != net_id) {
return false;
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::valid_string_id(typename Netlist<BlockId, PortId, PinId, NetId>::StringId string_id) const {
if (string_id == StringId::INVALID()
|| !string_ids_.contains(string_id)
|| string_ids_[string_id] != string_id) {
return false;
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_block_sizes() const {
size_t num_blocks = blocks().size();
if (block_names_.size() != num_blocks
|| block_pins_.size() != num_blocks
|| block_num_input_pins_.size() != num_blocks
|| block_num_output_pins_.size() != num_blocks
|| block_num_clock_pins_.size() != num_blocks
|| block_ports_.size() != num_blocks
|| block_num_input_ports_.size() != num_blocks
|| block_num_output_ports_.size() != num_blocks
|| block_num_clock_ports_.size() != num_blocks
|| block_attrs_.size() != num_blocks
|| block_params_.size() != num_blocks
|| !validate_block_sizes_impl(num_blocks)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Inconsistent block data sizes");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_port_sizes() const {
size_t num_ports = ports().size();
if (port_names_.size() != num_ports
|| port_blocks_.size() != num_ports
|| port_pins_.size() != num_ports
|| !validate_port_sizes_impl(num_ports)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Inconsistent port data sizes");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_pin_sizes() const {
size_t num_pins = pins().size();
if (pin_ports_.size() != num_pins
|| pin_port_bits_.size() != num_pins
|| pin_nets_.size() != num_pins
|| pin_net_indices_.size() != num_pins
|| pin_is_constant_.size() != num_pins
|| !validate_pin_sizes_impl(num_pins)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Inconsistent pin data sizes");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_net_sizes() const {
size_t num_nets = nets().size();
if (net_names_.size() != num_nets
|| net_pins_.size() != num_nets
|| !validate_net_sizes_impl(num_nets)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Inconsistent net data sizes");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_string_sizes() const {
if (strings_.size() != string_ids_.size()) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Inconsistent string data sizes");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_block_port_refs() const {
//Verify that all block <-> port references are consistent
//Track how many times we've seen each port from the blocks
vtr::vector_map<PortId, unsigned> seen_port_ids(port_ids_.size());
for (auto blk_id : blocks()) {
for (auto in_port_id : block_input_ports(blk_id)) {
if (blk_id != port_block(in_port_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block-input port cross-reference does not match");
}
++seen_port_ids[in_port_id];
}
for (auto out_port_id : block_output_ports(blk_id)) {
if (blk_id != port_block(out_port_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block-output port cross-reference does not match");
}
++seen_port_ids[out_port_id];
}
for (auto clock_port_id : block_clock_ports(blk_id)) {
if (blk_id != port_block(clock_port_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block-clock port cross-reference does not match");
}
++seen_port_ids[clock_port_id];
}
}
//Check that we have neither orphaned ports (i.e. that aren't referenced by a block)
//nor shared ports (i.e. referenced by multiple blocks)
auto is_one = [](unsigned val) {
return val == 1;
};
if (!std::all_of(seen_port_ids.begin(), seen_port_ids.end(), is_one)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Port not referenced by a single block");
}
if (std::accumulate(seen_port_ids.begin(), seen_port_ids.end(), 0u) != port_ids_.size()) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Found orphaned port (not referenced by a block)");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_block_pin_refs() const {
//Verify that block pin refs are consistent
//(e.g. inputs are inputs, outputs are outputs etc.)
for (auto blk_id : blocks()) {
//Check that only input pins are found when iterating through inputs
for (auto pin_id : block_input_pins(blk_id)) {
auto port_id = pin_port(pin_id);
auto type = port_type(port_id);
if (type != PortType::INPUT) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Non-input pin in block input pins");
}
}
//Check that only output pins are found when iterating through outputs
for (auto pin_id : block_output_pins(blk_id)) {
auto port_id = pin_port(pin_id);
auto type = port_type(port_id);
if (type != PortType::OUTPUT) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Non-output pin in block output pins");
}
}
//Check that only clock pins are found when iterating through clocks
for (auto pin_id : block_clock_pins(blk_id)) {
auto port_id = pin_port(pin_id);
auto type = port_type(port_id);
if (type != PortType::CLOCK) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Non-clock pin in block clock pins");
}
}
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_port_pin_refs() const {
//Check that port <-> pin references are consistent
//Track how many times we've seen each pin from the ports
vtr::vector_map<PinId, unsigned> seen_pin_ids(pin_ids_.size());
for (auto port_id : port_ids_) {
bool first_bit = true;
BitIndex prev_bit_index = 0;
for (auto pin_id : port_pins(port_id)) {
if (pin_port(pin_id) != port_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Port-pin cross-reference does not match");
}
if (pin_port_bit(pin_id) >= port_width(port_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Out-of-range port bit index");
}
++seen_pin_ids[pin_id];
BitIndex port_bit_index = pin_port_bit(pin_id);
//Verify that the port bit index is legal
if (!valid_port_bit(port_id, port_bit_index)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Invalid pin bit index in port");
}
//Verify that the pins are listed in increasing order of port bit index,
//we rely on this property to perform fast binary searches for pins with specific bit
//indicies
if (first_bit) {
prev_bit_index = port_bit_index;
first_bit = false;
} else if (prev_bit_index >= port_bit_index) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Port pin indicies are not in ascending order");
}
}
}
//Check that we have neither orphaned pins (i.e. that aren't referenced by a port)
//nor shared pins (i.e. referenced by multiple ports)
auto is_one = [](unsigned val) {
return val == 1;
};
if (!std::all_of(seen_pin_ids.begin(), seen_pin_ids.end(), is_one)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Pins referenced by zero or multiple ports");
}
if (std::accumulate(seen_pin_ids.begin(), seen_pin_ids.end(), 0u) != pin_ids_.size()) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Found orphaned pins (not referenced by a port)");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_net_pin_refs() const {
//Check that net <-> pin references are consistent
//Track how many times we've seen each pin from the ports
vtr::vector_map<PinId, unsigned> seen_pin_ids(pin_ids_.size());
for (auto net_id : nets()) {
auto pins_rng = net_pins(net_id);
for (auto iter = pins_rng.begin(); iter != pins_rng.end(); ++iter) {
int pin_index = std::distance(pins_rng.begin(), iter);
auto pin_id = *iter;
if (iter == pins_rng.begin()) {
//The first net pin is the driver, which may be invalid
//if there is no driver.
if (pin_id) {
VTR_ASSERT(pin_index == NET_DRIVER_INDEX);
if (pin_type(pin_id) != PinType::DRIVER) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Driver pin not found at expected index in net");
}
}
} else {
//Any non-driver (i.e. sink) pins must be valid
if (!pin_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Invalid pin found in net sinks");
}
if (pin_type(pin_id) != PinType::SINK) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Invalid pin type found in net sinks");
}
}
if (pin_id) {
//Verify the cross reference if the pin_id is valid (i.e. a sink or a valid driver)
if (pin_net(pin_id) != net_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Net-pin cross-reference does not match");
}
if (pin_net_index(pin_id) != pin_index) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Pin's net index cross-reference does not match actual net index");
}
//We only record valid seen pins since we may see multiple undriven nets with invalid IDs
++seen_pin_ids[pin_id];
}
}
}
//Check that we have neither orphaned pins (i.e. that aren't referenced by a port)
//nor shared pins (i.e. referenced by multiple ports)
auto is_one = [](unsigned val) {
return val == 1;
};
if (!std::all_of(seen_pin_ids.begin(), seen_pin_ids.end(), is_one)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Found pins referenced by zero or multiple nets");
}
if (std::accumulate(seen_pin_ids.begin(), seen_pin_ids.end(), 0u) != pin_ids_.size()) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Found orphaned pins (not referenced by a net)");
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::validate_string_refs() const {
for (const auto& str_id : block_names_) {
if (!valid_string_id(str_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Invalid block name string reference");
}
}
for (const auto& str_id : port_names_) {
if (!valid_string_id(str_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Invalid port name string reference");
}
}
for (const auto& str_id : net_names_) {
if (!valid_string_id(str_id)) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Invalid net name string reference");
}
}
return true;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
bool Netlist<BlockId, PortId, PinId, NetId>::verify_block_invariants() const {
for (auto blk_id : blocks()) {
//Check block type
//Find any connected clock
NetId clk_net_id;
for (auto pin_id : block_clock_pins(blk_id)) {
if (pin_id) {
clk_net_id = pin_net(pin_id);
break;
}
}
if (block_is_combinational(blk_id)) {
//Non-sequential types must not have a clock
if (clk_net_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block '%s' is a non-sequential type but has a clock '%s'",
block_name(blk_id).c_str(), net_name(clk_net_id).c_str());
}
} else {
//Sequential types must have a clock
if (!clk_net_id) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block '%s' is sequential type but has no clock",
block_name(blk_id).c_str());
}
}
//Check that block pins and ports are consistent (i.e. same number of pins)
size_t num_block_pins = block_pins(blk_id).size();
size_t total_block_port_pins = 0;
for (auto port_id : block_ports(blk_id)) {
total_block_port_pins += port_pins(port_id).size();
}
if (num_block_pins != total_block_port_pins) {
VPR_FATAL_ERROR(VPR_ERROR_NETLIST, "Block pins and port pins do not match on block '%s'",
block_name(blk_id).c_str());
}
}
return true;
}
/*
*
* Internal utilities
*
*/
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::StringId Netlist<BlockId, PortId, PinId, NetId>::find_string(const std::string& str) const {
auto iter = string_to_string_id_.find(str);
if (iter != string_to_string_id_.end()) {
StringId str_id = iter->second;
VTR_ASSERT_SAFE(str_id);
VTR_ASSERT_SAFE(strings_[str_id] == str);
return str_id;
} else {
return StringId::INVALID();
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
BlockId Netlist<BlockId, PortId, PinId, NetId>::find_block(const typename Netlist<BlockId, PortId, PinId, NetId>::StringId name_id) const {
VTR_ASSERT_SAFE(valid_string_id(name_id));
auto iter = block_name_to_block_id_.find(name_id);
if (iter != block_name_to_block_id_.end()) {
BlockId blk_id = *iter;
//Check post-conditions
if (blk_id) {
VTR_ASSERT_SAFE(valid_block_id(blk_id));
VTR_ASSERT_SAFE(block_names_[blk_id] == name_id);
}
return blk_id;
} else {
return BlockId::INVALID();
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::associate_pin_with_block(const PinId pin_id, const PortType type, const BlockId blk_id) {
//Get an interator pointing to where we want to insert
pin_iterator iter;
if (type == PortType::INPUT) {
iter = block_input_pins(blk_id).end();
++block_num_input_pins_[blk_id];
} else if (type == PortType::OUTPUT) {
iter = block_output_pins(blk_id).end();
++block_num_output_pins_[blk_id];
} else {
VTR_ASSERT(type == PortType::CLOCK);
iter = block_clock_pins(blk_id).end();
++block_num_clock_pins_[blk_id];
}
//Insert the element just before iter
auto new_iter = block_pins_[blk_id].insert(iter, pin_id);
//Inserted value should be the last valid range element
if (type == PortType::INPUT) {
VTR_ASSERT(new_iter + 1 == block_input_pins(blk_id).end());
VTR_ASSERT(new_iter + 1 == block_output_pins(blk_id).begin());
} else if (type == PortType::OUTPUT) {
VTR_ASSERT(new_iter + 1 == block_output_pins(blk_id).end());
VTR_ASSERT(new_iter + 1 == block_clock_pins(blk_id).begin());
} else {
VTR_ASSERT(type == PortType::CLOCK);
VTR_ASSERT(new_iter + 1 == block_clock_pins(blk_id).end());
VTR_ASSERT(new_iter + 1 == block_pins(blk_id).end());
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
int Netlist<BlockId, PortId, PinId, NetId>::associate_pin_with_net(const PinId pin_id, const PinType type, const NetId net_id) {
//Add the pin to the net
if (type == PinType::DRIVER) {
VTR_ASSERT_MSG(net_pins_[net_id].size() > 0, "Must be space for net's pin");
VTR_ASSERT_MSG(net_pins_[net_id][0] == PinId::INVALID(), "Must be no existing net driver");
net_pins_[net_id][0] = pin_id; //Set driver
return 0; //Driver always at index 0
} else {
VTR_ASSERT(type == PinType::SINK);
net_pins_[net_id].emplace_back(pin_id); //Add sink
return net_pins_[net_id].size() - 1; //Index of inserted pin
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::associate_pin_with_port(const PinId pin_id, const PortId port_id) {
//A ports pins are stored in ascending order of port bit index, to allow for fast look-ups.
//As a result we need to ensure that even if pins are created out-of-order (with respect
//to port bit) they are still inserted in the port in sorted order.
//
//As a result we find the correct position using std::lower_bound and insert there.
//Note that in the typical case (when port pins are created in order) this will be
//the end of the port's pins and the insertion will be efficient. Only in the (more
//unusual) case where the pins are created out-of-order will we need to do a slow
//insert in the middle of the vector.
auto port_bit_cmp = [&](const PinId pin, BitIndex bit_index) {
return pin_port_bit(pin) < bit_index;
};
auto iter = std::lower_bound(port_pins_[port_id].begin(), port_pins_[port_id].end(), pin_port_bit(pin_id), port_bit_cmp);
port_pins_[port_id].insert(iter, pin_id);
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
void Netlist<BlockId, PortId, PinId, NetId>::associate_port_with_block(const PortId port_id, const PortType type, const BlockId blk_id) {
//Associate the port with the blocks inputs/outputs/clocks
//Get an interator pointing to where we want to insert
port_iterator iter;
if (type == PortType::INPUT) {
iter = block_input_ports(blk_id).end();
++block_num_input_ports_[blk_id];
} else if (type == PortType::OUTPUT) {
iter = block_output_ports(blk_id).end();
++block_num_output_ports_[blk_id];
} else {
VTR_ASSERT(type == PortType::CLOCK);
iter = block_clock_ports(blk_id).end();
++block_num_clock_ports_[blk_id];
}
//Insert the element just before iter
auto new_iter = block_ports_[blk_id].insert(iter, port_id);
//Inserted value should be the last valid range element
if (type == PortType::INPUT) {
VTR_ASSERT(new_iter + 1 == block_input_ports(blk_id).end());
VTR_ASSERT(new_iter + 1 == block_output_ports(blk_id).begin());
} else if (type == PortType::OUTPUT) {
VTR_ASSERT(new_iter + 1 == block_output_ports(blk_id).end());
VTR_ASSERT(new_iter + 1 == block_clock_ports(blk_id).begin());
} else {
VTR_ASSERT(type == PortType::CLOCK);
VTR_ASSERT(new_iter + 1 == block_clock_ports(blk_id).end());
VTR_ASSERT(new_iter + 1 == block_ports(blk_id).end());
}
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
typename Netlist<BlockId, PortId, PinId, NetId>::StringId Netlist<BlockId, PortId, PinId, NetId>::create_string(const std::string& str) {
StringId str_id = find_string(str);
if (!str_id) {
//Not found, create
//Reserve an id
str_id = StringId(string_ids_.size());
string_ids_.push_back(str_id);
//Store the reverse look-up
auto key = str;
string_to_string_id_[key] = str_id;
//Initialize the data
strings_.emplace_back(str);
}
//Check post-conditions: sizes
VTR_ASSERT(string_to_string_id_.size() == string_ids_.size());
VTR_ASSERT(strings_.size() == string_ids_.size());
//Check post-conditions: values
VTR_ASSERT(strings_[str_id] == str);
VTR_ASSERT_SAFE(find_string(str) == str_id);
return str_id;
}
template<typename BlockId, typename PortId, typename PinId, typename NetId>
NetId Netlist<BlockId, PortId, PinId, NetId>::find_net(const typename Netlist<BlockId, PortId, PinId, NetId>::StringId name_id) const {
VTR_ASSERT_SAFE(valid_string_id(name_id));
auto iter = net_name_to_net_id_.find(name_id);
if (iter != net_name_to_net_id_.end()) {
NetId net_id = *iter;
if (net_id) {
//Check post-conditions
VTR_ASSERT_SAFE(valid_net_id(net_id));
VTR_ASSERT_SAFE(net_names_[net_id] == name_id);
}
return net_id;
} else {
return NetId::INVALID();
}
}