mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1389 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1389 lines
		
	
	
	
		
			33 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 <string>
 | 
						|
#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;
 | 
						|
 | 
						|
std::string log_signal(DriveChunkWire const &chunk);
 | 
						|
std::string log_signal(DriveChunkPort const &chunk);
 | 
						|
std::string log_signal(DriveChunkMarker const &chunk);
 | 
						|
std::string log_signal(DriveChunk const &chunk);
 | 
						|
std::string 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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
 | 
						|
	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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
struct DriveBitMultiple
 | 
						|
{
 | 
						|
private:
 | 
						|
	pool<DriveBit> multiple_;
 | 
						|
 | 
						|
public:
 | 
						|
	DriveBitMultiple();
 | 
						|
	DriveBitMultiple(DriveBit const &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_;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
};
 | 
						|
 | 
						|
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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
	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_abort();
 | 
						|
	}
 | 
						|
 | 
						|
	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);
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
inline DriveBitMultiple::DriveBitMultiple() {}
 | 
						|
inline DriveBitMultiple::DriveBitMultiple(DriveBit const &single)
 | 
						|
{
 | 
						|
	multiple_.emplace(single);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
	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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
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;
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
};
 | 
						|
 | 
						|
struct DriveChunkMultiple
 | 
						|
{
 | 
						|
private:
 | 
						|
	mutable pool<DriveChunk> multiple_;
 | 
						|
	int width_;
 | 
						|
 | 
						|
public:
 | 
						|
	pool<DriveChunk> const &multiple() const { return multiple_; }
 | 
						|
 | 
						|
	DriveChunkMultiple(DriveBitMultiple const &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
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
};
 | 
						|
 | 
						|
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);
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
	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_abort();
 | 
						|
	}
 | 
						|
 | 
						|
	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_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; }
 | 
						|
 | 
						|
	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();
 | 
						|
		}
 | 
						|
		log_abort();
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
inline DriveChunkMultiple::DriveChunkMultiple(DriveBitMultiple const &bit)
 | 
						|
	: width_(1)
 | 
						|
{
 | 
						|
	for (auto const &bit : bit.multiple())
 | 
						|
		multiple_.emplace(bit);
 | 
						|
}
 | 
						|
 | 
						|
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); }
 | 
						|
 | 
						|
	void updhash() const {
 | 
						|
		if (hash_ != 0)
 | 
						|
			return;
 | 
						|
		pack();
 | 
						|
		hash_ = run_hash(chunks_);
 | 
						|
		hash_ |= (hash_ == 0);
 | 
						|
	}
 | 
						|
 | 
						|
	[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
 | 
						|
	bool operator==(DriveSpec const &other) const {
 | 
						|
		updhash();
 | 
						|
		other.updhash();
 | 
						|
		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; }
 | 
						|
		[[nodiscard]] Hasher hash_into(Hasher h) const;
 | 
						|
	};
 | 
						|
	// 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));
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
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
 |