mirror of https://github.com/YosysHQ/yosys.git
Compare commits
39 Commits
fe8eec5568
...
78361c051b
Author | SHA1 | Date |
---|---|---|
Emil J | 78361c051b | |
George Rennie | 18b616578a | |
Emil J. Tywoniak | e0285101c2 | |
Emil J | 5b6baa3ef1 | |
Martin Povišer | 53a4ec375b | |
George Rennie | 9043dc0ad6 | |
Emil J. Tywoniak | 80ec052878 | |
Emil J. Tywoniak | ae5b715107 | |
Emil J. Tywoniak | 00a0457060 | |
Emil J. Tywoniak | 4d96cbec75 | |
Emil J. Tywoniak | 983c54c75f | |
Emil J. Tywoniak | a5bc36f77e | |
Emil J. Tywoniak | e6793da9a0 | |
Emil J. Tywoniak | b08441d95c | |
Emil J. Tywoniak | 1e3f8cc630 | |
Emil J. Tywoniak | c921d85a85 | |
Emil J. Tywoniak | 45880ea7f2 | |
Emil J. Tywoniak | 5584e74128 | |
Emil J. Tywoniak | f2f7d3e5e1 | |
Emil J. Tywoniak | 7bffd7c885 | |
Emil J. Tywoniak | 795c0fb2d8 | |
Emil J | 9fcbcf3e0d | |
Krystine Sherwin | 3406b20006 | |
Emil J. Tywoniak | 5dc3cb1ccc | |
Emil J. Tywoniak | 950478edaa | |
Emil J. Tywoniak | f83d6aff97 | |
Emil J. Tywoniak | d4b7bf3ddf | |
Emil J. Tywoniak | aacb9ca55f | |
Emil J. Tywoniak | 3da1d26088 | |
Emil J. Tywoniak | f79e634d33 | |
Emil J. Tywoniak | 8f160c5ef7 | |
Emil J. Tywoniak | 0e6f631b5e | |
Emil J. Tywoniak | 7cbdc925ee | |
Emil J. Tywoniak | 4923f12f28 | |
Emil J. Tywoniak | 3e6e5be519 | |
Emil J. Tywoniak | a2880c96da | |
Emil J. Tywoniak | 0c1bc86119 | |
Emil J. Tywoniak | fdcca7c165 | |
Emil J. Tywoniak | ea0be3b403 |
|
@ -47,7 +47,7 @@ struct Scheduler {
|
|||
struct Vertex {
|
||||
T *data;
|
||||
Vertex *prev, *next;
|
||||
pool<Vertex*, hash_ptr_ops> preds, succs;
|
||||
pool<Vertex*> preds, succs;
|
||||
|
||||
Vertex() : data(NULL), prev(this), next(this) {}
|
||||
Vertex(T *data) : data(data), prev(NULL), next(NULL) {}
|
||||
|
@ -300,10 +300,10 @@ struct FlowGraph {
|
|||
};
|
||||
|
||||
std::vector<Node*> nodes;
|
||||
dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses;
|
||||
dict<Node*, pool<const RTLIL::Wire*>, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses;
|
||||
dict<const RTLIL::Wire*, pool<Node*>> wire_comb_defs, wire_sync_defs, wire_uses;
|
||||
dict<Node*, pool<const RTLIL::Wire*>> node_comb_defs, node_sync_defs, node_uses;
|
||||
dict<const RTLIL::Wire*, bool> wire_def_inlinable;
|
||||
dict<const RTLIL::Wire*, dict<Node*, bool, hash_ptr_ops>> wire_use_inlinable;
|
||||
dict<const RTLIL::Wire*, dict<Node*, bool>> wire_use_inlinable;
|
||||
dict<RTLIL::SigBit, bool> bit_has_state;
|
||||
|
||||
~FlowGraph()
|
||||
|
@ -365,7 +365,7 @@ struct FlowGraph {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*, hash_ptr_ops> &nodes) const
|
||||
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*> &nodes) const
|
||||
{
|
||||
// Can the wire be inlined, knowing that the given nodes are reachable?
|
||||
if (nodes.size() != 1)
|
||||
|
@ -3080,7 +3080,7 @@ struct CxxrtlWorker {
|
|||
// without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only
|
||||
// a single delta cycle.
|
||||
Scheduler<FlowGraph::Node> scheduler;
|
||||
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*, hash_ptr_ops> node_vertex_map;
|
||||
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*> node_vertex_map;
|
||||
for (auto node : flow.nodes)
|
||||
node_vertex_map[node] = scheduler.add(node);
|
||||
for (auto node_comb_def : flow.node_comb_defs) {
|
||||
|
@ -3095,7 +3095,7 @@ struct CxxrtlWorker {
|
|||
|
||||
// Find out whether the order includes any feedback arcs.
|
||||
std::vector<FlowGraph::Node*> node_order;
|
||||
pool<FlowGraph::Node*, hash_ptr_ops> evaluated_nodes;
|
||||
pool<FlowGraph::Node*> evaluated_nodes;
|
||||
pool<const RTLIL::Wire*> feedback_wires;
|
||||
for (auto vertex : scheduler.schedule()) {
|
||||
auto node = vertex->data;
|
||||
|
@ -3139,7 +3139,7 @@ struct CxxrtlWorker {
|
|||
}
|
||||
|
||||
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
|
||||
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
|
||||
pool<FlowGraph::Node*> worklist;
|
||||
for (auto node : flow.nodes) {
|
||||
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
|
||||
worklist.insert(node); // node evaluates a submodule
|
||||
|
@ -3159,8 +3159,8 @@ struct CxxrtlWorker {
|
|||
worklist.insert(node); // node drives public wires
|
||||
}
|
||||
}
|
||||
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> live_wires;
|
||||
pool<FlowGraph::Node*, hash_ptr_ops> live_nodes;
|
||||
dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> live_wires;
|
||||
pool<FlowGraph::Node*> live_nodes;
|
||||
while (!worklist.empty()) {
|
||||
auto node = worklist.pop();
|
||||
live_nodes.insert(node);
|
||||
|
@ -3290,15 +3290,15 @@ struct CxxrtlWorker {
|
|||
|
||||
// Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members)
|
||||
// and collect reachable wire users.
|
||||
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
|
||||
pool<FlowGraph::Node*> worklist;
|
||||
for (auto node : flow.nodes) {
|
||||
if (flow.node_comb_defs.count(node))
|
||||
for (auto wire : flow.node_comb_defs[node])
|
||||
if (debug_wire_types[wire].is_outline())
|
||||
worklist.insert(node); // node drives outline
|
||||
}
|
||||
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> debug_live_wires;
|
||||
pool<FlowGraph::Node*, hash_ptr_ops> debug_live_nodes;
|
||||
dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> debug_live_wires;
|
||||
pool<FlowGraph::Node*> debug_live_nodes;
|
||||
while (!worklist.empty()) {
|
||||
auto node = worklist.pop();
|
||||
debug_live_nodes.insert(node);
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
Hashing and associative data structures in Yosys
|
||||
------------------------------------------------
|
||||
|
||||
Container classes based on hashing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Yosys uses ``dict<K, T>`` and ``pool<T>`` as main container classes.
|
||||
``dict<K, T>`` is essentially a replacement for ``std::unordered_map<K, T>``
|
||||
and ``pool<T>`` is a replacement for ``std::unordered_set<T>``.
|
||||
The main characteristics are:
|
||||
|
||||
* ``dict<K, T>`` and ``pool<T>`` are about 2x faster than the std containers
|
||||
(though this claim hasn't been verified for over 10 years)
|
||||
|
||||
* references to elements in a ``dict<K, T>`` or ``pool<T>`` are invalidated by
|
||||
insert and remove operations (similar to ``std::vector<T>`` on ``push_back()``).
|
||||
|
||||
* some iterators are invalidated by ``erase()``. specifically, iterators
|
||||
that have not passed the erased element yet are invalidated. (``erase()``
|
||||
itself returns valid iterator to the next element.)
|
||||
|
||||
* no iterators are invalidated by ``insert()``. elements are inserted at
|
||||
``begin()``. i.e. only a new iterator that starts at ``begin()`` will see the
|
||||
inserted elements.
|
||||
|
||||
* the method ``.count(key, iterator)`` is like ``.count(key)`` but only
|
||||
considers elements that can be reached via the iterator.
|
||||
|
||||
* iterators can be compared. ``it1 < it2`` means that the position of ``t2``
|
||||
can be reached via ``t1`` but not vice versa.
|
||||
|
||||
* the method ``.sort()`` can be used to sort the elements in the container
|
||||
the container stays sorted until elements are added or removed.
|
||||
|
||||
* ``dict<K, T>`` and ``pool<T>`` will have the same order of iteration across
|
||||
all compilers, standard libraries and architectures.
|
||||
|
||||
In addition to ``dict<K, T>`` and ``pool<T>`` there is also an ``idict<K>`` that
|
||||
creates a bijective map from ``K`` to the integers. For example:
|
||||
|
||||
::
|
||||
|
||||
idict<string, 42> si;
|
||||
log("%d\n", si("hello")); // will print 42
|
||||
log("%d\n", si("world")); // will print 43
|
||||
log("%d\n", si.at("world")); // will print 43
|
||||
log("%d\n", si.at("dummy")); // will throw exception
|
||||
log("%s\n", si[42].c_str())); // will print hello
|
||||
log("%s\n", si[43].c_str())); // will print world
|
||||
log("%s\n", si[44].c_str())); // will throw exception
|
||||
|
||||
It is not possible to remove elements from an idict.
|
||||
|
||||
Finally ``mfp<K>`` implements a merge-find set data structure (aka. disjoint-set
|
||||
or union-find) over the type ``K`` ("mfp" = merge-find-promote).
|
||||
|
||||
The hash function
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The hash function generally used in Yosys is the XOR version of DJB2:
|
||||
|
||||
::
|
||||
|
||||
state = ((state << 5) + state) ^ value
|
||||
|
||||
This is an old-school hash designed to hash ASCII characters. Yosys doesn't hash
|
||||
a lot of ASCII text, but it still happens to be a local optimum due to factors
|
||||
described later.
|
||||
|
||||
Hash function quality is multi-faceted and highly dependent on what is being
|
||||
hashed. Yosys isn't concerned by any cryptographic qualities, instead the goal
|
||||
is minimizing total hashing collision risk given the data patterns within Yosys.
|
||||
In general, a good hash function typically folds values into a state accumulator
|
||||
with a mathematical function that is fast to compute and has some beneficial
|
||||
properties. One of these is the avalanche property, which demands that a small
|
||||
change such as flipping a bit or incrementing by one in the input produces a
|
||||
large, unpredictable change in the output. Additionally, the bit independence
|
||||
criterion states that any pair of output bits should change independently when
|
||||
any single input bit is inverted. These properties are important for avoiding
|
||||
hash collision on data patterns like the hash of a sequence not colliding with
|
||||
its permutation, not losing from the state the information added by hashing
|
||||
preceding elements, etc.
|
||||
|
||||
DJB2 lacks these properties. Instead, since Yosys hashes large numbers of data
|
||||
structures composed of incrementing integer IDs, Yosys abuses the predictability
|
||||
of DJB2 to get lower hash collisions, with regular nature of the hashes
|
||||
surviving through the interaction with the "modulo prime" operations in the
|
||||
associative data structures. For example, some most common objects in Yosys are
|
||||
interned ``IdString``\ s of incrementing indices or ``SigBit``\ s with bit
|
||||
offsets into wire (represented by its unique ``IdString`` name) as the typical
|
||||
case. This is what makes DJB2 a local optimum. Additionally, the ADD version of
|
||||
DJB2 (like above but with addition instead of XOR) is used to this end for some
|
||||
types, abandoning the general pattern of folding values into a state value.
|
||||
|
||||
Making a type hashable
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Let's first take a look at the external interface on a simplified level.
|
||||
Generally, to get the hash for ``T obj``, you would call the utility function
|
||||
``run_hash<T>(const T& obj)``, corresponding to ``hash_top_ops<T>::hash(obj)``,
|
||||
the default implementation of which is ``hash_ops<T>::hash_into(Hasher(), obj)``.
|
||||
``Hasher`` is the class actually implementing the hash function, hiding its
|
||||
initialized internal state, and passing it out on ``hash_t yield()`` with
|
||||
perhaps some finalization steps.
|
||||
|
||||
``hash_ops<T>`` is the star of the show. By default it pulls the ``Hasher h``
|
||||
through a ``Hasher T::hash_into(Hasher h)`` method. That's the method you have to
|
||||
implement to make a record (class or struct) type easily hashable with Yosys
|
||||
hashlib associative data structures.
|
||||
|
||||
``hash_ops<T>`` is specialized for built-in types like ``int`` or ``bool`` and
|
||||
treats pointers the same as integers, so it doesn't dereference pointers. Since
|
||||
many RTLIL data structures like ``RTLIL::Wire`` carry their own unique index
|
||||
``Hasher::hash_t hashidx_;``, there are specializations for ``hash_ops<Wire*>``
|
||||
and others in ``kernel/hashlib.h`` that actually dereference the pointers and
|
||||
call ``hash_into`` on the instances pointed to.
|
||||
|
||||
``hash_ops<T>`` is also specialized for simple compound types like
|
||||
``std::pair<U>`` by calling hash_into in sequence on its members. For flexible
|
||||
size containers like ``std::vector<U>`` the size of the container is hashed
|
||||
first. That is also how implementing hashing for a custom record data type
|
||||
should be - unless there is strong reason to do otherwise, call ``h.eat(m)`` on
|
||||
the ``Hasher h`` you have received for each member in sequence and ``return
|
||||
h;``. If you do have a strong reason to do so, look at how
|
||||
``hash_top_ops<RTLIL::SigBit>`` is implemented in ``kernel/rtlil.h``.
|
||||
|
||||
Porting plugins from the legacy interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Previously, the interface to implement hashing on custom types was just
|
||||
``unsigned int T::hash() const``. This meant hashes for members were computed
|
||||
independently and then ad-hoc combined with the hash function with some xorshift
|
||||
operations thrown in to mix bits together somewhat. A plugin can stay compatible
|
||||
with both versions prior and after the break by implementing both interfaces
|
||||
based on the existance and value of `YS_HASHING_VERSION`.
|
||||
|
||||
.. code-block:: cpp
|
||||
:caption: Example hash compatibility wrapper
|
||||
:name: hash_plugin_compat
|
||||
|
||||
#ifndef YS_HASHING_VERSION
|
||||
unsigned int T::hash() const {
|
||||
return mkhash(a, b);
|
||||
}
|
||||
#elif YS_HASHING_VERSION == 1
|
||||
Hasher T::hash_into(Hasher h) const {
|
||||
h.eat(a);
|
||||
h.eat(b);
|
||||
return h;
|
||||
}
|
||||
#else
|
||||
#error "Unsupported hashing interface"
|
||||
#endif
|
||||
|
||||
Feel free to contact Yosys maintainers with related issues.
|
|
@ -38,3 +38,4 @@ as reference to implement a similar system in any language.
|
|||
formats/index
|
||||
extending_yosys/index
|
||||
techmap
|
||||
hashing
|
||||
|
|
|
@ -90,7 +90,7 @@ struct ScopeinfoExamplePass : public Pass {
|
|||
|
||||
// Shuffle wires so this example produces more interesting outputs
|
||||
std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) {
|
||||
return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5);
|
||||
return mkhash_xorshift(run_hash(a->name) * 0x2c9277b5) < mkhash_xorshift(run_hash(b->name) * 0x2c9277b5);
|
||||
});
|
||||
|
||||
ModuleHdlnameIndex index(module);
|
||||
|
|
|
@ -177,7 +177,7 @@ namespace AST
|
|||
{
|
||||
// for dict<> and pool<>
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
// this nodes type
|
||||
AstNodeType type;
|
||||
|
|
|
@ -611,7 +611,7 @@ RTLIL::SigSpec VerificImporter::operatorInportCase(Instance *inst, const char *p
|
|||
}
|
||||
}
|
||||
|
||||
RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*, hash_ptr_ops> *any_all_nets)
|
||||
RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*> *any_all_nets)
|
||||
{
|
||||
RTLIL::SigSpec sig;
|
||||
RTLIL::Wire *dummy_wire = NULL;
|
||||
|
@ -1567,9 +1567,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
|
||||
module->fixup_ports();
|
||||
|
||||
dict<Net*, char, hash_ptr_ops> init_nets;
|
||||
pool<Net*, hash_ptr_ops> anyconst_nets, anyseq_nets;
|
||||
pool<Net*, hash_ptr_ops> allconst_nets, allseq_nets;
|
||||
dict<Net*, char> init_nets;
|
||||
pool<Net*> anyconst_nets, anyseq_nets;
|
||||
pool<Net*> allconst_nets, allseq_nets;
|
||||
any_all_nets.clear();
|
||||
|
||||
FOREACH_NET_OF_NETLIST(nl, mi, net)
|
||||
|
@ -1832,10 +1832,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
module->connect(net_map_at(net), module->Anyseq(new_verific_id(net)));
|
||||
|
||||
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
|
||||
pool<Instance*, hash_ptr_ops> sva_asserts;
|
||||
pool<Instance*, hash_ptr_ops> sva_assumes;
|
||||
pool<Instance*, hash_ptr_ops> sva_covers;
|
||||
pool<Instance*, hash_ptr_ops> sva_triggers;
|
||||
pool<Instance*> sva_asserts;
|
||||
pool<Instance*> sva_assumes;
|
||||
pool<Instance*> sva_covers;
|
||||
pool<Instance*> sva_triggers;
|
||||
#endif
|
||||
|
||||
pool<RTLIL::Cell*> past_ffs;
|
||||
|
|
|
@ -71,7 +71,7 @@ struct VerificImporter
|
|||
|
||||
std::map<Verific::Net*, RTLIL::SigBit> net_map;
|
||||
std::map<Verific::Net*, Verific::Net*> sva_posedge_map;
|
||||
pool<Verific::Net*, hash_ptr_ops> any_all_nets;
|
||||
pool<Verific::Net*> any_all_nets;
|
||||
|
||||
bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific;
|
||||
bool mode_autocover, mode_fullinit;
|
||||
|
@ -89,7 +89,7 @@ struct VerificImporter
|
|||
RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
|
||||
RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname);
|
||||
RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname);
|
||||
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*, hash_ptr_ops> *any_all_nets = nullptr);
|
||||
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*> *any_all_nets = nullptr);
|
||||
|
||||
bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name);
|
||||
bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name);
|
||||
|
|
|
@ -1051,7 +1051,7 @@ struct VerificSvaImporter
|
|||
msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
|
||||
}
|
||||
|
||||
dict<Net*, bool, hash_ptr_ops> check_expression_cache;
|
||||
dict<Net*, bool> check_expression_cache;
|
||||
|
||||
bool check_expression(Net *net, bool raise_error = false)
|
||||
{
|
||||
|
|
|
@ -37,57 +37,15 @@ And then executed using the following command:
|
|||
Yosys Data Structures
|
||||
---------------------
|
||||
|
||||
Here is a short list of data structures that you should make yourself familiar
|
||||
with before you write C++ code for Yosys. The following data structures are all
|
||||
defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used.
|
||||
1. Container classes based on hashing
|
||||
|
||||
1. Yosys Container Classes
|
||||
Yosys heavily relies on custom container data structures such as dict or pool
|
||||
defined in kernel/hashlib.h.
|
||||
dict<K, T> is essentially a replacement for std::unordered_map<K, T>
|
||||
and pool<T> is a replacement for std::unordered_set<T>. Please refer to
|
||||
docs/source/yosys_internals/hashing.rst for more information on those.
|
||||
|
||||
Yosys uses dict<K, T> and pool<T> as main container classes. dict<K, T> is
|
||||
essentially a replacement for std::unordered_map<K, T> and pool<T> is a
|
||||
replacement for std::unordered_set<T>. The main characteristics are:
|
||||
|
||||
- dict<K, T> and pool<T> are about 2x faster than the std containers
|
||||
|
||||
- references to elements in a dict<K, T> or pool<T> are invalidated by
|
||||
insert and remove operations (similar to std::vector<T> on push_back()).
|
||||
|
||||
- some iterators are invalidated by erase(). specifically, iterators
|
||||
that have not passed the erased element yet are invalidated. (erase()
|
||||
itself returns valid iterator to the next element.)
|
||||
|
||||
- no iterators are invalidated by insert(). elements are inserted at
|
||||
begin(). i.e. only a new iterator that starts at begin() will see the
|
||||
inserted elements.
|
||||
|
||||
- the method .count(key, iterator) is like .count(key) but only
|
||||
considers elements that can be reached via the iterator.
|
||||
|
||||
- iterators can be compared. it1 < it2 means that the position of t2
|
||||
can be reached via t1 but not vice versa.
|
||||
|
||||
- the method .sort() can be used to sort the elements in the container
|
||||
the container stays sorted until elements are added or removed.
|
||||
|
||||
- dict<K, T> and pool<T> will have the same order of iteration across
|
||||
all compilers, standard libraries and architectures.
|
||||
|
||||
In addition to dict<K, T> and pool<T> there is also an idict<K> that
|
||||
creates a bijective map from K to the integers. For example:
|
||||
|
||||
idict<string, 42> si;
|
||||
log("%d\n", si("hello")); // will print 42
|
||||
log("%d\n", si("world")); // will print 43
|
||||
log("%d\n", si.at("world")); // will print 43
|
||||
log("%d\n", si.at("dummy")); // will throw exception
|
||||
log("%s\n", si[42].c_str())); // will print hello
|
||||
log("%s\n", si[43].c_str())); // will print world
|
||||
log("%s\n", si[44].c_str())); // will throw exception
|
||||
|
||||
It is not possible to remove elements from an idict.
|
||||
|
||||
Finally mfp<K> implements a merge-find set data structure (aka. disjoint-set or
|
||||
union-find) over the type K ("mfp" = merge-find-promote).
|
||||
Otherwise, Yosys makes use of the following:
|
||||
|
||||
2. Standard STL data types
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct BitPatternPool
|
|||
int width;
|
||||
struct bits_t {
|
||||
std::vector<RTLIL::State> bitdata;
|
||||
mutable unsigned int cached_hash;
|
||||
mutable Hasher::hash_t cached_hash;
|
||||
bits_t(int width = 0) : bitdata(width), cached_hash(0) { }
|
||||
RTLIL::State &operator[](int index) {
|
||||
return bitdata[index];
|
||||
|
@ -39,14 +39,15 @@ struct BitPatternPool
|
|||
return bitdata[index];
|
||||
}
|
||||
bool operator==(const bits_t &other) const {
|
||||
if (hash() != other.hash())
|
||||
if (run_hash(*this) != run_hash(other))
|
||||
return false;
|
||||
return bitdata == other.bitdata;
|
||||
}
|
||||
unsigned int hash() const {
|
||||
Hasher hash_into(Hasher h) const {
|
||||
if (!cached_hash)
|
||||
cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata);
|
||||
return cached_hash;
|
||||
cached_hash = run_hash(bitdata);
|
||||
h.eat(cached_hash);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
pool<bits_t> database;
|
||||
|
|
|
@ -39,13 +39,13 @@ bool AigNode::operator==(const AigNode &other) const
|
|||
return true;
|
||||
}
|
||||
|
||||
unsigned int AigNode::hash() const
|
||||
Hasher AigNode::hash_into(Hasher h) const
|
||||
{
|
||||
unsigned int h = mkhash_init;
|
||||
h = mkhash(portname.hash(), portbit);
|
||||
h = mkhash(h, inverter);
|
||||
h = mkhash(h, left_parent);
|
||||
h = mkhash(h, right_parent);
|
||||
h.eat(portname);
|
||||
h.eat(portbit);
|
||||
h.eat(inverter);
|
||||
h.eat(left_parent);
|
||||
h.eat(right_parent);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
@ -54,9 +54,10 @@ bool Aig::operator==(const Aig &other) const
|
|||
return name == other.name;
|
||||
}
|
||||
|
||||
unsigned int Aig::hash() const
|
||||
Hasher Aig::hash_into(Hasher h) const
|
||||
{
|
||||
return hash_ops<std::string>::hash(name);
|
||||
h.eat(name);
|
||||
return h;
|
||||
}
|
||||
|
||||
struct AigMaker
|
||||
|
|
|
@ -34,7 +34,7 @@ struct AigNode
|
|||
|
||||
AigNode();
|
||||
bool operator==(const AigNode &other) const;
|
||||
unsigned int hash() const;
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
|
||||
struct Aig
|
||||
|
@ -44,7 +44,7 @@ struct Aig
|
|||
Aig(Cell *cell);
|
||||
|
||||
bool operator==(const Aig &other) const;
|
||||
unsigned int hash() const;
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/hashlib.h"
|
||||
#include "libs/sha1/sha1.h"
|
||||
#include "libs/cxxopts/include/cxxopts.hpp"
|
||||
#include <iostream>
|
||||
|
@ -276,6 +277,8 @@ int main(int argc, char **argv)
|
|||
options.add_options("developer")
|
||||
("X,trace", "enable tracing of core data structure changes. for debugging")
|
||||
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
|
||||
("hash-seed", "mix up hashing values with <seed>, for extreme optimization and testing",
|
||||
cxxopts::value<uint64_t>(), "<seed>")
|
||||
("A,abort", "will call abort() at the end of the script. for debugging")
|
||||
("x,experimental", "do not print warnings for the experimental <feature>",
|
||||
cxxopts::value<std::vector<std::string>>(), "<feature>")
|
||||
|
@ -427,6 +430,10 @@ int main(int argc, char **argv)
|
|||
if (result.count("infile")) {
|
||||
frontend_files = result["infile"].as<std::vector<std::string>>();
|
||||
}
|
||||
if (result.count("hash-seed")) {
|
||||
int seed = result["hash-seed"].as<uint64_t>();
|
||||
Hasher::set_fudge((Hasher::hash_t)seed);
|
||||
}
|
||||
|
||||
if (log_errfile == NULL) {
|
||||
log_files.push_back(stdout);
|
||||
|
|
|
@ -74,10 +74,8 @@ struct DriveBitWire
|
|||
return offset < other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash_add(wire->name.hash(), offset);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
|
||||
operator SigBit() const
|
||||
{
|
||||
|
@ -107,10 +105,8 @@ struct DriveBitPort
|
|||
return offset < other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -133,10 +129,7 @@ struct DriveBitMarker
|
|||
return offset < other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash_add(marker, offset);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
};
|
||||
|
||||
|
@ -171,10 +164,7 @@ public:
|
|||
return multiple_ == other.multiple_;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return multiple_.hash();
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
|
||||
struct DriveBit
|
||||
|
@ -362,32 +352,7 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
unsigned int inner;
|
||||
switch (type_)
|
||||
{
|
||||
case DriveType::NONE:
|
||||
inner = 0;
|
||||
break;
|
||||
case DriveType::CONSTANT:
|
||||
inner = constant_;
|
||||
break;
|
||||
case DriveType::WIRE:
|
||||
inner = wire_.hash();
|
||||
break;
|
||||
case DriveType::PORT:
|
||||
inner = port_.hash();
|
||||
break;
|
||||
case DriveType::MARKER:
|
||||
inner = marker_.hash();
|
||||
break;
|
||||
case DriveType::MULTIPLE:
|
||||
inner = multiple_.hash();
|
||||
break;
|
||||
}
|
||||
return mkhash((unsigned int)type_, inner);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
bool operator==(const DriveBit &other) const
|
||||
{
|
||||
|
@ -508,10 +473,7 @@ struct DriveChunkWire
|
|||
return offset < other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash_add(mkhash(wire->name.hash(), width), offset);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
explicit operator SigChunk() const
|
||||
{
|
||||
|
@ -569,10 +531,7 @@ struct DriveChunkPort
|
|||
return offset < other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash_add(mkhash(mkhash(cell->name.hash(), port.hash()), width), offset);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -613,10 +572,7 @@ struct DriveChunkMarker
|
|||
return offset < other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash_add(mkhash(marker, width), offset);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
|
||||
struct DriveChunkMultiple
|
||||
|
@ -656,10 +612,7 @@ public:
|
|||
return false; // TODO implement, canonicalize order
|
||||
}
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
return mkhash(width_, multiple_.hash());
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
|
||||
struct DriveChunk
|
||||
|
@ -910,32 +863,7 @@ public:
|
|||
bool try_append(DriveBit const &bit);
|
||||
bool try_append(DriveChunk const &chunk);
|
||||
|
||||
unsigned int hash() const
|
||||
{
|
||||
unsigned int inner;
|
||||
switch (type_)
|
||||
{
|
||||
case DriveType::NONE:
|
||||
inner = 0;
|
||||
break;
|
||||
case DriveType::CONSTANT:
|
||||
inner = constant_.hash();
|
||||
break;
|
||||
case DriveType::WIRE:
|
||||
inner = wire_.hash();
|
||||
break;
|
||||
case DriveType::PORT:
|
||||
inner = port_.hash();
|
||||
break;
|
||||
case DriveType::MARKER:
|
||||
inner = marker_.hash();
|
||||
break;
|
||||
case DriveType::MULTIPLE:
|
||||
inner = multiple_.hash();
|
||||
break;
|
||||
}
|
||||
return mkhash((unsigned int)type_, inner);
|
||||
}
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
bool operator==(const DriveChunk &other) const
|
||||
{
|
||||
|
@ -1138,17 +1066,20 @@ public:
|
|||
DriveSpec &operator=(DriveBitMarker const &bit) { return *this = DriveBit(bit); }
|
||||
DriveSpec &operator=(DriveBitMultiple const &bit) { return *this = DriveBit(bit); }
|
||||
|
||||
unsigned int hash() const {
|
||||
if (hash_ != 0) return hash_;
|
||||
|
||||
void updhash() const {
|
||||
if (hash_ != 0)
|
||||
return;
|
||||
pack();
|
||||
hash_ = hash_ops<std::vector<DriveChunk>>().hash(chunks_);
|
||||
hash_ = run_hash(chunks_);
|
||||
hash_ |= (hash_ == 0);
|
||||
return hash_;
|
||||
}
|
||||
|
||||
Hasher hash_into(Hasher h) const;
|
||||
|
||||
bool operator==(DriveSpec const &other) const {
|
||||
if (size() != other.size() || hash() != other.hash())
|
||||
updhash();
|
||||
other.updhash();
|
||||
if (size() != other.size() || hash_ != other.hash_)
|
||||
return false;
|
||||
return chunks() == other.chunks();
|
||||
}
|
||||
|
@ -1181,7 +1112,7 @@ private:
|
|||
bool operator==(const DriveBitId &other) const { return id == other.id; }
|
||||
bool operator!=(const DriveBitId &other) const { return id != other.id; }
|
||||
bool operator<(const DriveBitId &other) const { return id < other.id; }
|
||||
unsigned int hash() const { return id; }
|
||||
Hasher hash_into(Hasher h) const;
|
||||
};
|
||||
// Essentially a dict<DriveBitId, pool<DriveBitId>> but using less memory
|
||||
// and fewer allocations
|
||||
|
@ -1327,6 +1258,131 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
inline Hasher DriveBitWire::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(wire->name);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveBitPort::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(cell->name);
|
||||
h.eat(port);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveBitMarker::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(marker);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveBitMultiple::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(multiple_);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveBit::hash_into(Hasher h) const
|
||||
{
|
||||
switch (type_) {
|
||||
case DriveType::NONE:
|
||||
h.eat(0);
|
||||
break;
|
||||
case DriveType::CONSTANT:
|
||||
h.eat(constant_);
|
||||
break;
|
||||
case DriveType::WIRE:
|
||||
h.eat(wire_);
|
||||
break;
|
||||
case DriveType::PORT:
|
||||
h.eat(port_);
|
||||
break;
|
||||
case DriveType::MARKER:
|
||||
h.eat(marker_);
|
||||
break;
|
||||
case DriveType::MULTIPLE:
|
||||
h.eat(multiple_);
|
||||
break;
|
||||
}
|
||||
h.eat(type_);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveChunkWire::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(wire->name);
|
||||
h.eat(width);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveChunkPort::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(cell->name);
|
||||
h.eat(port);
|
||||
h.eat(width);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveChunkMarker::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(marker);
|
||||
h.eat(width);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveChunkMultiple::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(width_);
|
||||
h.eat(multiple_);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveChunk::hash_into(Hasher h) const
|
||||
{
|
||||
switch (type_) {
|
||||
case DriveType::NONE:
|
||||
h.eat(0);
|
||||
break;
|
||||
case DriveType::CONSTANT:
|
||||
h.eat(constant_);
|
||||
break;
|
||||
case DriveType::WIRE:
|
||||
h.eat(wire_);
|
||||
break;
|
||||
case DriveType::PORT:
|
||||
h.eat(port_);
|
||||
break;
|
||||
case DriveType::MARKER:
|
||||
h.eat(marker_);
|
||||
break;
|
||||
case DriveType::MULTIPLE:
|
||||
h.eat(multiple_);
|
||||
break;
|
||||
}
|
||||
h.eat(type_);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriveSpec::hash_into(Hasher h) const
|
||||
{
|
||||
updhash();
|
||||
h.eat(hash_);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline Hasher DriverMap::DriveBitId::hash_into(Hasher h) const
|
||||
{
|
||||
h.eat(id);
|
||||
return h;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace Functional {
|
|||
// returns the data width of a bitvector sort, errors out for other sorts
|
||||
int data_width() const { return std::get<1>(_v).second; }
|
||||
bool operator==(Sort const& other) const { return _v == other._v; }
|
||||
unsigned int hash() const { return mkhash(_v); }
|
||||
Hasher hash_into(Hasher h) const { h.eat(_v); return h; }
|
||||
};
|
||||
class IR;
|
||||
class Factory;
|
||||
|
@ -225,8 +225,10 @@ namespace Functional {
|
|||
const RTLIL::Const &as_const() const { return std::get<RTLIL::Const>(_extra); }
|
||||
std::pair<IdString, IdString> as_idstring_pair() const { return std::get<std::pair<IdString, IdString>>(_extra); }
|
||||
int as_int() const { return std::get<int>(_extra); }
|
||||
int hash() const {
|
||||
return mkhash((unsigned int) _fn, mkhash(_extra));
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat((unsigned int) _fn);
|
||||
h.eat(_extra);
|
||||
return h;
|
||||
}
|
||||
bool operator==(NodeData const &other) const {
|
||||
return _fn == other._fn && _extra == other._extra;
|
||||
|
|
430
kernel/hashlib.h
430
kernel/hashlib.h
|
@ -17,27 +17,62 @@
|
|||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <type_traits>
|
||||
#include <stdint.h>
|
||||
|
||||
#define YS_HASHING_VERSION 1
|
||||
|
||||
namespace hashlib {
|
||||
|
||||
/**
|
||||
* HASHING
|
||||
*
|
||||
* Also refer to docs/source/yosys_internals/hashing.rst
|
||||
*
|
||||
* The Hasher knows how to hash 32 and 64-bit integers. That's it.
|
||||
* In the future, it could be expanded to do vectors with SIMD.
|
||||
*
|
||||
* The Hasher doesn't know how to hash common standard containers
|
||||
* and compositions. However, hashlib provides centralized wrappers.
|
||||
*
|
||||
* Hashlib doesn't know how to hash silly Yosys-specific types.
|
||||
* Hashlib doesn't depend on Yosys and can be used standalone.
|
||||
* Please don't use hashlib standalone for new projects.
|
||||
* Never directly include kernel/hashlib.h in Yosys code.
|
||||
* Instead include kernel/yosys_common.h
|
||||
*
|
||||
* The hash_ops type is now always left to its default value, derived
|
||||
* from templated functions through SFINAE. Providing custom ops is
|
||||
* still supported.
|
||||
*
|
||||
* HASH TABLES
|
||||
*
|
||||
* We implement associative data structures with separate chaining.
|
||||
* Linked lists use integers into the indirection hashtable array
|
||||
* instead of pointers.
|
||||
*/
|
||||
|
||||
const int hashtable_size_trigger = 2;
|
||||
const int hashtable_size_factor = 3;
|
||||
|
||||
// The XOR version of DJB2
|
||||
inline unsigned int mkhash(unsigned int a, unsigned int b) {
|
||||
return ((a << 5) + a) ^ b;
|
||||
}
|
||||
|
||||
// traditionally 5381 is used as starting value for the djb2 hash
|
||||
const unsigned int mkhash_init = 5381;
|
||||
|
||||
// The ADD version of DJB2
|
||||
// (use this version for cache locality in b)
|
||||
inline unsigned int mkhash_add(unsigned int a, unsigned int b) {
|
||||
namespace legacy {
|
||||
inline uint32_t djb2_add(uint32_t a, uint32_t b) {
|
||||
return ((a << 5) + a) + b;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hash a type with an accumulator in a record or array context
|
||||
*/
|
||||
template<typename T>
|
||||
struct hash_ops;
|
||||
|
||||
/**
|
||||
* Hash a single instance in isolation.
|
||||
* Can have explicit specialization, but the default redirects to hash_ops
|
||||
*/
|
||||
template<typename T>
|
||||
struct hash_top_ops;
|
||||
|
||||
inline unsigned int mkhash_xorshift(unsigned int a) {
|
||||
if (sizeof(a) == 4) {
|
||||
|
@ -53,62 +88,100 @@ inline unsigned int mkhash_xorshift(unsigned int a) {
|
|||
return a;
|
||||
}
|
||||
|
||||
template<typename T> struct hash_ops {
|
||||
class HasherDJB32 {
|
||||
public:
|
||||
using hash_t = uint32_t;
|
||||
|
||||
HasherDJB32() {
|
||||
// traditionally 5381 is used as starting value for the djb2 hash
|
||||
state = 5381;
|
||||
}
|
||||
static void set_fudge(hash_t f) {
|
||||
fudge = f;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t state;
|
||||
static uint32_t fudge;
|
||||
// The XOR version of DJB2
|
||||
[[nodiscard]]
|
||||
static uint32_t djb2_xor(uint32_t a, uint32_t b) {
|
||||
uint32_t hash = ((a << 5) + a) ^ b;
|
||||
return hash;
|
||||
}
|
||||
public:
|
||||
void hash32(uint32_t i) {
|
||||
state = djb2_xor(i, state);
|
||||
state = mkhash_xorshift(fudge ^ state);
|
||||
return;
|
||||
}
|
||||
void hash64(uint64_t i) {
|
||||
state = djb2_xor((uint32_t)(i & 0xFFFFFFFFULL), state);
|
||||
state = djb2_xor((uint32_t)(i >> 32ULL), state);
|
||||
state = mkhash_xorshift(fudge ^ state);
|
||||
return;
|
||||
}
|
||||
[[nodiscard]]
|
||||
hash_t yield() {
|
||||
return (hash_t)state;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void eat(T&& t) {
|
||||
*this = hash_ops<std::remove_cv_t<std::remove_reference_t<T>>>::hash_into(std::forward<T>(t), *this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void eat(const T& t) {
|
||||
*this = hash_ops<T>::hash_into(t, *this);
|
||||
}
|
||||
|
||||
void commutative_eat(hash_t t) {
|
||||
state ^= t;
|
||||
}
|
||||
|
||||
void force(hash_t new_state) {
|
||||
state = new_state;
|
||||
}
|
||||
};
|
||||
|
||||
using Hasher = HasherDJB32;
|
||||
|
||||
template<typename T>
|
||||
struct hash_top_ops {
|
||||
static inline bool cmp(const T &a, const T &b) {
|
||||
return hash_ops<T>::cmp(a, b);
|
||||
}
|
||||
static inline Hasher hash(const T &a) {
|
||||
return hash_ops<T>::hash_into(a, Hasher());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash_ops {
|
||||
static inline bool cmp(const T &a, const T &b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(const T &a) {
|
||||
return a.hash();
|
||||
}
|
||||
};
|
||||
|
||||
struct hash_int_ops {
|
||||
template<typename T>
|
||||
static inline bool cmp(T a, T b) {
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct hash_ops<bool> : hash_int_ops
|
||||
{
|
||||
static inline unsigned int hash(bool a) {
|
||||
return a ? 1 : 0;
|
||||
}
|
||||
};
|
||||
template<> struct hash_ops<int32_t> : hash_int_ops
|
||||
{
|
||||
static inline unsigned int hash(int32_t a) {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
template<> struct hash_ops<int64_t> : hash_int_ops
|
||||
{
|
||||
static inline unsigned int hash(int64_t a) {
|
||||
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
|
||||
}
|
||||
};
|
||||
template<> struct hash_ops<uint32_t> : hash_int_ops
|
||||
{
|
||||
static inline unsigned int hash(uint32_t a) {
|
||||
return a;
|
||||
}
|
||||
};
|
||||
template<> struct hash_ops<uint64_t> : hash_int_ops
|
||||
{
|
||||
static inline unsigned int hash(uint64_t a) {
|
||||
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct hash_ops<std::string> {
|
||||
static inline bool cmp(const std::string &a, const std::string &b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(const std::string &a) {
|
||||
unsigned int v = 0;
|
||||
static inline Hasher hash_into(const T &a, Hasher h) {
|
||||
if constexpr (std::is_integral_v<T>) {
|
||||
static_assert(sizeof(T) <= sizeof(uint64_t));
|
||||
if (sizeof(T) == sizeof(uint64_t))
|
||||
h.hash64(a);
|
||||
else
|
||||
h.hash32(a);
|
||||
return h;
|
||||
} else if constexpr (std::is_enum_v<T>) {
|
||||
using u_type = std::underlying_type_t<T>;
|
||||
return hash_ops<u_type>::hash_into((u_type) a, h);
|
||||
} else if constexpr (std::is_pointer_v<T>) {
|
||||
return hash_ops<uintptr_t>::hash_into((uintptr_t) a, h);
|
||||
} else if constexpr (std::is_same_v<T, std::string>) {
|
||||
for (auto c : a)
|
||||
v = mkhash(v, c);
|
||||
return v;
|
||||
h.hash32(c);
|
||||
return h;
|
||||
} else {
|
||||
return a.hash_into(h);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -116,8 +189,10 @@ template<typename P, typename Q> struct hash_ops<std::pair<P, Q>> {
|
|||
static inline bool cmp(std::pair<P, Q> a, std::pair<P, Q> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::pair<P, Q> a) {
|
||||
return mkhash(hash_ops<P>::hash(a.first), hash_ops<Q>::hash(a.second));
|
||||
static inline Hasher hash_into(std::pair<P, Q> a, Hasher h) {
|
||||
h = hash_ops<P>::hash_into(a.first, h);
|
||||
h = hash_ops<Q>::hash_into(a.second, h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -126,13 +201,15 @@ template<typename... T> struct hash_ops<std::tuple<T...>> {
|
|||
return a == b;
|
||||
}
|
||||
template<size_t I = 0>
|
||||
static inline typename std::enable_if<I == sizeof...(T), unsigned int>::type hash(std::tuple<T...>) {
|
||||
return mkhash_init;
|
||||
static inline typename std::enable_if<I == sizeof...(T), Hasher>::type hash_into(std::tuple<T...>, Hasher h) {
|
||||
return h;
|
||||
}
|
||||
template<size_t I = 0>
|
||||
static inline typename std::enable_if<I != sizeof...(T), unsigned int>::type hash(std::tuple<T...> a) {
|
||||
static inline typename std::enable_if<I != sizeof...(T), Hasher>::type hash_into(std::tuple<T...> a, Hasher h) {
|
||||
typedef hash_ops<typename std::tuple_element<I, std::tuple<T...>>::type> element_ops_t;
|
||||
return mkhash(hash<I+1>(a), element_ops_t::hash(std::get<I>(a)));
|
||||
h = hash_into<I+1>(a, h);
|
||||
h = element_ops_t::hash_into(std::get<I>(a), h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -140,35 +217,44 @@ template<typename T> struct hash_ops<std::vector<T>> {
|
|||
static inline bool cmp(std::vector<T> a, std::vector<T> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::vector<T> a) {
|
||||
unsigned int h = mkhash_init;
|
||||
static inline Hasher hash_into(std::vector<T> a, Hasher h) {
|
||||
h.eat((uint32_t)a.size());
|
||||
for (auto k : a)
|
||||
h = mkhash(h, hash_ops<T>::hash(k));
|
||||
h.eat(k);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, size_t N> struct hash_ops<std::array<T, N>> {
|
||||
static inline bool cmp(std::array<T, N> a, std::array<T, N> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline Hasher hash_into(std::array<T, N> a, Hasher h) {
|
||||
for (const auto& k : a)
|
||||
h = hash_ops<T>::hash_into(k, h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct hash_cstr_ops {
|
||||
static inline bool cmp(const char *a, const char *b) {
|
||||
for (int i = 0; a[i] || b[i]; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
static inline unsigned int hash(const char *a) {
|
||||
unsigned int hash = mkhash_init;
|
||||
static inline Hasher hash_into(const char *a, Hasher h) {
|
||||
while (*a)
|
||||
hash = mkhash(hash, *(a++));
|
||||
return hash;
|
||||
h.hash32(*(a++));
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash_ops<char*> : hash_cstr_ops {};
|
||||
|
||||
struct hash_ptr_ops {
|
||||
static inline bool cmp(const void *a, const void *b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(const void *a) {
|
||||
return (uintptr_t)a;
|
||||
static inline Hasher hash_into(const void *a, Hasher h) {
|
||||
return hash_ops<uintptr_t>::hash_into((uintptr_t)a, h);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -177,22 +263,40 @@ struct hash_obj_ops {
|
|||
return a == b;
|
||||
}
|
||||
template<typename T>
|
||||
static inline unsigned int hash(const T *a) {
|
||||
return a ? a->hash() : 0;
|
||||
static inline Hasher hash_into(const T *a, Hasher h) {
|
||||
if (a)
|
||||
a->hash_into(h);
|
||||
else
|
||||
h.eat(0);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If you find yourself using this function, think hard
|
||||
* about if it's the right thing to do. Mixing finalized
|
||||
* hashes together with XORs or worse can destroy
|
||||
* desirable qualities of the hash function
|
||||
*/
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
Hasher::hash_t run_hash(const T& obj) {
|
||||
return hash_top_ops<T>::hash(obj).yield();
|
||||
}
|
||||
|
||||
/** Refer to docs/source/yosys_internals/hashing.rst */
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
[[deprecated]]
|
||||
inline unsigned int mkhash(const T &v) {
|
||||
return hash_ops<T>().hash(v);
|
||||
return (unsigned int) run_hash<T>(v);
|
||||
}
|
||||
|
||||
template<> struct hash_ops<std::monostate> {
|
||||
static inline bool cmp(std::monostate a, std::monostate b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::monostate) {
|
||||
return mkhash_init;
|
||||
static inline Hasher hash_into(std::monostate, Hasher h) {
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -200,9 +304,10 @@ template<typename... T> struct hash_ops<std::variant<T...>> {
|
|||
static inline bool cmp(std::variant<T...> a, std::variant<T...> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::variant<T...> a) {
|
||||
unsigned int h = std::visit([](const auto &v) { return mkhash(v); }, a);
|
||||
return mkhash(a.index(), h);
|
||||
static inline Hasher hash_into(std::variant<T...> a, Hasher h) {
|
||||
std::visit([& h](const auto &v) { h.eat(v); }, a);
|
||||
h.eat(a.index());
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -210,11 +315,12 @@ template<typename T> struct hash_ops<std::optional<T>> {
|
|||
static inline bool cmp(std::optional<T> a, std::optional<T> b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline unsigned int hash(std::optional<T> a) {
|
||||
static inline Hasher hash_into(std::optional<T> a, Hasher h) {
|
||||
if(a.has_value())
|
||||
return mkhash(*a);
|
||||
h.eat(*a);
|
||||
else
|
||||
return 0;
|
||||
h.eat(0);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -246,14 +352,13 @@ inline int hashtable_size(int min_size)
|
|||
throw std::length_error("hash table exceeded maximum size.");
|
||||
}
|
||||
|
||||
template<typename K, typename T, typename OPS = hash_ops<K>> class dict;
|
||||
template<typename K, int offset = 0, typename OPS = hash_ops<K>> class idict;
|
||||
template<typename K, typename OPS = hash_ops<K>> class pool;
|
||||
template<typename K, typename OPS = hash_ops<K>> class mfp;
|
||||
template<typename K, typename T, typename OPS = hash_top_ops<K>> class dict;
|
||||
template<typename K, int offset = 0, typename OPS = hash_top_ops<K>> class idict;
|
||||
template<typename K, typename OPS = hash_top_ops<K>> class pool;
|
||||
template<typename K, typename OPS = hash_top_ops<K>> class mfp;
|
||||
|
||||
template<typename K, typename T, typename OPS>
|
||||
class dict
|
||||
{
|
||||
class dict {
|
||||
struct entry_t
|
||||
{
|
||||
std::pair<K, T> udata;
|
||||
|
@ -277,11 +382,11 @@ class dict
|
|||
}
|
||||
#endif
|
||||
|
||||
int do_hash(const K &key) const
|
||||
Hasher::hash_t do_hash(const K &key) const
|
||||
{
|
||||
unsigned int hash = 0;
|
||||
Hasher::hash_t hash = 0;
|
||||
if (!hashtable.empty())
|
||||
hash = ops.hash(key) % (unsigned int)(hashtable.size());
|
||||
hash = ops.hash(key).yield() % (unsigned int)(hashtable.size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
@ -292,13 +397,13 @@ class dict
|
|||
|
||||
for (int i = 0; i < int(entries.size()); i++) {
|
||||
do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
|
||||
int hash = do_hash(entries[i].udata.first);
|
||||
Hasher::hash_t hash = do_hash(entries[i].udata.first);
|
||||
entries[i].next = hashtable[hash];
|
||||
hashtable[hash] = i;
|
||||
}
|
||||
}
|
||||
|
||||
int do_erase(int index, int hash)
|
||||
int do_erase(int index, Hasher::hash_t hash)
|
||||
{
|
||||
do_assert(index < int(entries.size()));
|
||||
if (hashtable.empty() || index < 0)
|
||||
|
@ -321,7 +426,7 @@ class dict
|
|||
|
||||
if (index != back_idx)
|
||||
{
|
||||
int back_hash = do_hash(entries[back_idx].udata.first);
|
||||
Hasher::hash_t back_hash = do_hash(entries[back_idx].udata.first);
|
||||
|
||||
k = hashtable[back_hash];
|
||||
do_assert(0 <= k && k < int(entries.size()));
|
||||
|
@ -347,7 +452,7 @@ class dict
|
|||
return 1;
|
||||
}
|
||||
|
||||
int do_lookup(const K &key, int &hash) const
|
||||
int do_lookup(const K &key, Hasher::hash_t &hash) const
|
||||
{
|
||||
if (hashtable.empty())
|
||||
return -1;
|
||||
|
@ -367,7 +472,7 @@ class dict
|
|||
return index;
|
||||
}
|
||||
|
||||
int do_insert(const K &key, int &hash)
|
||||
int do_insert(const K &key, Hasher::hash_t &hash)
|
||||
{
|
||||
if (hashtable.empty()) {
|
||||
entries.emplace_back(std::pair<K, T>(key, T()), -1);
|
||||
|
@ -380,7 +485,7 @@ class dict
|
|||
return entries.size() - 1;
|
||||
}
|
||||
|
||||
int do_insert(const std::pair<K, T> &value, int &hash)
|
||||
int do_insert(const std::pair<K, T> &value, Hasher::hash_t &hash)
|
||||
{
|
||||
if (hashtable.empty()) {
|
||||
entries.emplace_back(value, -1);
|
||||
|
@ -393,7 +498,7 @@ class dict
|
|||
return entries.size() - 1;
|
||||
}
|
||||
|
||||
int do_insert(std::pair<K, T> &&rvalue, int &hash)
|
||||
int do_insert(std::pair<K, T> &&rvalue, Hasher::hash_t &hash)
|
||||
{
|
||||
if (hashtable.empty()) {
|
||||
auto key = rvalue.first;
|
||||
|
@ -505,7 +610,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> insert(const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -515,7 +620,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> insert(const std::pair<K, T> &value)
|
||||
{
|
||||
int hash = do_hash(value.first);
|
||||
Hasher::hash_t hash = do_hash(value.first);
|
||||
int i = do_lookup(value.first, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -525,7 +630,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> insert(std::pair<K, T> &&rvalue)
|
||||
{
|
||||
int hash = do_hash(rvalue.first);
|
||||
Hasher::hash_t hash = do_hash(rvalue.first);
|
||||
int i = do_lookup(rvalue.first, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -535,7 +640,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> emplace(K const &key, T const &value)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -545,7 +650,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> emplace(K const &key, T &&rvalue)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -555,7 +660,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> emplace(K &&rkey, T const &value)
|
||||
{
|
||||
int hash = do_hash(rkey);
|
||||
Hasher::hash_t hash = do_hash(rkey);
|
||||
int i = do_lookup(rkey, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -565,7 +670,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> emplace(K &&rkey, T &&rvalue)
|
||||
{
|
||||
int hash = do_hash(rkey);
|
||||
Hasher::hash_t hash = do_hash(rkey);
|
||||
int i = do_lookup(rkey, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -575,35 +680,35 @@ public:
|
|||
|
||||
int erase(const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int index = do_lookup(key, hash);
|
||||
return do_erase(index, hash);
|
||||
}
|
||||
|
||||
iterator erase(iterator it)
|
||||
{
|
||||
int hash = do_hash(it->first);
|
||||
Hasher::hash_t hash = do_hash(it->first);
|
||||
do_erase(it.index, hash);
|
||||
return ++it;
|
||||
}
|
||||
|
||||
int count(const K &key) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
return i < 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
int count(const K &key, const_iterator it) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
return i < 0 || i > it.index ? 0 : 1;
|
||||
}
|
||||
|
||||
iterator find(const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
return end();
|
||||
|
@ -612,7 +717,7 @@ public:
|
|||
|
||||
const_iterator find(const K &key) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
return end();
|
||||
|
@ -621,7 +726,7 @@ public:
|
|||
|
||||
T& at(const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
throw std::out_of_range("dict::at()");
|
||||
|
@ -630,7 +735,7 @@ public:
|
|||
|
||||
const T& at(const K &key) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
throw std::out_of_range("dict::at()");
|
||||
|
@ -639,7 +744,7 @@ public:
|
|||
|
||||
const T& at(const K &key, const T &defval) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
return defval;
|
||||
|
@ -648,7 +753,7 @@ public:
|
|||
|
||||
T& operator[](const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
i = do_insert(std::pair<K, T>(key, T()), hash);
|
||||
|
@ -683,12 +788,14 @@ public:
|
|||
return !operator==(other);
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
unsigned int h = mkhash_init;
|
||||
for (auto &entry : entries) {
|
||||
h ^= hash_ops<K>::hash(entry.udata.first);
|
||||
h ^= hash_ops<T>::hash(entry.udata.second);
|
||||
Hasher hash_into(Hasher h) const {
|
||||
for (auto &it : entries) {
|
||||
Hasher entry_hash;
|
||||
entry_hash.eat(it.udata.first);
|
||||
entry_hash.eat(it.udata.second);
|
||||
h.commutative_eat(entry_hash.yield());
|
||||
}
|
||||
h.eat(entries.size());
|
||||
return h;
|
||||
}
|
||||
|
||||
|
@ -734,11 +841,11 @@ protected:
|
|||
}
|
||||
#endif
|
||||
|
||||
int do_hash(const K &key) const
|
||||
Hasher::hash_t do_hash(const K &key) const
|
||||
{
|
||||
unsigned int hash = 0;
|
||||
Hasher::hash_t hash = 0;
|
||||
if (!hashtable.empty())
|
||||
hash = ops.hash(key) % (unsigned int)(hashtable.size());
|
||||
hash = ops.hash(key).yield() % (unsigned int)(hashtable.size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
@ -749,13 +856,13 @@ protected:
|
|||
|
||||
for (int i = 0; i < int(entries.size()); i++) {
|
||||
do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
|
||||
int hash = do_hash(entries[i].udata);
|
||||
Hasher::hash_t hash = do_hash(entries[i].udata);
|
||||
entries[i].next = hashtable[hash];
|
||||
hashtable[hash] = i;
|
||||
}
|
||||
}
|
||||
|
||||
int do_erase(int index, int hash)
|
||||
int do_erase(int index, Hasher::hash_t hash)
|
||||
{
|
||||
do_assert(index < int(entries.size()));
|
||||
if (hashtable.empty() || index < 0)
|
||||
|
@ -776,7 +883,7 @@ protected:
|
|||
|
||||
if (index != back_idx)
|
||||
{
|
||||
int back_hash = do_hash(entries[back_idx].udata);
|
||||
Hasher::hash_t back_hash = do_hash(entries[back_idx].udata);
|
||||
|
||||
k = hashtable[back_hash];
|
||||
if (k == back_idx) {
|
||||
|
@ -800,7 +907,7 @@ protected:
|
|||
return 1;
|
||||
}
|
||||
|
||||
int do_lookup(const K &key, int &hash) const
|
||||
int do_lookup(const K &key, Hasher::hash_t &hash) const
|
||||
{
|
||||
if (hashtable.empty())
|
||||
return -1;
|
||||
|
@ -820,7 +927,7 @@ protected:
|
|||
return index;
|
||||
}
|
||||
|
||||
int do_insert(const K &value, int &hash)
|
||||
int do_insert(const K &value, Hasher::hash_t &hash)
|
||||
{
|
||||
if (hashtable.empty()) {
|
||||
entries.emplace_back(value, -1);
|
||||
|
@ -833,7 +940,7 @@ protected:
|
|||
return entries.size() - 1;
|
||||
}
|
||||
|
||||
int do_insert(K &&rvalue, int &hash)
|
||||
int do_insert(K &&rvalue, Hasher::hash_t &hash)
|
||||
{
|
||||
if (hashtable.empty()) {
|
||||
entries.emplace_back(std::forward<K>(rvalue), -1);
|
||||
|
@ -940,7 +1047,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> insert(const K &value)
|
||||
{
|
||||
int hash = do_hash(value);
|
||||
Hasher::hash_t hash = do_hash(value);
|
||||
int i = do_lookup(value, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -950,7 +1057,7 @@ public:
|
|||
|
||||
std::pair<iterator, bool> insert(K &&rvalue)
|
||||
{
|
||||
int hash = do_hash(rvalue);
|
||||
Hasher::hash_t hash = do_hash(rvalue);
|
||||
int i = do_lookup(rvalue, hash);
|
||||
if (i >= 0)
|
||||
return std::pair<iterator, bool>(iterator(this, i), false);
|
||||
|
@ -966,35 +1073,35 @@ public:
|
|||
|
||||
int erase(const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int index = do_lookup(key, hash);
|
||||
return do_erase(index, hash);
|
||||
}
|
||||
|
||||
iterator erase(iterator it)
|
||||
{
|
||||
int hash = do_hash(*it);
|
||||
Hasher::hash_t hash = do_hash(*it);
|
||||
do_erase(it.index, hash);
|
||||
return ++it;
|
||||
}
|
||||
|
||||
int count(const K &key) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
return i < 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
int count(const K &key, const_iterator it) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
return i < 0 || i > it.index ? 0 : 1;
|
||||
}
|
||||
|
||||
iterator find(const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
return end();
|
||||
|
@ -1003,7 +1110,7 @@ public:
|
|||
|
||||
const_iterator find(const K &key) const
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
return end();
|
||||
|
@ -1012,7 +1119,7 @@ public:
|
|||
|
||||
bool operator[](const K &key)
|
||||
{
|
||||
int hash = do_hash(key);
|
||||
Hasher::hash_t hash = do_hash(key);
|
||||
int i = do_lookup(key, hash);
|
||||
return i >= 0;
|
||||
}
|
||||
|
@ -1051,11 +1158,12 @@ public:
|
|||
return !operator==(other);
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
unsigned int hashval = mkhash_init;
|
||||
for (auto &it : entries)
|
||||
hashval ^= ops.hash(it.udata);
|
||||
return hashval;
|
||||
Hasher hash_into(Hasher h) const {
|
||||
for (auto &it : entries) {
|
||||
h.commutative_eat(ops.hash(it.udata).yield());
|
||||
}
|
||||
h.eat(entries.size());
|
||||
return h;
|
||||
}
|
||||
|
||||
void reserve(size_t n) { entries.reserve(n); }
|
||||
|
@ -1105,7 +1213,7 @@ public:
|
|||
|
||||
int operator()(const K &key)
|
||||
{
|
||||
int hash = database.do_hash(key);
|
||||
Hasher::hash_t hash = database.do_hash(key);
|
||||
int i = database.do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
i = database.do_insert(key, hash);
|
||||
|
@ -1114,7 +1222,7 @@ public:
|
|||
|
||||
int at(const K &key) const
|
||||
{
|
||||
int hash = database.do_hash(key);
|
||||
Hasher::hash_t hash = database.do_hash(key);
|
||||
int i = database.do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
throw std::out_of_range("idict::at()");
|
||||
|
@ -1123,7 +1231,7 @@ public:
|
|||
|
||||
int at(const K &key, int defval) const
|
||||
{
|
||||
int hash = database.do_hash(key);
|
||||
Hasher::hash_t hash = database.do_hash(key);
|
||||
int i = database.do_lookup(key, hash);
|
||||
if (i < 0)
|
||||
return defval;
|
||||
|
@ -1132,7 +1240,7 @@ public:
|
|||
|
||||
int count(const K &key) const
|
||||
{
|
||||
int hash = database.do_hash(key);
|
||||
Hasher::hash_t hash = database.do_hash(key);
|
||||
int i = database.do_lookup(key, hash);
|
||||
return i < 0 ? 0 : 1;
|
||||
}
|
||||
|
@ -1176,7 +1284,7 @@ class mfp
|
|||
mutable std::vector<int> parents;
|
||||
|
||||
public:
|
||||
typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
|
||||
typedef typename idict<K, 0>::const_iterator const_iterator;
|
||||
|
||||
constexpr mfp()
|
||||
{
|
||||
|
|
12
kernel/log.h
12
kernel/log.h
|
@ -363,13 +363,13 @@ void log_dump_val_worker(RTLIL::IdString v);
|
|||
void log_dump_val_worker(RTLIL::SigSpec v);
|
||||
void log_dump_val_worker(RTLIL::State v);
|
||||
|
||||
template<typename K, typename T, typename OPS> static inline void log_dump_val_worker(dict<K, T, OPS> &v);
|
||||
template<typename K, typename OPS> static inline void log_dump_val_worker(pool<K, OPS> &v);
|
||||
template<typename K, typename T> static inline void log_dump_val_worker(dict<K, T> &v);
|
||||
template<typename K> static inline void log_dump_val_worker(pool<K> &v);
|
||||
template<typename K> static inline void log_dump_val_worker(std::vector<K> &v);
|
||||
template<typename T> static inline void log_dump_val_worker(T *ptr);
|
||||
|
||||
template<typename K, typename T, typename OPS>
|
||||
static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
|
||||
template<typename K, typename T>
|
||||
static inline void log_dump_val_worker(dict<K, T> &v) {
|
||||
log("{");
|
||||
bool first = true;
|
||||
for (auto &it : v) {
|
||||
|
@ -382,8 +382,8 @@ static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
|
|||
log(" }");
|
||||
}
|
||||
|
||||
template<typename K, typename OPS>
|
||||
static inline void log_dump_val_worker(pool<K, OPS> &v) {
|
||||
template<typename K>
|
||||
static inline void log_dump_val_worker(pool<K> &v) {
|
||||
log("{");
|
||||
bool first = true;
|
||||
for (auto &it : v) {
|
||||
|
|
|
@ -48,8 +48,11 @@ struct ModIndex : public RTLIL::Monitor
|
|||
return cell == other.cell && port == other.port && offset == other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(cell->name);
|
||||
h.eat(port);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -304,6 +307,7 @@ struct ModWalker
|
|||
RTLIL::Cell *cell;
|
||||
RTLIL::IdString port;
|
||||
int offset;
|
||||
PortBit(Cell* c, IdString p, int o) : cell(c), port(p), offset(o) {}
|
||||
|
||||
bool operator<(const PortBit &other) const {
|
||||
if (cell != other.cell)
|
||||
|
@ -317,8 +321,11 @@ struct ModWalker
|
|||
return cell == other.cell && port == other.port && offset == other.offset;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(cell->name);
|
||||
h.eat(port);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -355,7 +362,7 @@ struct ModWalker
|
|||
{
|
||||
for (int i = 0; i < int(bits.size()); i++)
|
||||
if (bits[i].wire != NULL) {
|
||||
PortBit pbit = { cell, port, i };
|
||||
PortBit pbit {cell, port, i};
|
||||
if (is_output) {
|
||||
signal_drivers[bits[i]].insert(pbit);
|
||||
cell_outputs[cell].insert(bits[i]);
|
||||
|
|
|
@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN
|
|||
bool RTLIL::IdString::destruct_guard_ok = false;
|
||||
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
|
||||
std::vector<char*> RTLIL::IdString::global_id_storage_;
|
||||
dict<char*, int, hash_cstr_ops> RTLIL::IdString::global_id_index_;
|
||||
dict<char*, int> RTLIL::IdString::global_id_index_;
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
std::vector<int> RTLIL::IdString::global_refcount_storage_;
|
||||
std::vector<int> RTLIL::IdString::global_free_idx_list_;
|
||||
|
@ -4461,17 +4461,17 @@ void RTLIL::SigSpec::updhash() const
|
|||
cover("kernel.rtlil.sigspec.hash");
|
||||
that->pack();
|
||||
|
||||
that->hash_ = mkhash_init;
|
||||
Hasher h;
|
||||
for (auto &c : that->chunks_)
|
||||
if (c.wire == NULL) {
|
||||
for (auto &v : c.data)
|
||||
that->hash_ = mkhash(that->hash_, v);
|
||||
h.eat(v);
|
||||
} else {
|
||||
that->hash_ = mkhash(that->hash_, c.wire->name.index_);
|
||||
that->hash_ = mkhash(that->hash_, c.offset);
|
||||
that->hash_ = mkhash(that->hash_, c.width);
|
||||
h.eat(c.wire->name.index_);
|
||||
h.eat(c.offset);
|
||||
h.eat(c.width);
|
||||
}
|
||||
|
||||
that->hash_ = h.yield();
|
||||
if (that->hash_ == 0)
|
||||
that->hash_ = 1;
|
||||
}
|
||||
|
|
131
kernel/rtlil.h
131
kernel/rtlil.h
|
@ -76,10 +76,12 @@ namespace RTLIL
|
|||
struct SyncRule;
|
||||
struct Process;
|
||||
struct Binding;
|
||||
struct IdString;
|
||||
|
||||
typedef std::pair<SigSpec, SigSpec> SigSig;
|
||||
};
|
||||
|
||||
struct IdString
|
||||
struct RTLIL::IdString
|
||||
{
|
||||
#undef YOSYS_XTRACE_GET_PUT
|
||||
#undef YOSYS_SORT_ID_FREE_LIST
|
||||
|
@ -95,7 +97,7 @@ namespace RTLIL
|
|||
} destruct_guard;
|
||||
|
||||
static std::vector<char*> global_id_storage_;
|
||||
static dict<char*, int, hash_cstr_ops> global_id_index_;
|
||||
static dict<char*, int> global_id_index_;
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
static std::vector<int> global_refcount_storage_;
|
||||
static std::vector<int> global_free_idx_list_;
|
||||
|
@ -360,8 +362,12 @@ namespace RTLIL
|
|||
*this = IdString();
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return index_;
|
||||
Hasher hash_into(Hasher h) const { return hash_ops<int>::hash_into(index_, h); }
|
||||
|
||||
Hasher hash_top() const {
|
||||
Hasher h;
|
||||
h.force((Hasher::hash_t) index_);
|
||||
return h;
|
||||
}
|
||||
|
||||
// The following is a helper key_compare class. Instead of for example std::set<Cell*>
|
||||
|
@ -376,29 +382,48 @@ namespace RTLIL
|
|||
|
||||
// often one needs to check if a given IdString is part of a list (for example a list
|
||||
// of cell types). the following functions helps with that.
|
||||
|
||||
template<typename... Args>
|
||||
bool in(Args... args) const {
|
||||
// Credit: https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html
|
||||
bool result = false;
|
||||
(void) std::initializer_list<int>{ (result = result || in(args), 0)... };
|
||||
return result;
|
||||
return (... || in(args));
|
||||
}
|
||||
|
||||
bool in(const IdString &rhs) const { return *this == rhs; }
|
||||
bool in(const char *rhs) const { return *this == rhs; }
|
||||
bool in(const std::string &rhs) const { return *this == rhs; }
|
||||
bool in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
|
||||
inline bool in(const pool<IdString> &rhs) const;
|
||||
inline bool in(const pool<IdString> &&rhs) const;
|
||||
|
||||
bool isPublic() const { return begins_with("\\"); }
|
||||
};
|
||||
|
||||
namespace hashlib {
|
||||
template <>
|
||||
struct hash_top_ops<RTLIL::IdString> {
|
||||
static inline bool cmp(const RTLIL::IdString &a, const RTLIL::IdString &b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline Hasher hash(const RTLIL::IdString id) {
|
||||
return id.hash_top();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* How to not use these methods:
|
||||
* 1. if(celltype.in({...})) -> if(celltype.in(...))
|
||||
* 2. pool<IdString> p; ... a.in(p) -> (bool)p.count(a)
|
||||
*/
|
||||
[[deprecated]]
|
||||
inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
|
||||
[[deprecated]]
|
||||
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
|
||||
|
||||
namespace RTLIL {
|
||||
namespace ID {
|
||||
#define X(_id) extern IdString _id;
|
||||
#include "kernel/constids.inc"
|
||||
#undef X
|
||||
};
|
||||
|
||||
extern dict<std::string, std::string> constpad;
|
||||
|
||||
const pool<IdString> &builtin_ff_cell_types();
|
||||
|
@ -796,11 +821,10 @@ public:
|
|||
bv.resize(width, bv.empty() ? RTLIL::State::Sx : bv.back());
|
||||
}
|
||||
|
||||
inline unsigned int hash() const {
|
||||
unsigned int h = mkhash_init;
|
||||
|
||||
for (State b : *this)
|
||||
h = mkhash(h, b);
|
||||
inline Hasher hash_into(Hasher h) const {
|
||||
h.eat(size());
|
||||
for (auto b : *this)
|
||||
h.eat(b);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
@ -889,7 +913,20 @@ struct RTLIL::SigBit
|
|||
bool operator <(const RTLIL::SigBit &other) const;
|
||||
bool operator ==(const RTLIL::SigBit &other) const;
|
||||
bool operator !=(const RTLIL::SigBit &other) const;
|
||||
unsigned int hash() const;
|
||||
Hasher hash_into(Hasher h) const;
|
||||
Hasher hash_top() const;
|
||||
};
|
||||
|
||||
namespace hashlib {
|
||||
template <>
|
||||
struct hash_top_ops<RTLIL::SigBit> {
|
||||
static inline bool cmp(const RTLIL::SigBit &a, const RTLIL::SigBit &b) {
|
||||
return a == b;
|
||||
}
|
||||
static inline Hasher hash(const RTLIL::SigBit sb) {
|
||||
return sb.hash_top();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct RTLIL::SigSpecIterator
|
||||
|
@ -930,7 +967,7 @@ struct RTLIL::SigSpec
|
|||
{
|
||||
private:
|
||||
int width_;
|
||||
unsigned long hash_;
|
||||
Hasher::hash_t hash_;
|
||||
std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0
|
||||
std::vector<RTLIL::SigBit> bits_; // LSB at index 0
|
||||
|
||||
|
@ -971,11 +1008,6 @@ public:
|
|||
SigSpec(const std::set<RTLIL::SigBit> &bits);
|
||||
explicit SigSpec(bool bit);
|
||||
|
||||
size_t get_hash() const {
|
||||
if (!hash_) hash();
|
||||
return hash_;
|
||||
}
|
||||
|
||||
inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; }
|
||||
inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; }
|
||||
|
||||
|
@ -1082,7 +1114,7 @@ public:
|
|||
operator std::vector<RTLIL::SigBit>() const { return bits(); }
|
||||
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; }
|
||||
|
||||
unsigned int hash() const { if (!hash_) updhash(); return hash_; };
|
||||
Hasher hash_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
void check(Module *mod = nullptr) const;
|
||||
|
@ -1123,8 +1155,8 @@ struct RTLIL::Selection
|
|||
|
||||
struct RTLIL::Monitor
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
Monitor() {
|
||||
static unsigned int hashidx_count = 123456789;
|
||||
|
@ -1146,8 +1178,8 @@ struct define_map_t;
|
|||
|
||||
struct RTLIL::Design
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
pool<RTLIL::Monitor*> monitors;
|
||||
dict<std::string, std::string> scratchpad;
|
||||
|
@ -1251,8 +1283,8 @@ struct RTLIL::Design
|
|||
|
||||
struct RTLIL::Module : public RTLIL::AttrObject
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
protected:
|
||||
void add(RTLIL::Wire *wire);
|
||||
|
@ -1602,8 +1634,8 @@ void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire);
|
|||
|
||||
struct RTLIL::Wire : public RTLIL::AttrObject
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
protected:
|
||||
// use module->addWire() and module->remove() to create or destroy wires
|
||||
|
@ -1641,8 +1673,8 @@ inline int GetSize(RTLIL::Wire *wire) {
|
|||
|
||||
struct RTLIL::Memory : public RTLIL::AttrObject
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
Memory();
|
||||
|
||||
|
@ -1656,8 +1688,8 @@ struct RTLIL::Memory : public RTLIL::AttrObject
|
|||
|
||||
struct RTLIL::Cell : public RTLIL::AttrObject
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
protected:
|
||||
// use module->addCell() and module->remove() to create or destroy cells
|
||||
|
@ -1766,8 +1798,8 @@ struct RTLIL::SyncRule
|
|||
|
||||
struct RTLIL::Process : public RTLIL::AttrObject
|
||||
{
|
||||
unsigned int hashidx_;
|
||||
unsigned int hash() const { return hashidx_; }
|
||||
Hasher::hash_t hashidx_;
|
||||
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
|
||||
|
||||
protected:
|
||||
// use module->addProcess() and module->remove() to create or destroy processes
|
||||
|
@ -1811,10 +1843,25 @@ inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const {
|
|||
return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data));
|
||||
}
|
||||
|
||||
inline unsigned int RTLIL::SigBit::hash() const {
|
||||
if (wire)
|
||||
return mkhash_add(wire->name.hash(), offset);
|
||||
return data;
|
||||
inline Hasher RTLIL::SigBit::hash_into(Hasher h) const {
|
||||
if (wire) {
|
||||
h.eat(offset);
|
||||
h.eat(wire->name);
|
||||
return h;
|
||||
}
|
||||
h.eat(data);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
inline Hasher RTLIL::SigBit::hash_top() const {
|
||||
Hasher h;
|
||||
if (wire) {
|
||||
h.force(hashlib::legacy::djb2_add(wire->name.index_, offset));
|
||||
return h;
|
||||
}
|
||||
h.force(data);
|
||||
return h;
|
||||
}
|
||||
|
||||
inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const {
|
||||
|
|
|
@ -318,7 +318,7 @@ struct ModuleItem {
|
|||
Cell *cell() const { return type == Type::Cell ? static_cast<Cell *>(ptr) : nullptr; }
|
||||
|
||||
bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; }
|
||||
unsigned int hash() const { return (uintptr_t)ptr; }
|
||||
Hasher hash_into(Hasher h) const { h.eat(ptr); return h; }
|
||||
};
|
||||
|
||||
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }
|
||||
|
|
|
@ -29,7 +29,11 @@ struct SigPool
|
|||
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
|
||||
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
|
||||
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
|
||||
unsigned int hash() const { return first->name.hash() + second; }
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(first->name);
|
||||
h.eat(second);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
pool<bitDef_t> bits;
|
||||
|
@ -143,7 +147,11 @@ struct SigSet
|
|||
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
|
||||
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
|
||||
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
|
||||
unsigned int hash() const { return first->name.hash() + second; }
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(first->name);
|
||||
h.eat(second);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
dict<bitDef_t, std::set<T, Compare>> bits;
|
||||
|
|
|
@ -36,7 +36,6 @@ struct TimingInfo
|
|||
explicit NameBit(const RTLIL::SigBit &b) : name(b.wire->name), offset(b.offset) {}
|
||||
bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; }
|
||||
bool operator!=(const NameBit& nb) const { return !operator==(nb); }
|
||||
unsigned int hash() const { return mkhash_add(name.hash(), offset); }
|
||||
std::optional<SigBit> get_connection(RTLIL::Cell *cell) {
|
||||
if (!cell->hasPort(name))
|
||||
return {};
|
||||
|
@ -45,6 +44,11 @@ struct TimingInfo
|
|||
return {};
|
||||
return port[offset];
|
||||
}
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(name);
|
||||
h.eat(offset);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
struct BitBit
|
||||
{
|
||||
|
@ -52,7 +56,11 @@ struct TimingInfo
|
|||
BitBit(const NameBit &first, const NameBit &second) : first(first), second(second) {}
|
||||
BitBit(const SigBit &first, const SigBit &second) : first(first), second(second) {}
|
||||
bool operator==(const BitBit& bb) const { return bb.first == first && bb.second == second; }
|
||||
unsigned int hash() const { return mkhash_add(first.hash(), second.hash()); }
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(first);
|
||||
h.eat(second);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct ModuleTiming
|
||||
|
|
|
@ -31,17 +31,17 @@ YOSYS_NAMESPACE_BEGIN
|
|||
// A map-like container, but you can save and restore the state
|
||||
// ------------------------------------------------
|
||||
|
||||
template<typename Key, typename T, typename OPS = hash_ops<Key>>
|
||||
template<typename Key, typename T>
|
||||
struct stackmap
|
||||
{
|
||||
private:
|
||||
std::vector<dict<Key, T*, OPS>> backup_state;
|
||||
dict<Key, T, OPS> current_state;
|
||||
std::vector<dict<Key, T*>> backup_state;
|
||||
dict<Key, T> current_state;
|
||||
static T empty_tuple;
|
||||
|
||||
public:
|
||||
stackmap() { }
|
||||
stackmap(const dict<Key, T, OPS> &other) : current_state(other) { }
|
||||
stackmap(const dict<Key, T> &other) : current_state(other) { }
|
||||
|
||||
template<typename Other>
|
||||
void operator=(const Other &other)
|
||||
|
@ -94,7 +94,7 @@ public:
|
|||
current_state.erase(k);
|
||||
}
|
||||
|
||||
const dict<Key, T, OPS> &stdmap()
|
||||
const dict<Key, T> &stdmap()
|
||||
{
|
||||
return current_state;
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ public:
|
|||
// A simple class for topological sorting
|
||||
// ------------------------------------------------
|
||||
|
||||
template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> class TopoSort
|
||||
template <typename T, typename C = std::less<T>> class TopoSort
|
||||
{
|
||||
public:
|
||||
// We use this ordering of the edges in the adjacency matrix for
|
||||
|
|
|
@ -92,6 +92,7 @@ std::set<std::string> yosys_input_files, yosys_output_files;
|
|||
bool memhasher_active = false;
|
||||
uint32_t memhasher_rng = 123456;
|
||||
std::vector<void*> memhasher_store;
|
||||
uint32_t Hasher::fudge = 0;
|
||||
|
||||
std::string yosys_share_dirname;
|
||||
std::string yosys_abc_executable;
|
||||
|
|
|
@ -157,8 +157,7 @@ YOSYS_NAMESPACE_BEGIN
|
|||
// Note: All headers included in hashlib.h must be included
|
||||
// outside of YOSYS_NAMESPACE before this or bad things will happen.
|
||||
#ifdef HASHLIB_H
|
||||
# undef HASHLIB_H
|
||||
# include "kernel/hashlib.h"
|
||||
# error You've probably included hashlib.h under two namespace paths. Bad idea.
|
||||
#else
|
||||
# include "kernel/hashlib.h"
|
||||
# undef HASHLIB_H
|
||||
|
@ -176,6 +175,15 @@ using std::get;
|
|||
using std::min;
|
||||
using std::max;
|
||||
|
||||
using hashlib::Hasher;
|
||||
using hashlib::run_hash;
|
||||
using hashlib::hash_ops;
|
||||
using hashlib::mkhash_xorshift;
|
||||
using hashlib::dict;
|
||||
using hashlib::idict;
|
||||
using hashlib::pool;
|
||||
using hashlib::mfp;
|
||||
|
||||
// A primitive shared string implementation that does not
|
||||
// move its .c_str() when the object is copied or moved.
|
||||
struct shared_str {
|
||||
|
@ -186,22 +194,12 @@ struct shared_str {
|
|||
const char *c_str() const { return content->c_str(); }
|
||||
const string &str() const { return *content; }
|
||||
bool operator==(const shared_str &other) const { return *content == *other.content; }
|
||||
unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); }
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(*content);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
using hashlib::mkhash;
|
||||
using hashlib::mkhash_init;
|
||||
using hashlib::mkhash_add;
|
||||
using hashlib::mkhash_xorshift;
|
||||
using hashlib::hash_ops;
|
||||
using hashlib::hash_cstr_ops;
|
||||
using hashlib::hash_ptr_ops;
|
||||
using hashlib::hash_obj_ops;
|
||||
using hashlib::dict;
|
||||
using hashlib::idict;
|
||||
using hashlib::pool;
|
||||
using hashlib::mfp;
|
||||
|
||||
namespace RTLIL {
|
||||
struct IdString;
|
||||
struct Const;
|
||||
|
@ -370,10 +368,6 @@ RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std:
|
|||
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })()
|
||||
namespace ID = RTLIL::ID;
|
||||
|
||||
namespace hashlib {
|
||||
template<> struct hash_ops<RTLIL::State> : hash_ops<int> {};
|
||||
}
|
||||
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ struct IdPath : public std::vector<RTLIL::IdString>
|
|||
bool has_address() const { int tmp; return get_address(tmp); };
|
||||
bool get_address(int &addr) const;
|
||||
|
||||
int hash() const { return hashlib::hash_ops<std::vector<RTLIL::IdString>>::hash(*this); }
|
||||
Hasher hash_into(Hasher h) const { h.eat(*this); return h; }
|
||||
};
|
||||
|
||||
struct WitnessHierarchyItem {
|
||||
|
|
|
@ -855,7 +855,11 @@ class WClass:
|
|||
if self.hash_id != None:
|
||||
text += "\n\t\tunsigned int get_hash_py()"
|
||||
text += "\n\t\t{"
|
||||
text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";"
|
||||
suffix = f"->{self.hash_id}" if self.hash_id else f"->{self.hash_id}"
|
||||
if self.hash_id == "":
|
||||
text += f"\n\t\t\treturn run_hash(*(get_cpp_obj()));"
|
||||
else:
|
||||
text += f"\n\t\t\treturn run_hash(get_cpp_obj()->{self.hash_id});"
|
||||
text += "\n\t\t}"
|
||||
|
||||
text += "\n\t};\n"
|
||||
|
@ -956,7 +960,7 @@ class WClass:
|
|||
|
||||
sources = [
|
||||
Source("kernel/celltypes",[
|
||||
WClass("CellType", link_types.pointer, None, None, "type.hash()", True),
|
||||
WClass("CellType", link_types.pointer, None, None, "type", True),
|
||||
WClass("CellTypes", link_types.pointer, None, None, None, True)
|
||||
]
|
||||
),
|
||||
|
@ -970,23 +974,23 @@ sources = [
|
|||
]
|
||||
),
|
||||
Source("kernel/rtlil",[
|
||||
WClass("IdString", link_types.ref_copy, None, "str()", "hash()"),
|
||||
WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"),
|
||||
WClass("IdString", link_types.ref_copy, None, "str()", ""),
|
||||
WClass("Const", link_types.ref_copy, None, "as_string()", ""),
|
||||
WClass("AttrObject", link_types.ref_copy, None, None, None),
|
||||
WClass("Selection", link_types.ref_copy, None, None, None),
|
||||
WClass("Monitor", link_types.derive, None, None, None),
|
||||
WClass("CaseRule",link_types.ref_copy, None, None, None, True),
|
||||
WClass("SwitchRule",link_types.ref_copy, None, None, None, True),
|
||||
WClass("SyncRule", link_types.ref_copy, None, None, None, True),
|
||||
WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"),
|
||||
WClass("Process", link_types.ref_copy, None, "name.c_str()", "name"),
|
||||
WClass("SigChunk", link_types.ref_copy, None, None, None),
|
||||
WClass("SigBit", link_types.ref_copy, None, None, "hash()"),
|
||||
WClass("SigSpec", link_types.ref_copy, None, None, "hash()"),
|
||||
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
|
||||
WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
|
||||
WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
|
||||
WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
|
||||
WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()")
|
||||
WClass("SigBit", link_types.ref_copy, None, None, ""),
|
||||
WClass("SigSpec", link_types.ref_copy, None, None, ""),
|
||||
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
|
||||
WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
|
||||
WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
|
||||
WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
|
||||
WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "")
|
||||
]
|
||||
),
|
||||
#Source("kernel/satgen",[
|
||||
|
@ -1579,10 +1583,15 @@ class WFunction:
|
|||
return_stmt = "return " if self.ret_type.name != "void" else ""
|
||||
|
||||
text += ")\n\t\t{"
|
||||
text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
|
||||
text += f"\n\t\t\t\t{return_stmt}" + call_string
|
||||
text += "\n\t\t\telse"
|
||||
text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\")) {"
|
||||
text += "\n\t\t\t\ttry {"
|
||||
text += f"\n\t\t\t\t\t{return_stmt}" + call_string
|
||||
text += "\n\t\t\t\t} catch (boost::python::error_already_set &) {"
|
||||
text += "\n\t\t\t\t\tlog_python_exception_as_error();"
|
||||
text += "\n\t\t\t\t}"
|
||||
text += "\n\t\t\t} else {"
|
||||
text += f"\n\t\t\t\t{return_stmt}" + self.member_of.name + "::" + call_string
|
||||
text += "\n\t\t\t}"
|
||||
text += "\n\t\t}"
|
||||
|
||||
text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
|
||||
|
@ -2335,6 +2344,11 @@ USING_YOSYS_NAMESPACE
|
|||
|
||||
namespace YOSYS_PYTHON {
|
||||
|
||||
[[noreturn]] static void log_python_exception_as_error() {
|
||||
PyErr_Print();
|
||||
log_error("Python interpreter encountered an exception.\\n");
|
||||
}
|
||||
|
||||
struct YosysStatics{};
|
||||
""")
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ struct DftTagWorker {
|
|||
bool operator<(const tag_set &other) const { return index < other.index; }
|
||||
bool operator==(const tag_set &other) const { return index == other.index; }
|
||||
|
||||
unsigned int hash() const { return hash_ops<int>::hash(index); }
|
||||
Hasher hash_into(Hasher h) const { h.eat(index); return h; }
|
||||
|
||||
bool empty() const { return index == 0; }
|
||||
};
|
||||
|
|
|
@ -52,8 +52,10 @@ struct ExampleDtPass : public Pass
|
|||
return name == other.name && parameters == other.parameters;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return mkhash(name.hash(), parameters.hash());
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(name);
|
||||
h.eat(parameters);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/hashlib.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
|
|
@ -70,13 +70,13 @@ struct GraphNode {
|
|||
|
||||
pool<IdString> names_;
|
||||
dict<int, uint8_t> tags_;
|
||||
pool<GraphNode*, hash_ptr_ops> upstream_;
|
||||
pool<GraphNode*, hash_ptr_ops> downstream_;
|
||||
pool<GraphNode*> upstream_;
|
||||
pool<GraphNode*> downstream_;
|
||||
|
||||
pool<IdString> &names() { return get()->names_; }
|
||||
dict<int, uint8_t> &tags() { return get()->tags_; }
|
||||
pool<GraphNode*, hash_ptr_ops> &upstream() { return get()->upstream_; }
|
||||
pool<GraphNode*, hash_ptr_ops> &downstream() { return get()->downstream_; }
|
||||
pool<GraphNode*> &upstream() { return get()->upstream_; }
|
||||
pool<GraphNode*> &downstream() { return get()->downstream_; }
|
||||
|
||||
uint8_t tag(int index) {
|
||||
return tags().at(index, 0);
|
||||
|
@ -154,8 +154,8 @@ struct Graph {
|
|||
nodes.push_back(n);
|
||||
n->index = GetSize(nodes);
|
||||
|
||||
pool<GraphNode*, hash_ptr_ops> new_upstream;
|
||||
pool<GraphNode*, hash_ptr_ops> new_downstream;
|
||||
pool<GraphNode*> new_upstream;
|
||||
pool<GraphNode*> new_downstream;
|
||||
|
||||
for (auto g : n->upstream()) {
|
||||
if (n != (g = g->get()))
|
||||
|
@ -302,7 +302,7 @@ struct Graph {
|
|||
}
|
||||
}
|
||||
|
||||
pool<GraphNode*, hash_ptr_ops> excluded;
|
||||
pool<GraphNode*> excluded;
|
||||
|
||||
for (auto grp : config.groups)
|
||||
{
|
||||
|
@ -348,7 +348,7 @@ struct Graph {
|
|||
excluded.insert(g->get());
|
||||
|
||||
dict<Cell*, GraphNode*> cell_nodes;
|
||||
dict<SigBit, pool<GraphNode*, hash_ptr_ops>> sig_users;
|
||||
dict<SigBit, pool<GraphNode*>> sig_users;
|
||||
|
||||
for (auto cell : module->selected_cells()) {
|
||||
auto g = new GraphNode;
|
||||
|
@ -483,8 +483,8 @@ struct Graph {
|
|||
|
||||
{
|
||||
header("Any nodes with identical connections");
|
||||
typedef pair<pool<GraphNode*, hash_ptr_ops>, pool<GraphNode*, hash_ptr_ops>> node_conn_t;
|
||||
dict<node_conn_t, pool<GraphNode*, hash_ptr_ops>> nodes_by_conn;
|
||||
typedef pair<pool<GraphNode*>, pool<GraphNode*>> node_conn_t;
|
||||
dict<node_conn_t, pool<GraphNode*>> nodes_by_conn;
|
||||
for (auto g : term ? term_nodes : nonterm_nodes) {
|
||||
auto &entry = nodes_by_conn[node_conn_t(g->upstream(), g->downstream())];
|
||||
for (auto n : entry)
|
||||
|
@ -506,8 +506,8 @@ struct Graph {
|
|||
|
||||
header("Sibblings with identical tags");
|
||||
for (auto g : nonterm_nodes) {
|
||||
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
|
||||
dict<std::vector<int>, pool<GraphNode*, hash_ptr_ops>> nodes_by_tags;
|
||||
auto process_conns = [&](const pool<GraphNode*> &stream) {
|
||||
dict<std::vector<int>, pool<GraphNode*>> nodes_by_tags;
|
||||
for (auto n : stream) {
|
||||
if (n->terminal) continue;
|
||||
std::vector<int> key;
|
||||
|
@ -556,7 +556,7 @@ struct Graph {
|
|||
if (!term) {
|
||||
header("Sibblings with similar tags (strict)");
|
||||
for (auto g : nonterm_nodes) {
|
||||
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
|
||||
auto process_conns = [&](const pool<GraphNode*> &stream) {
|
||||
std::vector<GraphNode*> nodes;
|
||||
for (auto n : stream)
|
||||
if (!n->terminal) nodes.push_back(n);
|
||||
|
@ -585,7 +585,7 @@ struct Graph {
|
|||
if (!term) {
|
||||
header("Sibblings with similar tags (non-strict)");
|
||||
for (auto g : nonterm_nodes) {
|
||||
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
|
||||
auto process_conns = [&](const pool<GraphNode*> &stream) {
|
||||
std::vector<GraphNode*> nodes;
|
||||
for (auto n : stream)
|
||||
if (!n->terminal) nodes.push_back(n);
|
||||
|
@ -603,7 +603,7 @@ struct Graph {
|
|||
|
||||
{
|
||||
header("Any nodes with identical fan-in or fan-out");
|
||||
dict<pool<GraphNode*, hash_ptr_ops>, pool<GraphNode*, hash_ptr_ops>> nodes_by_conn[2];
|
||||
dict<pool<GraphNode*>, pool<GraphNode*>> nodes_by_conn[2];
|
||||
for (auto g : term ? term_nodes : nonterm_nodes) {
|
||||
auto &up_entry = nodes_by_conn[0][g->upstream()];
|
||||
auto &down_entry = nodes_by_conn[1][g->downstream()];
|
||||
|
@ -629,7 +629,7 @@ struct Graph {
|
|||
if (!term) {
|
||||
header("Sibblings with similar tags (lax)");
|
||||
for (auto g : nonterm_nodes) {
|
||||
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
|
||||
auto process_conns = [&](const pool<GraphNode*> &stream) {
|
||||
std::vector<GraphNode*> nodes;
|
||||
for (auto n : stream)
|
||||
if (!n->terminal) nodes.push_back(n);
|
||||
|
@ -720,9 +720,9 @@ struct VizWorker
|
|||
fprintf(f, "digraph \"%s\" {\n", log_id(module));
|
||||
fprintf(f, " rankdir = LR;\n");
|
||||
|
||||
dict<GraphNode*, std::vector<std::vector<std::string>>, hash_ptr_ops> extra_lines;
|
||||
dict<GraphNode*, GraphNode*, hash_ptr_ops> bypass_nodes;
|
||||
pool<GraphNode*, hash_ptr_ops> bypass_candidates;
|
||||
dict<GraphNode*, std::vector<std::vector<std::string>>> extra_lines;
|
||||
dict<GraphNode*, GraphNode*> bypass_nodes;
|
||||
pool<GraphNode*> bypass_candidates;
|
||||
|
||||
auto bypass = [&](GraphNode *g, GraphNode *n) {
|
||||
log_assert(g->terminal);
|
||||
|
|
|
@ -46,11 +46,11 @@ struct EquivStructWorker
|
|||
parameters == other.parameters && port_sizes == other.port_sizes;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
unsigned int h = mkhash_init;
|
||||
h = mkhash(h, mkhash(type));
|
||||
h = mkhash(h, mkhash(parameters));
|
||||
h = mkhash(h, mkhash(connections));
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(type);
|
||||
h.eat(parameters);
|
||||
h.eat(port_sizes);
|
||||
h.eat(connections);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -127,11 +127,10 @@ struct proc_dlatch_db_t
|
|||
return signal == other.signal && match == other.match && children == other.children;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
unsigned int h = mkhash_init;
|
||||
mkhash(h, signal.hash());
|
||||
mkhash(h, match.hash());
|
||||
for (auto i : children) mkhash(h, i);
|
||||
Hasher hash_into(Hasher h) const {
|
||||
h.eat(signal);
|
||||
h.eat(match);
|
||||
h.eat(children);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -108,8 +108,8 @@ struct SigSnippets
|
|||
|
||||
struct SnippetSwCache
|
||||
{
|
||||
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache;
|
||||
dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
|
||||
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>> full_case_bits_cache;
|
||||
dict<RTLIL::SwitchRule*, pool<int>> cache;
|
||||
const SigSnippets *snippets;
|
||||
int current_snippet;
|
||||
|
||||
|
@ -318,7 +318,7 @@ const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul
|
|||
return swcache.full_case_bits_cache.at(sw);
|
||||
}
|
||||
|
||||
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
|
||||
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool> &swpara,
|
||||
RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
|
||||
{
|
||||
RTLIL::SigSpec result = defval;
|
||||
|
@ -421,7 +421,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
|
|||
swcache.snippets = &sigsnip;
|
||||
swcache.insert(&proc->root_case);
|
||||
|
||||
dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara;
|
||||
dict<RTLIL::SwitchRule*, bool> swpara;
|
||||
|
||||
int cnt = 0;
|
||||
for (int idx : sigsnip.snippets)
|
||||
|
|
|
@ -176,7 +176,7 @@ struct coverdb_t
|
|||
|
||||
struct mutate_queue_t
|
||||
{
|
||||
pool<mutate_t*, hash_ptr_ops> db;
|
||||
pool<mutate_t*> db;
|
||||
|
||||
mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
|
||||
mutate_t *m = nullptr;
|
||||
|
|
|
@ -46,9 +46,11 @@ struct IdBit {
|
|||
|
||||
bool operator==(const IdBit &other) const { return name == other.name && bit == other.bit; };
|
||||
bool operator!=(const IdBit &other) const { return name != other.name || bit != other.bit; };
|
||||
unsigned hash() const
|
||||
Hasher hash_into(Hasher h) const
|
||||
{
|
||||
return mkhash_add(name.hash(), bit);
|
||||
h.eat(name);
|
||||
h.eat(bit);
|
||||
return h;
|
||||
}
|
||||
|
||||
IdString name;
|
||||
|
@ -62,9 +64,11 @@ struct InvBit {
|
|||
|
||||
bool operator==(const InvBit &other) const { return bit == other.bit && inverted == other.inverted; };
|
||||
bool operator!=(const InvBit &other) const { return bit != other.bit || inverted != other.inverted; };
|
||||
unsigned hash() const
|
||||
Hasher hash_into(Hasher h) const
|
||||
{
|
||||
return mkhash(bit.hash(), inverted);
|
||||
h.eat(bit);
|
||||
h.eat(inverted);
|
||||
return h;
|
||||
}
|
||||
|
||||
IdBit bit;
|
||||
|
|
|
@ -161,7 +161,7 @@ struct SimInstance
|
|||
pool<SigBit> dirty_bits;
|
||||
pool<Cell*> dirty_cells;
|
||||
pool<IdString> dirty_memories;
|
||||
pool<SimInstance*, hash_ptr_ops> dirty_children;
|
||||
pool<SimInstance*> dirty_children;
|
||||
|
||||
struct ff_state_t
|
||||
{
|
||||
|
|
|
@ -1411,6 +1411,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
|
|||
module->connect(conn);
|
||||
}
|
||||
|
||||
cell_stats.sort();
|
||||
for (auto &it : cell_stats)
|
||||
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
|
||||
int in_wires = 0, out_wires = 0;
|
||||
|
|
|
@ -111,7 +111,7 @@ struct AlumaccWorker
|
|||
|
||||
dict<RTLIL::SigBit, int> bit_users;
|
||||
dict<RTLIL::SigSpec, maccnode_t*> sig_macc;
|
||||
dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu;
|
||||
dict<RTLIL::SigSig, pool<alunode_t*>> sig_alu;
|
||||
int macc_counter, alu_counter;
|
||||
|
||||
AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
|
||||
|
@ -226,7 +226,7 @@ struct AlumaccWorker
|
|||
{
|
||||
while (1)
|
||||
{
|
||||
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
|
||||
pool<maccnode_t*> delete_nodes;
|
||||
|
||||
for (auto &it : sig_macc)
|
||||
{
|
||||
|
@ -278,7 +278,7 @@ struct AlumaccWorker
|
|||
|
||||
void macc_to_alu()
|
||||
{
|
||||
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
|
||||
pool<maccnode_t*> delete_nodes;
|
||||
|
||||
for (auto &it : sig_macc)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/ff.h"
|
||||
#include "libparse.h"
|
||||
#include <optional>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
@ -10,6 +11,7 @@ struct ClockGateCell {
|
|||
IdString ce_pin;
|
||||
IdString clk_in_pin;
|
||||
IdString clk_out_pin;
|
||||
std::vector<IdString> tie_lo_pins;
|
||||
};
|
||||
|
||||
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
|
||||
|
@ -37,6 +39,166 @@ ClockGateCell icg_from_arg(std::string& name, std::string& str) {
|
|||
return c;
|
||||
}
|
||||
|
||||
static std::pair<std::optional<ClockGateCell>, std::optional<ClockGateCell>>
|
||||
find_icgs(std::string filename, std::vector<std::string> const& dont_use_cells) {
|
||||
std::ifstream f;
|
||||
f.open(filename.c_str());
|
||||
if (f.fail())
|
||||
log_cmd_error("Can't open liberty file `%s': %s\n", filename.c_str(), strerror(errno));
|
||||
LibertyParser libparser(f);
|
||||
f.close();
|
||||
auto ast = libparser.ast;
|
||||
|
||||
// We will pick the most suitable ICG absed on tie_lo count and area
|
||||
struct ICGRankable : public ClockGateCell { double area; };
|
||||
std::optional<ICGRankable> best_pos;
|
||||
std::optional<ICGRankable> best_neg;
|
||||
|
||||
if (ast->id != "library")
|
||||
log_error("Format error in liberty file.\n");
|
||||
|
||||
// This is a lot of boilerplate, isn't it?
|
||||
for (auto cell : ast->children)
|
||||
{
|
||||
if (cell->id != "cell" || cell->args.size() != 1)
|
||||
continue;
|
||||
|
||||
const LibertyAst *dn = cell->find("dont_use");
|
||||
if (dn != nullptr && dn->value == "true")
|
||||
continue;
|
||||
|
||||
bool dont_use = false;
|
||||
for (auto dont_use_cell : dont_use_cells)
|
||||
{
|
||||
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
|
||||
{
|
||||
dont_use = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dont_use)
|
||||
continue;
|
||||
|
||||
const LibertyAst *icg_kind_ast = cell->find("clock_gating_integrated_cell");
|
||||
if (icg_kind_ast == nullptr)
|
||||
continue;
|
||||
|
||||
auto cell_name = cell->args[0];
|
||||
auto icg_kind = icg_kind_ast->value;
|
||||
auto starts_with = [&](std::string prefix) {
|
||||
return icg_kind.compare(0, prefix.size(), prefix) == 0;
|
||||
};
|
||||
bool clk_pol;
|
||||
if (icg_kind == "latch_posedge" || starts_with("latch_posedge_")) {
|
||||
clk_pol = true;
|
||||
} else if (icg_kind == "latch_negedge" || starts_with("latch_negedge_")) {
|
||||
clk_pol = false;
|
||||
} else {
|
||||
log("Ignoring ICG primitive %s of kind '%s'\n", cell_name.c_str(), icg_kind.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("maybe valid icg: %s\n", cell_name.c_str());
|
||||
ClockGateCell icg_interface;
|
||||
icg_interface.name = RTLIL::escape_id(cell_name);
|
||||
|
||||
for (auto pin : cell->children) {
|
||||
if (pin->id != "pin" || pin->args.size() != 1)
|
||||
continue;
|
||||
|
||||
if (auto clk = pin->find("clock_gate_clock_pin")) {
|
||||
if (!icg_interface.clk_in_pin.empty()) {
|
||||
log_warning("Malformed liberty file - multiple clock_gate_clock_pin in cell %s\n",
|
||||
cell_name.c_str());
|
||||
continue;
|
||||
} else
|
||||
icg_interface.clk_in_pin = RTLIL::escape_id(pin->args[0]);
|
||||
} else if (auto gclk = pin->find("clock_gate_out_pin")) {
|
||||
if (!icg_interface.clk_out_pin.empty()) {
|
||||
log_warning("Malformed liberty file - multiple clock_gate_out_pin in cell %s\n",
|
||||
cell_name.c_str());
|
||||
continue;
|
||||
} else
|
||||
icg_interface.clk_out_pin = RTLIL::escape_id(pin->args[0]);
|
||||
} else if (auto en = pin->find("clock_gate_enable_pin")) {
|
||||
if (!icg_interface.ce_pin.empty()) {
|
||||
log_warning("Malformed liberty file - multiple clock_gate_enable_pin in cell %s\n",
|
||||
cell_name.c_str());
|
||||
continue;
|
||||
} else
|
||||
icg_interface.ce_pin = RTLIL::escape_id(pin->args[0]);
|
||||
} else if (auto se = pin->find("clock_gate_test_pin")) {
|
||||
icg_interface.tie_lo_pins.push_back(RTLIL::escape_id(pin->args[0]));
|
||||
} else {
|
||||
const LibertyAst *dir = pin->find("direction");
|
||||
if (dir->value == "internal")
|
||||
continue;
|
||||
|
||||
log_warning("Malformed liberty file - extra pin %s in cell %s\n",
|
||||
pin->args[0].c_str(), cell_name.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (icg_interface.clk_in_pin.empty()) {
|
||||
log_warning("Malformed liberty file - missing clock_gate_clock_pin in cell %s",
|
||||
cell_name.c_str());
|
||||
continue;
|
||||
}
|
||||
if (icg_interface.clk_out_pin.empty()) {
|
||||
log_warning("Malformed liberty file - missing clock_gate_out_pin in cell %s",
|
||||
cell_name.c_str());
|
||||
continue;
|
||||
}
|
||||
if (icg_interface.ce_pin.empty()) {
|
||||
log_warning("Malformed liberty file - missing clock_gate_enable_pin in cell %s",
|
||||
cell_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
double area = 0;
|
||||
const LibertyAst *ar = cell->find("area");
|
||||
if (ar != nullptr && !ar->value.empty())
|
||||
area = atof(ar->value.c_str());
|
||||
|
||||
std::optional<ICGRankable>& icg_to_beat = clk_pol ? best_pos : best_neg;
|
||||
|
||||
bool winning = false;
|
||||
if (icg_to_beat) {
|
||||
log_debug("ties: %zu ? %zu\n", icg_to_beat->tie_lo_pins.size(),
|
||||
icg_interface.tie_lo_pins.size());
|
||||
log_debug("area: %f ? %f\n", icg_to_beat->area, area);
|
||||
|
||||
// Prefer fewer test enables over area reduction (unlikely to matter)
|
||||
auto goal = std::make_pair(icg_to_beat->tie_lo_pins.size(), icg_to_beat->area);
|
||||
auto cost = std::make_pair(icg_interface.tie_lo_pins.size(), area);
|
||||
winning = cost < goal;
|
||||
|
||||
if (winning)
|
||||
log_debug("%s beats %s\n", icg_interface.name.c_str(), icg_to_beat->name.c_str());
|
||||
} else {
|
||||
log_debug("%s is the first of its polarity\n", icg_interface.name.c_str());
|
||||
winning = true;
|
||||
}
|
||||
if (winning) {
|
||||
ICGRankable new_icg {icg_interface, area};
|
||||
icg_to_beat.emplace(new_icg);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ClockGateCell> pos;
|
||||
std::optional<ClockGateCell> neg;
|
||||
if (best_pos) {
|
||||
log("Selected rising edge ICG %s from Liberty file\n", best_pos->name.c_str());
|
||||
pos.emplace(*best_pos);
|
||||
}
|
||||
if (best_neg) {
|
||||
log("Selected falling edge ICG %s from Liberty file\n", best_neg->name.c_str());
|
||||
neg.emplace(*best_neg);
|
||||
}
|
||||
return std::make_pair(pos, neg);
|
||||
}
|
||||
|
||||
struct ClockgatePass : public Pass {
|
||||
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
|
||||
void help() override {
|
||||
|
@ -60,12 +222,20 @@ struct ClockgatePass : public Pass {
|
|||
log(" user-specified <celltype> ICG (integrated clock gating)\n");
|
||||
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
|
||||
log(" The ICG's clock enable pin must be active high.\n");
|
||||
log(" -liberty <filename>\n");
|
||||
log(" If specified, ICGs will be selected from the liberty file\n");
|
||||
log(" if available. Priority is given to cells with fewer tie_lo\n");
|
||||
log(" inputs and smaller size. This removes the need to manually\n");
|
||||
log(" specify -pos or -neg and -tie_lo.\n");
|
||||
log(" -dont_use <celltype>\n");
|
||||
log(" Cells <celltype> won't be considered when searching for ICGs\n");
|
||||
log(" in the liberty file specified by -liberty.\n");
|
||||
log(" -tie_lo <port_name>\n");
|
||||
log(" Port <port_name> of the ICG will be tied to zero.\n");
|
||||
log(" Intended for DFT scan-enable pins.\n");
|
||||
log(" -min_net_size <n>\n");
|
||||
log(" Only transform sets of at least <n> eligible FFs.\n");
|
||||
// log(" \n");
|
||||
log(" \n");
|
||||
}
|
||||
|
||||
// One ICG will be generated per ClkNetInfo
|
||||
|
@ -77,10 +247,9 @@ struct ClockgatePass : public Pass {
|
|||
SigBit ce_bit;
|
||||
bool pol_clk;
|
||||
bool pol_ce;
|
||||
unsigned int hash() const {
|
||||
Hasher hash_into(Hasher h) const {
|
||||
auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce);
|
||||
unsigned int h = mkhash_init;
|
||||
h = mkhash(h, hash_ops<decltype(t)>::hash(t));
|
||||
h.eat(t);
|
||||
return h;
|
||||
}
|
||||
bool operator==(const ClkNetInfo& other) const {
|
||||
|
@ -110,7 +279,9 @@ struct ClockgatePass : public Pass {
|
|||
|
||||
std::optional<ClockGateCell> pos_icg_desc;
|
||||
std::optional<ClockGateCell> neg_icg_desc;
|
||||
std::vector<std::string> tie_lo_ports;
|
||||
std::vector<std::string> tie_lo_pins;
|
||||
std::string liberty_file;
|
||||
std::vector<std::string> dont_use_cells;
|
||||
int min_net_size = 0;
|
||||
|
||||
size_t argidx;
|
||||
|
@ -126,13 +297,33 @@ struct ClockgatePass : public Pass {
|
|||
neg_icg_desc = icg_from_arg(name, rest);
|
||||
}
|
||||
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
|
||||
tie_lo_ports.push_back(RTLIL::escape_id(args[++argidx]));
|
||||
tie_lo_pins.push_back(RTLIL::escape_id(args[++argidx]));
|
||||
}
|
||||
if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
|
||||
liberty_file = args[++argidx];
|
||||
rewrite_filename(liberty_file);
|
||||
}
|
||||
if (args[argidx] == "-dont_use" && argidx+1 < args.size()) {
|
||||
dont_use_cells.push_back(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
|
||||
min_net_size = atoi(args[++argidx].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!liberty_file.empty())
|
||||
std::tie(pos_icg_desc, neg_icg_desc) =
|
||||
find_icgs(liberty_file, dont_use_cells);
|
||||
else {
|
||||
for (auto pin : tie_lo_pins) {
|
||||
if (pos_icg_desc)
|
||||
pos_icg_desc->tie_lo_pins.push_back(pin);
|
||||
if (neg_icg_desc)
|
||||
neg_icg_desc->tie_lo_pins.push_back(pin);
|
||||
}
|
||||
}
|
||||
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
pool<Cell*> ce_ffs;
|
||||
|
@ -185,7 +376,7 @@ struct ClockgatePass : public Pass {
|
|||
gclk.new_net = module->addWire(NEW_ID);
|
||||
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
|
||||
// Tie low DFT ports like scan chain enable
|
||||
for (auto port : tie_lo_ports)
|
||||
for (auto port : matching_icg_desc->tie_lo_pins)
|
||||
icg->setPort(port, Const(0, 1));
|
||||
// Fix CE polarity if needed
|
||||
if (!clk.pol_ce) {
|
||||
|
|
|
@ -250,9 +250,11 @@ struct FlowGraph
|
|||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
unsigned int hash() const
|
||||
Hasher hash_into(Hasher h) const
|
||||
{
|
||||
return hash_ops<pair<RTLIL::SigBit, int>>::hash({node, is_bottom});
|
||||
std::pair<RTLIL::SigBit, int> p = {node, is_bottom};
|
||||
h.eat(p);
|
||||
return h;
|
||||
}
|
||||
|
||||
static NodePrime top(RTLIL::SigBit node)
|
||||
|
|
|
@ -53,7 +53,7 @@ struct QlDspSimdPass : public Pass {
|
|||
DspConfig(const DspConfig &ref) = default;
|
||||
DspConfig(DspConfig &&ref) = default;
|
||||
|
||||
unsigned int hash() const { return connections.hash(); }
|
||||
Hasher hash_into(Hasher h) const { h.eat(connections); return h; }
|
||||
|
||||
bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
read_ilang <<EOT
|
||||
read_rtlil <<EOT
|
||||
autoidx 1
|
||||
module \top
|
||||
wire output 1 \Y
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
library(test) {
|
||||
/* Integrated clock gating cells */
|
||||
cell (pos_small_tielo) {
|
||||
area : 1;
|
||||
clock_gating_integrated_cell : latch_posedge_precontrol;
|
||||
pin (GCLK) {
|
||||
clock_gate_out_pin : true;
|
||||
direction : output;
|
||||
}
|
||||
pin (CLK) {
|
||||
clock_gate_clock_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (CE) {
|
||||
clock_gate_enable_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (SE) {
|
||||
clock_gate_test_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
cell (pos_big) {
|
||||
area : 10;
|
||||
clock_gating_integrated_cell : latch_posedge;
|
||||
pin (GCLK) {
|
||||
clock_gate_out_pin : true;
|
||||
direction : output;
|
||||
}
|
||||
pin (CLK) {
|
||||
clock_gate_clock_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (CE) {
|
||||
clock_gate_enable_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
cell (pos_small) {
|
||||
area : 1;
|
||||
clock_gating_integrated_cell : latch_posedge;
|
||||
pin (GCLK) {
|
||||
clock_gate_out_pin : true;
|
||||
direction : output;
|
||||
}
|
||||
pin (CLK) {
|
||||
clock_gate_clock_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (CE) {
|
||||
clock_gate_enable_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
cell (neg_big) {
|
||||
area : 10;
|
||||
clock_gating_integrated_cell : latch_negedge;
|
||||
pin (GCLK) {
|
||||
clock_gate_out_pin : true;
|
||||
direction : output;
|
||||
}
|
||||
pin (CLK) {
|
||||
clock_gate_clock_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (CE) {
|
||||
clock_gate_enable_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
cell (neg_small_tielo) {
|
||||
area : 1;
|
||||
clock_gating_integrated_cell : latch_negedge_precontrol;
|
||||
pin (GCLK) {
|
||||
clock_gate_out_pin : true;
|
||||
direction : output;
|
||||
}
|
||||
pin (CLK) {
|
||||
clock_gate_clock_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (CE) {
|
||||
clock_gate_enable_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (SE) {
|
||||
clock_gate_test_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
cell (neg_small) {
|
||||
area : 1;
|
||||
clock_gating_integrated_cell : latch_negedge;
|
||||
pin (GCLK) {
|
||||
clock_gate_out_pin : true;
|
||||
direction : output;
|
||||
}
|
||||
pin (CLK) {
|
||||
clock_gate_clock_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
pin (CE) {
|
||||
clock_gate_enable_pin : true;
|
||||
direction : input;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ clockgate -pos pdk_icg ce:clkin:clkout -tie_lo scanen
|
|||
# falling edge clock flops don't get matched on -pos
|
||||
select -module dffe_00 -assert-count 0 t:\\pdk_icg
|
||||
select -module dffe_01 -assert-count 0 t:\\pdk_icg
|
||||
# falling edge clock flops do get matched on -pos
|
||||
# rising edge clock flops do get matched on -pos
|
||||
select -module dffe_10 -assert-count 1 t:\\pdk_icg
|
||||
select -module dffe_11 -assert-count 1 t:\\pdk_icg
|
||||
# if necessary, EN is inverted, since the given ICG
|
||||
|
@ -79,10 +79,10 @@ select -module dffe_wide_11 -assert-count 1 t:\\pdk_icg
|
|||
design -load before
|
||||
clockgate -min_net_size 1 -neg pdk_icg ce:clkin:clkout -tie_lo scanen
|
||||
|
||||
# rising edge clock flops don't get matched on -neg
|
||||
# falling edge clock flops do get matched on -neg
|
||||
select -module dffe_00 -assert-count 1 t:\\pdk_icg
|
||||
select -module dffe_01 -assert-count 1 t:\\pdk_icg
|
||||
# rising edge clock flops do get matched on -neg
|
||||
# rising edge clock flops don't get matched on -neg
|
||||
select -module dffe_10 -assert-count 0 t:\\pdk_icg
|
||||
select -module dffe_11 -assert-count 0 t:\\pdk_icg
|
||||
# if necessary, EN is inverted, since the given ICG
|
||||
|
@ -193,4 +193,55 @@ select -assert-count 1 t:\\pdk_icg
|
|||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# TODO test -tie_lo
|
||||
design -load before
|
||||
clockgate -liberty clockgate.lib
|
||||
|
||||
# rising edge ICGs
|
||||
select -module dffe_00 -assert-count 0 t:\\pos_small
|
||||
select -module dffe_01 -assert-count 0 t:\\pos_small
|
||||
|
||||
select -module dffe_10 -assert-count 1 t:\\pos_small
|
||||
select -module dffe_11 -assert-count 1 t:\\pos_small
|
||||
|
||||
# falling edge ICGs
|
||||
select -module dffe_00 -assert-count 1 t:\\neg_small
|
||||
select -module dffe_01 -assert-count 1 t:\\neg_small
|
||||
|
||||
select -module dffe_10 -assert-count 0 t:\\neg_small
|
||||
select -module dffe_11 -assert-count 0 t:\\neg_small
|
||||
|
||||
# and nothing else
|
||||
select -module dffe_00 -assert-count 0 t:\\pos_big
|
||||
select -module dffe_01 -assert-count 0 t:\\pos_big
|
||||
select -module dffe_10 -assert-count 0 t:\\pos_big
|
||||
select -module dffe_11 -assert-count 0 t:\\pos_big
|
||||
select -module dffe_00 -assert-count 0 t:\\pos_small_tielo
|
||||
select -module dffe_01 -assert-count 0 t:\\pos_small_tielo
|
||||
select -module dffe_10 -assert-count 0 t:\\pos_small_tielo
|
||||
select -module dffe_11 -assert-count 0 t:\\pos_small_tielo
|
||||
select -module dffe_00 -assert-count 0 t:\\neg_big
|
||||
select -module dffe_01 -assert-count 0 t:\\neg_big
|
||||
select -module dffe_10 -assert-count 0 t:\\neg_big
|
||||
select -module dffe_11 -assert-count 0 t:\\neg_big
|
||||
select -module dffe_00 -assert-count 0 t:\\neg_small_tielo
|
||||
select -module dffe_01 -assert-count 0 t:\\neg_small_tielo
|
||||
select -module dffe_10 -assert-count 0 t:\\neg_small_tielo
|
||||
select -module dffe_11 -assert-count 0 t:\\neg_small_tielo
|
||||
|
||||
# if necessary, EN is inverted, since the given ICG
|
||||
# is assumed to have an active-high EN
|
||||
select -module dffe_10 -assert-count 1 t:\$_NOT_
|
||||
select -module dffe_11 -assert-count 0 t:\$_NOT_
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
design -load before
|
||||
clockgate -liberty clockgate.lib -dont_use pos_small -dont_use neg_small
|
||||
|
||||
# rising edge ICGs
|
||||
select -module dffe_10 -assert-count 1 t:\\pos_big
|
||||
select -module dffe_11 -assert-count 1 t:\\pos_big
|
||||
|
||||
# falling edge ICGs
|
||||
select -module dffe_00 -assert-count 1 t:\\neg_big
|
||||
select -module dffe_01 -assert-count 1 t:\\neg_big
|
||||
|
|
Loading…
Reference in New Issue