| #ifndef LIBTRELLIS_BITDATABASE_HPP | |
| #define LIBTRELLIS_BITDATABASE_HPP | |
| #include <vector> | |
| #include <map> | |
| #include <string> | |
| #include <cstdint> | |
| #include <boost/optional.hpp> | |
| #include <mutex> | |
| #include <boost/thread/shared_mutex.hpp> | |
| #include <atomic> | |
| #include <set> | |
| #include <unordered_set> | |
| #include "Util.hpp" | |
| #ifdef FUZZ_SAFETY_CHECK | |
| #include <boost/interprocess/sync/file_lock.hpp> | |
| #endif | |
| using namespace std; | |
| namespace Trellis { | |
| /* | |
| The BitDatabase keeps track of what each bit in a tile does. Unlike other databases, this database is mutable from | |
| within libtrellis, for use during fuzzing. | |
| */ | |
| // A single configuration bit, given by its offset inside the tile, | |
| // and whether or not it is inverted | |
| struct ConfigBit | |
| { | |
| int frame; | |
| int bit; | |
| bool inv = false; | |
| inline bool operator==(const ConfigBit &other) const | |
| { | |
| return (frame == other.frame) && (bit == other.bit) && (inv == other.inv); | |
| } | |
| }; | |
| inline bool operator<(const ConfigBit &a, const ConfigBit &b) | |
| { | |
| if (a.frame < b.frame) { | |
| return true; | |
| } else if (a.frame > b.frame) { | |
| return false; | |
| } else { | |
| if (a.bit < b.bit) { | |
| return true; | |
| } else if (a.bit > b.bit) { | |
| return false; | |
| } else { | |
| return a.inv < b.inv; | |
| } | |
| } | |
| } | |
| } | |
| namespace std { | |
| // Hash function for ConfigBit | |
| template<> | |
| struct hash<Trellis::ConfigBit> | |
| { | |
| public: | |
| inline size_t operator()(const Trellis::ConfigBit &bit) const | |
| { | |
| hash<int> hash_i_fn; | |
| hash<bool> hash_b_fn; | |
| return hash_i_fn(bit.frame) + hash_i_fn(bit.bit) + hash_b_fn(bit.inv); | |
| } | |
| }; | |
| } | |
| namespace Trellis { | |
| typedef unordered_set<ConfigBit> BitSet; | |
| // Write a configuration bit to string | |
| inline string to_string(ConfigBit b) | |
| { | |
| ostringstream ss; | |
| if (b.inv) ss << "!"; | |
| ss << "F" << b.frame; | |
| ss << "B" << b.bit; | |
| return ss.str(); | |
| } | |
| // Read a configuration bit from a string | |
| ConfigBit cbit_from_str(const string &s); | |
| class CRAMView; | |
| struct ChangedBit; | |
| typedef vector<ChangedBit> CRAMDelta; | |
| // A BitGroup is a list of configuration bits that correspond to a given setting | |
| struct BitGroup | |
| { | |
| // Create an empty BitGroup | |
| BitGroup(); | |
| // Create a BitGroup from a delta. | |
| // Delta should be calculated as (with feature) - (without feature) | |
| explicit BitGroup(const CRAMDelta &delta); | |
| set<ConfigBit> bits; | |
| // Return true if the BitGroup is set in a tile | |
| bool match(const CRAMView &tile) const; | |
| // Update a coverage set with the bitgroup | |
| void add_coverage(BitSet &known_bits, bool value = true) const; | |
| // Set the BitGroup in a tile | |
| void set_group(CRAMView &tile) const; | |
| // Clear the BitGroup in a tile | |
| void clear_group(CRAMView &tile) const; | |
| inline bool operator==(const BitGroup &other) const | |
| { | |
| return bits == other.bits; | |
| } | |
| }; | |
| // Write BitGroup to output | |
| ostream &operator<<(ostream &out, const BitGroup &bits); | |
| // Read a BitGroup from input (until end of line) | |
| istream &operator>>(istream &out, BitGroup &bits); | |
| // An arc is a configurable connection between two nodes, defined within a mux | |
| struct ArcData | |
| { | |
| string source; | |
| string sink; | |
| BitGroup bits; | |
| inline bool operator==(const ArcData &other) const | |
| { | |
| return (source == other.source) && (sink == other.sink) && (bits == other.bits); | |
| } | |
| }; | |
| // A mux specifies all the possible source node arcs driving a sink node | |
| struct MuxBits | |
| { | |
| string sink; | |
| map<string, ArcData> arcs; | |
| // Get a list of sources for the mux | |
| vector<string> get_sources() const; | |
| // Work out which connection inside the mux, if any, is made inside a tile | |
| boost::optional<string> | |
| get_driver(const CRAMView &tile, boost::optional<BitSet &> coverage = boost::optional<BitSet &>()) const; | |
| // Set the driver to a given value inside the tile | |
| void set_driver(CRAMView &tile, const string &driver) const; | |
| inline bool operator==(const MuxBits &other) const | |
| { | |
| return (sink == other.sink) && (arcs == other.arcs); | |
| } | |
| }; | |
| // Write mux database entry to output | |
| ostream &operator<<(ostream &out, const MuxBits &mux); | |
| // Read mux database entry (excluding .mux token) from input | |
| istream &operator>>(istream &in, MuxBits &mux); | |
| // There are three types of non-routing config setting in the database | |
| // word : a multibit setting, such as LUT initialisation | |
| // simple: a single on/off setting, a special case of the above | |
| // enum : a setting with several different textual values, such as an IO type | |
| struct WordSettingBits | |
| { | |
| string name; | |
| vector<BitGroup> bits; | |
| vector<bool> defval; | |
| // Return the word value in a tile, returning empty if equal to the default | |
| boost::optional<vector<bool>> | |
| get_value(const CRAMView &tile, boost::optional<BitSet &> coverage = boost::optional<BitSet &>()) const; | |
| // Set the word value in a tile | |
| void set_value(CRAMView &tile, const vector<bool> &value) const; | |
| inline bool operator==(const WordSettingBits &other) const | |
| { | |
| return (name == other.name) && (bits == other.bits) && (defval == other.defval); | |
| } | |
| }; | |
| // Write config word setting bits to output | |
| ostream &operator<<(ostream &out, const WordSettingBits &ws); | |
| // Read config word database entry (excluding .config token) from input | |
| istream &operator>>(istream &out, WordSettingBits &ws); | |
| struct EnumSettingBits | |
| { | |
| string name; | |
| map<string, BitGroup> options; | |
| boost::optional<string> defval; | |
| // Needed for Python | |
| void set_defval(string val); | |
| string get_defval() const; | |
| vector<string> get_options() const; | |
| // Get the value of the enumeration, returning empty if not set or set to default, if default is non-empty | |
| boost::optional<string> | |
| get_value(const CRAMView &tile, boost::optional<BitSet &> coverage = boost::optional<BitSet &>()) const; | |
| // Set the value of the enumeration in a tile | |
| void set_value(CRAMView &tile, const string &value) const; | |
| inline bool operator==(const EnumSettingBits &other) const | |
| { | |
| return (name == other.name) && (options == other.options) && (defval == other.defval); | |
| } | |
| }; | |
| // Write config enum bits to output | |
| ostream &operator<<(ostream &out, const EnumSettingBits &es); | |
| // Read config enum bits database entry (excluding .config_enum token) from input | |
| istream &operator>>(istream &out, EnumSettingBits &es); | |
| // A fixed connection inside a tile | |
| struct FixedConnection | |
| { | |
| string source; | |
| string sink; | |
| inline bool operator==(const FixedConnection &other) const | |
| { | |
| return (source == other.source) && (sink == other.sink); | |
| } | |
| }; | |
| inline bool operator<(const FixedConnection &a, const FixedConnection &b) | |
| { | |
| if (a.sink < b.sink) { | |
| return true; | |
| } else if (a.sink > b.sink) { | |
| return false; | |
| } else { | |
| return a.source < b.source; | |
| } | |
| } | |
| // Write fixed connection to output | |
| ostream &operator<<(ostream &out, const FixedConnection &es); | |
| // Read fixed connection from input | |
| istream &operator>>(istream &out, FixedConnection &es); | |
| struct TileConfig; | |
| struct TileLocator; | |
| struct TileInfo; | |
| class RoutingGraph; | |
| class TileBitDatabase | |
| { | |
| public: | |
| // Access functions | |
| // Convert TileConfigs to and from actual Tile CRAM | |
| void config_to_tile_cram(const TileConfig &cfg, CRAMView &tile, bool is_tilegroup = false, set<string> *tg_matches = nullptr) const; | |
| TileConfig tile_cram_to_config(const CRAMView &tile) const; | |
| // All these functions are designed to be thread safe during fuzzing and database modification | |
| // Maybe we should have faster unsafe versions too, as that will be the majority of the use cases? | |
| vector<string> get_sinks() const; | |
| MuxBits get_mux_data_for_sink(const string &sink) const; | |
| vector<string> get_settings_words() const; | |
| WordSettingBits get_data_for_setword(const string &name) const; | |
| vector<string> get_settings_enums() const; | |
| EnumSettingBits get_data_for_enum(const string &name) const; | |
| vector<FixedConnection> get_fixed_conns() const; | |
| // TODO: function to get routing graph of tile | |
| // Get a list of wires downhill in the tile of a given wire | |
| // Returns pair<wire, configurable> | |
| vector<pair<string, bool>> get_downhill_wires(const string &wire) const; | |
| // Add the bit database for a tile to the routing graph | |
| void add_routing(const TileInfo &tile, RoutingGraph &graph) const; | |
| // Add relevant items to the database | |
| void add_mux_arc(const ArcData &arc); | |
| void add_setting_word(const WordSettingBits &wsb); | |
| void add_setting_enum(const EnumSettingBits &esb); | |
| void add_fixed_conn(const FixedConnection &conn); | |
| void remove_fixed_sink(const string &sink); | |
| void remove_setting_enum(const string &enum_name); | |
| void remove_setting_word(const string &word_name); | |
| // Save the bit database to file | |
| void save(); | |
| // Function to obtain the singleton BitDatabase for a given tile | |
| friend shared_ptr<TileBitDatabase> get_tile_bitdata(const TileLocator &tile); | |
| // This should not be used, but is required for PyTrellis | |
| TileBitDatabase(const TileBitDatabase &other); | |
| ~TileBitDatabase(); | |
| private: | |
| explicit TileBitDatabase(const string &filename); | |
| mutable boost::shared_mutex db_mutex; | |
| atomic<bool> dirty{false}; | |
| map<string, MuxBits> muxes; | |
| map<string, WordSettingBits> words; | |
| map<string, EnumSettingBits> enums; | |
| map<string, set<FixedConnection>> fixed_conns; | |
| string filename; | |
| void load(); | |
| #ifdef FUZZ_SAFETY_CHECK | |
| boost::interprocess::file_lock ip_db_lock; | |
| #endif | |
| }; | |
| // Represents a conflict while adding something to the database | |
| class DatabaseConflictError : public runtime_error | |
| { | |
| public: | |
| explicit DatabaseConflictError(const string &desc); | |
| }; | |
| } | |
| #endif //LIBTRELLIS_BITDATABASE_HPP |