yosys/kernel/drivertools.h

1326 lines
32 KiB
C++

/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Jannis Harder <jix@yosyshq.com> <me@jix.one>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef DRIVERTOOLS_H
#define DRIVERTOOLS_H
#include <type_traits>
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
YOSYS_NAMESPACE_BEGIN
// TODO move implementation into a .cc file
struct DriveBit;
struct DriveChunkWire;
struct DriveChunkPort;
struct DriveChunkMarker;
struct DriveChunk;
struct DriveSpec;
const char *log_signal(DriveChunkWire const &chunk);
const char *log_signal(DriveChunkPort const &chunk);
const char *log_signal(DriveChunkMarker const &chunk);
const char *log_signal(DriveChunk const &chunk);
const char *log_signal(DriveSpec const &chunk);
enum class DriveType : unsigned char
{
NONE,
CONSTANT,
WIRE,
PORT,
MULTIPLE,
MARKER,
};
struct DriveBitWire
{
Wire *wire;
int offset;
DriveBitWire(Wire *wire, int offset) : wire(wire), offset(offset) {}
bool operator==(const DriveBitWire &other) const
{
return wire == other.wire && offset == other.offset;
}
bool operator<(const DriveBitWire &other) const
{
if (wire != other.wire)
return wire->name < other.wire->name;
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(wire->name.hash(), offset);
}
operator SigBit() const
{
return SigBit(wire, offset);
}
};
struct DriveBitPort
{
Cell *cell;
IdString port;
int offset;
DriveBitPort(Cell *cell, IdString port, int offset) : cell(cell), port(port), offset(offset) {}
bool operator==(const DriveBitPort &other) const
{
return cell == other.cell && port == other.port && offset == other.offset;
}
bool operator<(const DriveBitPort &other) const
{
if (cell != other.cell)
return cell->name < other.cell->name;
if (port != other.port)
return port < other.port;
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
}
};
struct DriveBitMarker
{
int marker;
int offset;
DriveBitMarker(int marker, int offset) : marker(marker), offset(offset) {}
bool operator==(const DriveBitMarker &other) const
{
return marker == other.marker && offset == other.offset;
}
bool operator<(const DriveBitMarker &other) const
{
if (marker != other.marker)
return marker < other.marker;
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(marker, offset);
}
};
struct DriveBitMultiple
{
private:
pool<DriveBit> multiple_;
public:
DriveBitMultiple() {}
DriveBitMultiple(DriveBit const &single)
{
multiple_.emplace(single);
}
pool<DriveBit> const &multiple() const { return multiple_; }
void merge(DriveBitMultiple const &other)
{
for (DriveBit const &single : other.multiple_)
merge(single);
}
void merge(DriveBitMultiple &&other)
{
for (DriveBit &single : other.multiple_)
merge(std::move(single));
}
void merge(DriveBit const &single);
void merge(DriveBit &&single);
bool operator==(const DriveBitMultiple &other) const
{
return multiple_ == other.multiple_;
}
unsigned int hash() const
{
return multiple_.hash();
}
};
struct DriveBit
{
private:
DriveType type_ = DriveType::NONE;
union
{
State constant_;
DriveBitWire wire_;
DriveBitPort port_;
DriveBitMarker marker_;
DriveBitMultiple multiple_;
};
public:
DriveBit() {}
DriveBit(SigBit const &bit);
DriveBit(DriveBit const &other) { *this = other; }
DriveBit(DriveBit &&other) { *this = other; }
DriveBit(State constant) { *this = constant; }
DriveBit(DriveBitWire const &wire) { *this = wire; }
DriveBit(DriveBitWire &&wire) { *this = wire; }
DriveBit(DriveBitPort const &port) { *this = port; }
DriveBit(DriveBitPort &&port) { *this = port; }
DriveBit(DriveBitMarker const &marker) { *this = marker; }
DriveBit(DriveBitMarker &&marker) { *this = marker; }
DriveBit(DriveBitMultiple const &multiple) { *this = multiple; }
DriveBit(DriveBitMultiple &&multiple) { *this = multiple; }
~DriveBit() { set_none(); }
void set_none()
{
switch (type_)
{
case DriveType::NONE:
break;
case DriveType::CONSTANT:
break;
case DriveType::WIRE:
wire_.~DriveBitWire();
break;
case DriveType::PORT:
port_.~DriveBitPort();
break;
case DriveType::MARKER:
marker_.~DriveBitMarker();
break;
case DriveType::MULTIPLE:
multiple_.~DriveBitMultiple();
break;
}
type_ = DriveType::NONE;
}
DriveBit &operator=(DriveBit const &other)
{
switch (other.type_)
{
case DriveType::NONE:
set_none();
break;
case DriveType::CONSTANT:
*this = other.constant_;
break;
case DriveType::WIRE:
*this = other.wire_;
break;
case DriveType::PORT:
*this = other.port_;
break;
case DriveType::MARKER:
*this = other.marker_;
break;
case DriveType::MULTIPLE:
*this = other.multiple_;
break;
}
return *this;
}
DriveBit &operator=(DriveBit &&other)
{
switch (other.type_)
{
case DriveType::NONE:
set_none();
break;
case DriveType::CONSTANT:
*this = std::move(other.constant_);
break;
case DriveType::WIRE:
*this = std::move(other.wire_);
break;
case DriveType::PORT:
*this = std::move(other.port_);
break;
case DriveType::MARKER:
*this = std::move(other.marker_);
break;
case DriveType::MULTIPLE:
*this = std::move(other.multiple_);
break;
}
return *this;
}
DriveBit &operator=(State constant)
{
set_none();
constant_ = constant;
type_ = DriveType::CONSTANT;
return *this;
}
DriveBit &operator=(DriveBitWire const &wire)
{
set_none();
new (&wire_) DriveBitWire(wire);
type_ = DriveType::WIRE;
return *this;
}
DriveBit &operator=(DriveBitWire &&wire)
{
set_none();
new (&wire_) DriveBitWire(wire);
type_ = DriveType::WIRE;
return *this;
}
DriveBit &operator=(DriveBitPort const &port)
{
set_none();
new (&port_) DriveBitPort(port);
type_ = DriveType::PORT;
return *this;
}
DriveBit &operator=(DriveBitPort &&port)
{
set_none();
new (&port_) DriveBitPort(port);
type_ = DriveType::PORT;
return *this;
}
DriveBit &operator=(DriveBitMarker const &marker)
{
set_none();
new (&marker_) DriveBitMarker(marker);
type_ = DriveType::MARKER;
return *this;
}
DriveBit &operator=(DriveBitMarker &&marker)
{
set_none();
new (&marker_) DriveBitMarker(marker);
type_ = DriveType::MARKER;
return *this;
}
DriveBit &operator=(DriveBitMultiple const &multiple)
{
set_none();
if (multiple.multiple().empty())
return *this;
new (&multiple_) DriveBitMultiple(multiple);
type_ = DriveType::MULTIPLE;
return *this;
}
DriveBit &operator=(DriveBitMultiple &&multiple)
{
set_none();
if (multiple.multiple().empty())
return *this;
new (&multiple_) DriveBitMultiple(multiple);
type_ = DriveType::MULTIPLE;
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);
}
bool operator==(const DriveBit &other) const
{
if (type_ != other.type_)
return false;
switch (type_)
{
case DriveType::NONE:
return true;
case DriveType::CONSTANT:
return constant_ == other.constant_;
case DriveType::WIRE:
return wire_ == other.wire_;
case DriveType::PORT:
return port_ == other.port_;
case DriveType::MARKER:
return marker_ == other.marker_;
case DriveType::MULTIPLE:
return multiple_ == other.multiple_;
}
log_assert(false);
}
bool operator!=(const DriveBit &other) const
{
return !(*this == other);
}
bool operator<(const DriveBit &other) const
{
if (type_ != other.type_)
return type_ < other.type_;
switch (type_)
{
case DriveType::NONE:
return false;
case DriveType::CONSTANT:
return constant_ < other.constant_;
case DriveType::WIRE:
return wire_ < other.wire_;
case DriveType::PORT:
return port_ < other.port_;
case DriveType::MARKER:
return marker_ < other.marker_;
case DriveType::MULTIPLE:
log_assert(!"TODO");
}
log_abort();
}
DriveType type() const { return type_; }
bool is_none() const { return type_ == DriveType::NONE; }
bool is_constant() const { return type_ == DriveType::CONSTANT; }
bool is_wire() const { return type_ == DriveType::WIRE; }
bool is_port() const { return type_ == DriveType::PORT; }
bool is_marker() const { return type_ == DriveType::MARKER; }
bool is_multiple() const { return type_ == DriveType::MULTIPLE; }
State &constant() { log_assert(is_constant()); return constant_; }
State const &constant() const { log_assert(is_constant()); return constant_; }
DriveBitWire &wire() { log_assert(is_wire()); return wire_; }
DriveBitWire const &wire() const { log_assert(is_wire()); return wire_; }
DriveBitPort &port() { log_assert(is_port()); return port_; }
DriveBitPort const &port() const { log_assert(is_port()); return port_; }
DriveBitMarker &marker() { log_assert(is_marker()); return marker_; }
DriveBitMarker const &marker() const { log_assert(is_marker()); return marker_; }
DriveBitMultiple &multiple() { log_assert(is_multiple()); return multiple_; }
DriveBitMultiple const &multiple() const { log_assert(is_multiple()); return multiple_; }
void merge(DriveBit const &other);
};
struct DriveChunkWire
{
Wire *wire;
int offset;
int width;
DriveChunkWire(Wire *wire, int offset, int width) : wire(wire), offset(offset), width(width) {}
DriveChunkWire(DriveBitWire const &bit) : wire(bit.wire), offset(bit.offset), width(1) {}
int size() const { return width; }
DriveBitWire operator[](int i) const
{
log_assert(i >= 0 && i < width);
return DriveBitWire(wire, offset + i);
}
bool can_append(DriveBitWire const &bit) const;
bool try_append(DriveBitWire const &bit);
bool try_append(DriveChunkWire const &chunk);
// Whether this chunk is a whole wire
bool is_whole() const { return offset == 0 && width == wire->width; }
bool operator==(const DriveChunkWire &other) const
{
return wire == other.wire && offset == other.offset && width == other.width;
}
bool operator<(const DriveChunkWire &other) const
{
if (wire != other.wire)
return wire->name < other.wire->name;
if (width != other.width)
return width < other.width;
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(wire->name.hash(), width), offset);
}
explicit operator SigChunk() const
{
return SigChunk(wire, offset, width);
}
};
struct DriveChunkPort
{
Cell *cell;
IdString port;
int offset;
int width;
DriveChunkPort(Cell *cell, IdString port, int offset, int width) :
cell(cell), port(port), offset(offset), width(width) { }
DriveChunkPort(Cell *cell, IdString port) :
cell(cell), port(port), offset(0), width(GetSize(cell->connections().at(port))) { }
DriveChunkPort(Cell *cell, std::pair<IdString, SigSpec> const &conn) :
cell(cell), port(conn.first), offset(0), width(GetSize(conn.second)) { }
DriveChunkPort(DriveBitPort const &bit) :
cell(bit.cell), port(bit.port), offset(bit.offset), width(1) { }
int size() const { return width; }
DriveBitPort operator[](int i) const
{
log_assert(i >= 0 && i < width);
return DriveBitPort(cell, port, offset + i);
}
bool can_append(DriveBitPort const &bit) const;
bool try_append(DriveBitPort const &bit);
bool try_append(DriveChunkPort const &chunk);
// Whether this chunk is a whole port
bool is_whole() const
{
return offset == 0 && width == cell->connections().at(port).size();
}
bool operator==(const DriveChunkPort &other) const
{
return cell == other.cell && port == other.port && offset == other.offset && width == other.width;
}
bool operator<(const DriveChunkPort &other) const
{
if (cell != other.cell)
return cell->name < other.cell->name;
if (port != other.port)
return port < other.port;
if (width != other.width)
return width < other.width;
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(mkhash(cell->name.hash(), port.hash()), width), offset);
}
};
struct DriveChunkMarker
{
int marker;
int offset;
int width;
DriveChunkMarker(int marker, int offset, int width) :
marker(marker), offset(offset), width(width) {}
DriveChunkMarker(DriveBitMarker const &bit) :
marker(bit.marker), offset(bit.offset), width(1) {}
int size() const { return width; }
DriveBitMarker operator[](int i) const
{
log_assert(i >= 0 && i < width);
return DriveBitMarker(marker, offset + i);
}
bool can_append(DriveBitMarker const &bit) const;
bool try_append(DriveBitMarker const &bit);
bool try_append(DriveChunkMarker const &chunk);
bool operator==(const DriveChunkMarker &other) const
{
return marker == other.marker && offset == other.offset && width == other.width;
}
bool operator<(const DriveChunkMarker &other) const
{
if (marker != other.marker)
return marker < other.marker;
if (width != other.width)
return width < other.width;
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(marker, width), offset);
}
};
struct DriveChunkMultiple
{
private:
mutable pool<DriveChunk> multiple_;
int width_;
public:
pool<DriveChunk> const &multiple() const { return multiple_; }
DriveChunkMultiple(DriveBitMultiple const &bit) : width_(1) {
for (auto const &bit : bit.multiple())
multiple_.emplace(bit);
}
int size() const { return width_; }
DriveBitMultiple operator[](int i) const;
bool can_append(DriveBitMultiple const &bit) const;
bool try_append(DriveBitMultiple const &bit);
bool can_append(DriveChunkMultiple const &bit) const;
bool try_append(DriveChunkMultiple const &bit);
bool operator==(const DriveChunkMultiple &other) const
{
return width_ == other.width_ && multiple_ == other.multiple_;
}
bool operator<(const DriveChunkMultiple &other) const
{
if (multiple_.size() < other.multiple_.size())
multiple_.sort();
return false; // TODO implement, canonicalize order
}
unsigned int hash() const
{
return mkhash(width_, multiple_.hash());
}
};
struct DriveChunk
{
private:
DriveType type_ = DriveType::NONE;
union
{
int none_;
Const constant_;
DriveChunkWire wire_;
DriveChunkPort port_;
DriveChunkMarker marker_;
DriveChunkMultiple multiple_;
};
public:
DriveChunk() { set_none(); }
DriveChunk(DriveChunk const &other) { *this = other; }
DriveChunk(DriveChunk &&other) { *this = other; }
DriveChunk(DriveBit const &other) { *this = other; }
DriveChunk(Const const &constant) { *this = constant; }
DriveChunk(Const &&constant) { *this = constant; }
DriveChunk(DriveChunkWire const &wire) { *this = wire; }
DriveChunk(DriveChunkWire &&wire) { *this = wire; }
DriveChunk(DriveChunkPort const &port) { *this = port; }
DriveChunk(DriveChunkPort &&port) { *this = port; }
DriveChunk(DriveChunkMarker const &marker) { *this = marker; }
DriveChunk(DriveChunkMarker &&marker) { *this = marker; }
DriveChunk(DriveChunkMultiple const &multiple) { *this = multiple; }
DriveChunk(DriveChunkMultiple &&multiple) { *this = multiple; }
~DriveChunk() { set_none(); }
DriveBit operator[](int i) const
{
switch (type_)
{
case DriveType::NONE:
return DriveBit();
case DriveType::CONSTANT:
return constant_[i];
case DriveType::WIRE:
return wire_[i];
case DriveType::PORT:
return port_[i];
case DriveType::MARKER:
return marker_[i];
case DriveType::MULTIPLE:
return multiple_[i];
}
log_abort();
}
void set_none(int width = 0)
{
switch (type_)
{
case DriveType::NONE:
none_ = width;
break;
case DriveType::CONSTANT:
constant_.~Const();
break;
case DriveType::WIRE:
wire_.~DriveChunkWire();
break;
case DriveType::PORT:
port_.~DriveChunkPort();
break;
case DriveType::MARKER:
marker_.~DriveChunkMarker();
break;
case DriveType::MULTIPLE:
multiple_.~DriveChunkMultiple();
break;
}
type_ = DriveType::NONE;
none_ = width;
}
DriveChunk &operator=(DriveChunk const &other)
{
switch (other.type_)
{
case DriveType::NONE:
set_none(other.none_);
break;
case DriveType::CONSTANT:
*this = other.constant_;
break;
case DriveType::WIRE:
*this = other.wire_;
break;
case DriveType::PORT:
*this = other.port_;
break;
case DriveType::MARKER:
*this = other.marker_;
break;
case DriveType::MULTIPLE:
*this = other.multiple_;
break;
}
return *this;
}
DriveChunk &operator=(DriveChunk &&other)
{
switch (other.type_)
{
case DriveType::NONE:
set_none(other.none_);
break;
case DriveType::CONSTANT:
*this = std::move(other.constant_);
break;
case DriveType::WIRE:
*this = std::move(other.wire_);
break;
case DriveType::PORT:
*this = std::move(other.port_);
break;
case DriveType::MARKER:
*this = std::move(other.marker_);
break;
case DriveType::MULTIPLE:
*this = std::move(other.multiple_);
break;
}
return *this;
}
DriveChunk &operator=(Const const &constant)
{
set_none();
new (&constant_) Const(constant);
type_ = DriveType::CONSTANT;
return *this;
}
DriveChunk &operator=(Const &&constant)
{
set_none();
new (&constant_) Const(std::move(constant));
type_ = DriveType::CONSTANT;
return *this;
}
DriveChunk &operator=(DriveChunkWire const &wire)
{
set_none();
new (&wire_) DriveChunkWire(wire);
type_ = DriveType::WIRE;
return *this;
}
DriveChunk &operator=(DriveChunkWire &&wire)
{
set_none();
new (&wire_) DriveChunkWire(wire);
type_ = DriveType::WIRE;
return *this;
}
DriveChunk &operator=(DriveChunkPort const &port)
{
set_none();
new (&port_) DriveChunkPort(port);
type_ = DriveType::PORT;
return *this;
}
DriveChunk &operator=(DriveChunkPort &&port)
{
set_none();
new (&port_) DriveChunkPort(port);
type_ = DriveType::PORT;
return *this;
}
DriveChunk &operator=(DriveChunkMarker const &marker)
{
set_none();
new (&marker_) DriveChunkMarker(marker);
type_ = DriveType::MARKER;
return *this;
}
DriveChunk &operator=(DriveChunkMarker &&marker)
{
set_none();
new (&marker_) DriveChunkMarker(marker);
type_ = DriveType::MARKER;
return *this;
}
DriveChunk &operator=(DriveChunkMultiple const &multiple)
{
set_none(multiple.size());
if (multiple.multiple().empty())
return *this;
new (&multiple_) DriveChunkMultiple(multiple);
type_ = DriveType::MULTIPLE;
return *this;
}
DriveChunk &operator=(DriveChunkMultiple &&multiple)
{
set_none(multiple.size());
if (multiple.multiple().empty())
return *this;
new (&multiple_) DriveChunkMultiple(multiple);
type_ = DriveType::MULTIPLE;
return *this;
}
DriveChunk &operator=(DriveBit const &other)
{
switch (other.type())
{
case DriveType::NONE:
set_none(1);
break;
case DriveType::CONSTANT:
*this = Const(other.constant());
break;
case DriveType::WIRE:
*this = DriveChunkWire(other.wire());
break;
case DriveType::PORT:
*this = DriveChunkPort(other.port());
break;
case DriveType::MARKER:
*this = DriveChunkMarker(other.marker());
break;
case DriveType::MULTIPLE:
*this = DriveChunkMultiple(other.multiple());
break;
}
return *this;
}
bool can_append(DriveBit const &bit) const;
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);
}
bool operator==(const DriveChunk &other) const
{
if (type_ != other.type_)
return false;
switch (type_)
{
case DriveType::NONE:
return true;
case DriveType::CONSTANT:
return constant_ == other.constant_;
case DriveType::WIRE:
return wire_ == other.wire_;
case DriveType::PORT:
return port_ == other.port_;
case DriveType::MARKER:
return marker_ == other.marker_;
case DriveType::MULTIPLE:
return multiple_ == other.multiple_;
}
log_assert(false);
}
bool operator!=(const DriveChunk &other) const
{
return !(*this == other);
}
bool operator<(const DriveChunk &other) const
{
if (type_ != other.type_)
return type_ < other.type_;
switch (type_)
{
case DriveType::NONE:
return false;
case DriveType::CONSTANT:
return constant_ < other.constant_;
case DriveType::WIRE:
return wire_ < other.wire_;
case DriveType::PORT:
return port_ < other.port_;
case DriveType::MARKER:
return marker_ < other.marker_;
case DriveType::MULTIPLE:
return multiple_ < other.multiple_;
}
log_assert(false);
}
DriveType type() const { return type_; }
bool is_none() const { return type_ == DriveType::NONE; }
bool is_constant() const { return type_ == DriveType::CONSTANT; }
bool is_wire() const { return type_ == DriveType::WIRE; }
bool is_port() const { return type_ == DriveType::PORT; }
bool is_marker() const { return type_ == DriveType::MARKER; }
bool is_multiple() const { return type_ == DriveType::MULTIPLE; }
Const &constant() { log_assert(is_constant()); return constant_; }
Const const &constant() const { log_assert(is_constant()); return constant_; }
DriveChunkWire &wire() { log_assert(is_wire()); return wire_; }
DriveChunkWire const &wire() const { log_assert(is_wire()); return wire_; }
DriveChunkPort &port() { log_assert(is_port()); return port_; }
DriveChunkPort const &port() const { log_assert(is_port()); return port_; }
DriveChunkMarker &marker() { log_assert(is_marker()); return marker_; }
DriveChunkMarker const &marker() const { log_assert(is_marker()); return marker_; }
DriveChunkMultiple &multiple() { log_assert(is_multiple()); return multiple_; }
DriveChunkMultiple const &multiple() const { log_assert(is_multiple()); return multiple_; }
int size() const
{
switch (type_)
{
case DriveType::NONE:
return none_;
case DriveType::CONSTANT:
return constant_.size();
case DriveType::WIRE:
return wire_.size();
case DriveType::PORT:
return port_.size();
case DriveType::MARKER:
return marker_.size();
case DriveType::MULTIPLE:
return multiple_.size();
}
}
};
struct DriveSpec
{
private:
int width_ = 0;
mutable std::vector<DriveChunk> chunks_;
mutable std::vector<DriveBit> bits_;
mutable unsigned int hash_ = 0;
public:
inline bool packed() const {
return bits_.empty();
}
DriveSpec() {}
DriveSpec(DriveChunk const &chunk) { *this = chunk; }
DriveSpec(DriveChunkWire const &chunk) { *this = chunk; }
DriveSpec(DriveChunkPort const &chunk) { *this = chunk; }
DriveSpec(DriveChunkMarker const &chunk) { *this = chunk; }
DriveSpec(DriveChunkMultiple const &chunk) { *this = chunk; }
DriveSpec(DriveBit const &bit) { *this = bit; }
DriveSpec(DriveBitWire const &bit) { *this = bit; }
DriveSpec(DriveBitPort const &bit) { *this = bit; }
DriveSpec(DriveBitMarker const &bit) { *this = bit; }
DriveSpec(DriveBitMultiple const &bit) { *this = bit; }
DriveSpec(std::vector<DriveChunk> const &chunks) : chunks_(chunks) { compute_width(); }
DriveSpec(std::vector<DriveBit> const &bits)
{
for (auto const &bit : bits)
append(bit);
}
DriveSpec(SigSpec const &sig)
{
// TODO: converting one chunk at a time would be faster
for (auto const &bit : sig.bits())
append(bit);
}
std::vector<DriveChunk> const &chunks() const { pack(); return chunks_; }
std::vector<DriveBit> const &bits() const { unpack(); return bits_; }
int size() const { return width_; }
void append(DriveBit const &bit);
void append(DriveChunk const &chunk);
void pack() const;
void unpack() const;
DriveBit &operator[](int index)
{
log_assert(index >= 0 && index < size());
unpack();
return bits_[index];
}
const DriveBit &operator[](int index) const
{
log_assert(index >= 0 && index < size());
unpack();
return bits_[index];
}
void clear()
{
chunks_.clear();
bits_.clear();
width_ = 0;
}
DriveSpec &operator=(DriveChunk const &chunk)
{
chunks_.clear();
bits_.clear();
append(chunk);
return *this;
}
DriveSpec &operator=(DriveChunkWire const &chunk) { return *this = DriveChunk(chunk); }
DriveSpec &operator=(DriveChunkPort const &chunk) { return *this = DriveChunk(chunk); }
DriveSpec &operator=(DriveChunkMarker const &chunk) { return *this = DriveChunk(chunk); }
DriveSpec &operator=(DriveChunkMultiple const &chunk) { return *this = DriveChunk(chunk); }
DriveSpec &operator=(DriveBit const &bit)
{
chunks_.clear();
bits_.clear();
append(bit);
return *this;
}
DriveSpec &operator=(DriveBitWire const &bit) { return *this = DriveBit(bit); }
DriveSpec &operator=(DriveBitPort const &bit) { return *this = DriveBit(bit); }
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_;
pack();
hash_ = hash_ops<std::vector<DriveChunk>>().hash(chunks_);
hash_ |= (hash_ == 0);
return hash_;
}
bool operator==(DriveSpec const &other) const {
if (size() != other.size() || hash() != other.hash())
return false;
return chunks() == other.chunks();
}
private:
void compute_width();
};
struct DriverMap
{
CellTypes celltypes;
DriverMap() { celltypes.setup(); }
DriverMap(Design *design) { celltypes.setup(); celltypes.setup_design(design); }
private:
// Internally we represent all DriveBits by mapping them to DriveBitIds
// which use less memory and are cheaper to compare.
struct DriveBitId
{
int id = -1;
DriveBitId() {};
DriveBitId(int id) : id(id) { }
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; }
};
// Essentially a dict<DriveBitId, pool<DriveBitId>> but using less memory
// and fewer allocations
struct DriveBitGraph {
dict<DriveBitId, DriveBitId> first_edges;
dict<DriveBitId, DriveBitId> second_edges;
dict<DriveBitId, pool<DriveBitId>> more_edges;
void add_edge(DriveBitId src, DriveBitId dst);
DriveBitId pop_edge(DriveBitId src);
void clear(DriveBitId src);
bool contains(DriveBitId src);
int count(DriveBitId src);
DriveBitId at(DriveBitId src, int index);
};
// The following two maps maintain a sparse DriveBit to DriveBitId mapping.
// This saves a lot of memory compared to a `dict<DriveBit, DriveBitId>` or
// `idict<DriveBit>`.
// Maps wires to the first DriveBitId of the consecutive range used for
// that wire.
dict<Wire *, DriveBitId> wire_offsets;
// Maps cell ports to a the first DriveBitId of the consecutive range used
// for that cell port.
dict<pair<Cell *, IdString>, DriveBitId> port_offsets;
// For the inverse map that maps DriveBitIds back to DriveBits we use a
// sorted map containing only the first DriveBit for each wire and cell
// port.
std::map<DriveBitId, DriveBit> drive_bits;
// As a memory optimization for gate level net lists we store single-bit
// wires and cell ports in a `dict` which requires less memory and fewer
// allocations than `std::map` but doesn't support the kind of lookups we
// need for a sparse coarse grained mapping.
dict<DriveBitId, DriveBit> isolated_drive_bits;
// Used for allocating DriveBitIds, none and constant states use a fixewd
// mapping to the first few ids, which we need to skip.
int next_offset = 1 + (int)State::Sm;
// Union-Find over directly connected bits that share the same single
// driver or are undriven. We never merge connections between drivers
// and/or kept wires.
mfp<DriveBitId> same_driver;
// For each bit, store a set of connected driver bits for which the
// explicit connection should be preserved and the driving direction is
// locally unambiguous (one side only drives or requires a driven value).
DriveBitGraph connected_drivers;
// For each bit, store a set of connected driver bits for which the
// explicit connection should be preserved and the driving direction is
// locally ambiguous. Every such ambiguous connection is also present in
// the reverse direction and has to be resolved when querying drivers.
DriveBitGraph connected_undirected;
// Subset of `connected_undirected` for caching the resolved driving
// direction. In case multiple drivers are present this can still contain
// both orientations of a single connection, but for a single driver only
// one will be present.
DriveBitGraph connected_oriented;
// Stores for which bits we already resolved the orientation (cached in
// `connected_oriented`).
pool<DriveBitId> oriented_present;
enum class BitMode {
NONE = 0, // Not driven, no need to keep wire
DRIVEN = 1, // Not driven, uses a value driven elsewhere
DRIVEN_UNIQUE = 2, // Uses a value driven elsewhere, has at most one direct connection
KEEP = 3, // Wire that should be kept
TRISTATE = 4, // Can drive a value but can also use a value driven elsewhere
DRIVER = 5, // Drives a value
};
BitMode bit_mode(DriveBit const &bit);
DriveBitId id_from_drive_bit(DriveBit const &bit);
DriveBit drive_bit_from_id(DriveBitId id);
void connect_directed_merge(DriveBitId driven_id, DriveBitId driver_id);
void connect_directed_buffer(DriveBitId driven_id, DriveBitId driver_id);
void connect_undirected(DriveBitId a_id, DriveBitId b_id);
public:
void add(Module *module);
// Add a single bit connection to the driver map.
void add(DriveBit const &a, DriveBit const &b);
template<typename T>
static constexpr bool is_sig_type() {
return
std::is_same<T, SigSpec>::value ||
std::is_same<T, SigChunk>::value ||
std::is_same<T, DriveSpec>::value ||
std::is_same<T, DriveChunk>::value ||
std::is_same<T, DriveChunkPort>::value ||
std::is_same<T, DriveChunkWire>::value ||
std::is_same<T, Const>::value;
}
// We use the enable_if to produce better compiler errors when unsupported
// types are used
template<typename T, typename U>
typename std::enable_if<is_sig_type<T>() && is_sig_type<U>()>::type
add(T const &a, U const &b)
{
log_assert(a.size() == b.size());
for (int i = 0; i != GetSize(a); ++i)
add(DriveBit(a[i]), DriveBit(b[i]));
}
// Specialized version that avoids unpacking
void add(SigSpec const &a, SigSpec const &b);
private:
void add_port(Cell *cell, IdString const &port, SigSpec const &b);
// Only used a local variables in `orient_undirected`, always cleared, only
// stored to reduce allocations.
pool<DriveBitId> orient_undirected_seen;
pool<DriveBitId> orient_undirected_drivers;
dict<DriveBitId, int> orient_undirected_distance;
void orient_undirected(DriveBitId id);
public:
DriveBit operator()(DriveBit const &bit);
DriveSpec operator()(DriveSpec spec);
private:
bool keep_wire(Wire *wire) {
// TODO configurable
return wire->has_attribute(ID(keep));
}
};
YOSYS_NAMESPACE_END
#endif