/****************************************************************************** * This file introduces a data structure to store fabric-dependent bitstream *information * * General concept * --------------- * The idea is to create a unified data structure that stores the sequence of *configuration bit in the architecture bitstream database as well as the *information (such as address of each bit) required by a specific configuration *protocol * * Cross-reference * --------------- * By using the link between ArchBitstreamManager and FabricBitstream, * we can build a sequence of configuration bits to fit different configuration *protocols. * * +----------------------+ +-------------------+ * | | ConfigBitId | | * | ArchBitstreamManager |---------------->| FabricBitstream | * | | | | * +----------------------+ +-------------------+ * * Restrictions: * 1. Each block inside BitstreamManager should have only 1 parent block * and multiple child block * 2. Each bit inside BitstreamManager should have only 1 parent block * ******************************************************************************/ #ifndef FABRIC_BITSTREAM_H #define FABRIC_BITSTREAM_H #include #include #include #include "bitstream_manager_fwd.h" #include "fabric_bitstream_fwd.h" #include "vtr_vector.h" /* begin namespace openfpga */ namespace openfpga { class FabricBitstream { public: /* Type implementations */ /* * This class (forward delcared above) is a template used to represent a * lazily calculated iterator of the specified ID type. The key assumption * made is that the ID space is contiguous and can be walked by incrementing * the underlying ID value. To account for invalid IDs, it keeps a reference * to the invalid ID set and returns ID::INVALID() for ID values in the set. * * It is used to lazily create an iteration range (e.g. as returned by * RRGraph::edges() RRGraph::nodes()) just based on the count of allocated * elements (i.e. RRGraph::num_nodes_ or RRGraph::num_edges_), and the set of * any invalid IDs (i.e. RRGraph::invalid_node_ids_, * RRGraph::invalid_edge_ids_). */ template class lazy_id_iterator : public std::iterator { public: // Since we pass ID as a template to std::iterator we need to use an // explicit 'typename' to bring the value_type and iterator names into scope typedef typename std::iterator::value_type value_type; typedef typename std::iterator::iterator iterator; lazy_id_iterator(value_type init, const std::unordered_set& invalid_ids) : value_(init), invalid_ids_(invalid_ids) {} // Advance to the next ID value iterator operator++() { value_ = ID(size_t(value_) + 1); return *this; } // Advance to the previous ID value iterator operator--() { value_ = ID(size_t(value_) - 1); return *this; } // Dereference the iterator value_type operator*() const { return (invalid_ids_.count(value_)) ? ID::INVALID() : value_; } friend bool operator==(const lazy_id_iterator lhs, const lazy_id_iterator rhs) { return lhs.value_ == rhs.value_; } friend bool operator!=(const lazy_id_iterator lhs, const lazy_id_iterator rhs) { return !(lhs == rhs); } private: value_type value_; const std::unordered_set& invalid_ids_; }; public: /* Types and ranges */ // Lazy iterator utility forward declaration template class lazy_id_iterator; typedef lazy_id_iterator fabric_bit_iterator; typedef lazy_id_iterator fabric_bit_region_iterator; typedef vtr::Range fabric_bit_range; typedef vtr::Range fabric_bit_region_range; public: /* Public constructor */ FabricBitstream(); public: /* Public aggregators */ /* Find all the configuration bits */ size_t num_bits() const; fabric_bit_range bits() const; /* Find all the configuration regions */ size_t num_regions() const; fabric_bit_region_range regions() const; std::vector region_bits( const FabricBitRegionId& region_id) const; public: /* Public Accessors */ /* Find the configuration bit id in architecture bitstream database */ ConfigBitId config_bit(const FabricBitId& bit_id) const; /* Find the address of bitstream */ std::vector bit_address(const FabricBitId& bit_id) const; std::vector bit_bl_address(const FabricBitId& bit_id) const; std::vector bit_wl_address(const FabricBitId& bit_id) const; /* Find the data-in of bitstream */ char bit_din(const FabricBitId& bit_id) const; /* Check if address data is accessible or not*/ bool use_address() const; bool use_wl_address() const; public: /* Public Mutators */ /* Reserve config bits */ void reserve_bits(const size_t& num_bits); /* Add a new configuration bit to the bitstream manager */ FabricBitId add_bit(const ConfigBitId& config_bit_id); void set_bit_address(const FabricBitId& bit_id, const std::vector& address, const bool& tolerant_short_address = false); void set_bit_bl_address(const FabricBitId& bit_id, const std::vector& address, const bool& tolerant_short_address = false); void set_bit_wl_address(const FabricBitId& bit_id, const std::vector& address, const bool& tolerant_short_address = false); void set_bit_din(const FabricBitId& bit_id, const char& din); /* Reserve regions */ void reserve_regions(const size_t& num_regions); /* Add a new configuration region */ FabricBitRegionId add_region(); void add_bit_to_region(const FabricBitRegionId& region_id, const FabricBitId& bit_id); /* Reserve bits by region */ void reverse_region_bits(const FabricBitRegionId& region_id); /* Reverse bit sequence of the fabric bitstream * This is required by configuration chain protocol */ void reverse(); /* Enable the use of address-related data * When this is enabled, data allocation will be applied to these data * and users can access/modify the data * Otherwise, it will NOT be allocated and accessible. * * This function is only applicable before any bits are added */ void set_use_address(const bool& enable); void set_address_length(const size_t& length); void set_bl_address_length(const size_t& length); /* Enable the use of WL-address related data * Same priniciple as the set_use_address() */ void set_use_wl_address(const bool& enable); void set_wl_address_length(const size_t& length); public: /* Public Validators */ bool valid_bit_id(const FabricBitId& bit_id) const; bool valid_region_id(const FabricBitRegionId& bit_id) const; private: /* Private APIs */ uint64_t encode_address_1bits(const std::vector& address) const; uint64_t encode_address_xbits(const std::vector& address) const; std::vector decode_address_bits(const size_t& bit1, const size_t& bitx, const size_t& addr_len) const; private: /* Internal data */ /* Unique id of a region in the Bitstream */ size_t num_regions_; std::unordered_set invalid_region_ids_; vtr::vector> region_bit_ids_; /* Unique id of a bit in the Bitstream */ size_t num_bits_; std::unordered_set invalid_bit_ids_; vtr::vector config_bit_ids_; /* Flags to indicate if the addresses and din should be enabled */ bool use_address_; bool use_wl_address_; size_t address_length_; size_t wl_address_length_; /* Address bits: this is designed for memory decoders * Here we store the encoded format of the address, and decoded to binary * format which can be loaded to the configuration protocol directly * * Encoding strategy is as follows: * - An address bit which may contain '0', '1', 'x'. For example * 101x1 * - The string can be encoded into two integer numbers: * - bit-one number: which encodes the '0' and '1' bits into a number. For * example, 101x1 -> 10101 -> 21 * - bit-x number: which encodes the 'x' bits into a number. For example, * 101x1 -> 00010 -> 2 * * Note that when the length of address vector is more than 64, we use * multiple 64-bit data to store the encoded values */ vtr::vector> bit_address_1bits_; vtr::vector> bit_address_xbits_; vtr::vector> bit_wl_address_1bits_; vtr::vector> bit_wl_address_xbits_; /* Data input (Din) bits: this is designed for memory decoders */ vtr::vector bit_dins_; }; } /* end namespace openfpga */ #endif