diff --git a/kernel/drivertools.h b/kernel/drivertools.h index cc99207f629..cd0ac5399bd 100644 --- a/kernel/drivertools.h +++ b/kernel/drivertools.h @@ -28,8 +28,6 @@ YOSYS_NAMESPACE_BEGIN -// TODO move implementation into a .cc file - struct DriveBit; struct DriveChunkWire; @@ -103,399 +101,446 @@ struct DriveBitMarker struct DriveBitMultiple { private: - pool multiple_; + pool multiple_; public: - DriveBitMultiple(); - DriveBitMultiple(DriveBit const &single); + DriveBitMultiple(); + DriveBitMultiple(DriveBit const &single); - pool const &multiple() const; + pool const &multiple() const; - void merge(DriveBitMultiple const &other); - void merge(DriveBitMultiple &&other); - void merge(DriveBit const &single); - void merge(DriveBit &&single); + void merge(DriveBitMultiple const &other); + void merge(DriveBitMultiple &&other); + void merge(DriveBit const &single); + void merge(DriveBit &&single); - bool operator==(const DriveBitMultiple &other) const; + bool operator==(const DriveBitMultiple &other) const; - unsigned int hash() const; + unsigned int hash() const; }; struct DriveBit { private: - DriveType type_ = DriveType::NONE; - union - { - State constant_; - DriveBitWire wire_; - DriveBitPort port_; - DriveBitMarker marker_; - DriveBitMultiple multiple_; - }; + DriveType type_ = DriveType::NONE; + union + { + State constant_; + DriveBitWire wire_; + DriveBitPort port_; + DriveBitMarker marker_; + DriveBitMultiple multiple_; + }; public: - DriveBit(); + DriveBit(); - DriveBit(SigBit const &bit); + DriveBit(SigBit const &bit); - DriveBit(DriveBit const &other); - DriveBit(DriveBit &&other); + DriveBit(DriveBit const &other); + DriveBit(DriveBit &&other); - DriveBit(State constant); - DriveBit(DriveBitWire const &wire); - DriveBit(DriveBitWire &&wire); - DriveBit(DriveBitPort const &port); - DriveBit(DriveBitPort &&port); - DriveBit(DriveBitMarker const &marker); - DriveBit(DriveBitMarker &&marker); - DriveBit(DriveBitMultiple const &multiple); - DriveBit(DriveBitMultiple &&multiple); + DriveBit(State constant); + DriveBit(DriveBitWire const &wire); + DriveBit(DriveBitWire &&wire); + DriveBit(DriveBitPort const &port); + DriveBit(DriveBitPort &&port); + DriveBit(DriveBitMarker const &marker); + DriveBit(DriveBitMarker &&marker); + DriveBit(DriveBitMultiple const &multiple); + DriveBit(DriveBitMultiple &&multiple); - ~DriveBit(); + ~DriveBit(); - void set_none(); + void set_none(); - DriveBit &operator=(DriveBit const &other); - DriveBit &operator=(DriveBit &&other); + DriveBit &operator=(DriveBit const &other); + DriveBit &operator=(DriveBit &&other); - DriveBit &operator=(State constant); - DriveBit &operator=(DriveBitWire const &wire); - DriveBit &operator=(DriveBitWire &&wire); - DriveBit &operator=(DriveBitPort const &port); - DriveBit &operator=(DriveBitPort &&port); - DriveBit &operator=(DriveBitMarker const &marker); - DriveBit &operator=(DriveBitMarker &&marker); - DriveBit &operator=(DriveBitMultiple const &multiple); - DriveBit &operator=(DriveBitMultiple &&multiple); + DriveBit &operator=(State constant); + DriveBit &operator=(DriveBitWire const &wire); + DriveBit &operator=(DriveBitWire &&wire); + DriveBit &operator=(DriveBitPort const &port); + DriveBit &operator=(DriveBitPort &&port); + DriveBit &operator=(DriveBitMarker const &marker); + DriveBit &operator=(DriveBitMarker &&marker); + DriveBit &operator=(DriveBitMultiple const &multiple); + DriveBit &operator=(DriveBitMultiple &&multiple); - unsigned int hash() const; + unsigned int hash() const; - bool operator==(const DriveBit &other) const; - bool operator!=(const DriveBit &other) const; + bool operator==(const DriveBit &other) const; + bool operator!=(const DriveBit &other) const; - bool operator<(const DriveBit &other) const; + bool operator<(const DriveBit &other) const; - DriveType type() const; + DriveType type() const; - bool is_none() const; - bool is_constant() const; - bool is_wire() const; - bool is_port() const; - bool is_marker() const; - bool is_multiple() const; + bool is_none() const; + bool is_constant() const; + bool is_wire() const; + bool is_port() const; + bool is_marker() const; + bool is_multiple() const; - State &constant(); - State const &constant() const; - DriveBitWire &wire(); - DriveBitWire const &wire() const; - DriveBitPort &port(); - DriveBitPort const &port() const; - DriveBitMarker &marker(); - DriveBitMarker const &marker() const; - DriveBitMultiple &multiple(); - DriveBitMultiple const &multiple() const; + State &constant(); + State const &constant() const; + DriveBitWire &wire(); + DriveBitWire const &wire() const; + DriveBitPort &port(); + DriveBitPort const &port() const; + DriveBitMarker &marker(); + DriveBitMarker const &marker() const; + DriveBitMultiple &multiple(); + DriveBitMultiple const &multiple() const; - void merge(DriveBit const &other); + void merge(DriveBit const &other); }; struct DriveChunkWire { - Wire *wire; - int offset; - int width; - - DriveChunkWire(Wire *wire, int offset, int width); - DriveChunkWire(DriveBitWire const &bit); - - int size() const; - DriveBitWire operator[](int i) const; - bool can_append(DriveBitWire const &bit) const; - bool try_append(DriveBitWire const &bit); - bool try_append(DriveChunkWire const &chunk); - bool is_whole() const; - bool operator==(const DriveChunkWire &other) const; - bool operator<(const DriveChunkWire &other) const; - unsigned int hash() const; - explicit operator SigChunk() const; + Wire *wire; + int offset; + int width; + + DriveChunkWire(Wire *wire, int offset, int width); + DriveChunkWire(DriveBitWire const &bit); + + int size() const; + DriveBitWire operator[](int i) const; + bool can_append(DriveBitWire const &bit) const; + bool try_append(DriveBitWire const &bit); + bool try_append(DriveChunkWire const &chunk); + bool is_whole() const; + bool operator==(const DriveChunkWire &other) const; + bool operator<(const DriveChunkWire &other) const; + unsigned int hash() const; + explicit operator SigChunk() const; }; struct DriveChunkPort { - Cell *cell; - IdString port; - int offset; - int width; - - DriveChunkPort(Cell *cell, IdString port, int offset, int width); - DriveChunkPort(Cell *cell, IdString port); - DriveChunkPort(Cell *cell, std::pair const &conn); - DriveChunkPort(DriveBitPort const &bit); - - int size() const; - DriveBitPort operator[](int i) const; - bool can_append(DriveBitPort const &bit) const; - bool try_append(DriveBitPort const &bit); - bool try_append(DriveChunkPort const &chunk); - bool is_whole() const; - bool operator==(const DriveChunkPort &other) const; - bool operator<(const DriveChunkPort &other) const; - unsigned int hash() const; + Cell *cell; + IdString port; + int offset; + int width; + + DriveChunkPort(Cell *cell, IdString port, int offset, int width); + DriveChunkPort(Cell *cell, IdString port); + DriveChunkPort(Cell *cell, std::pair const &conn); + DriveChunkPort(DriveBitPort const &bit); + + int size() const; + DriveBitPort operator[](int i) const; + bool can_append(DriveBitPort const &bit) const; + bool try_append(DriveBitPort const &bit); + bool try_append(DriveChunkPort const &chunk); + bool is_whole() const; + bool operator==(const DriveChunkPort &other) const; + bool operator<(const DriveChunkPort &other) const; + unsigned int hash() const; }; struct DriveChunkMarker { - int marker; - int offset; - int width; - - DriveChunkMarker(int marker, int offset, int width); - DriveChunkMarker(DriveBitMarker const &bit); - - int size() const; - DriveBitMarker operator[](int i) const; - 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; - bool operator<(const DriveChunkMarker &other) const; - unsigned int hash() const; + int marker; + int offset; + int width; + + DriveChunkMarker(int marker, int offset, int width); + DriveChunkMarker(DriveBitMarker const &bit); + + int size() const; + DriveBitMarker operator[](int i) const; + 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; + bool operator<(const DriveChunkMarker &other) const; + unsigned int hash() const; }; struct DriveChunkMultiple { private: - mutable pool multiple_; - int width_; + mutable pool multiple_; + int width_; public: - pool const &multiple() const; - - DriveChunkMultiple(DriveBitMultiple const &bit); - - int size() const; - 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; - bool operator<(const DriveChunkMultiple &other) const; - unsigned int hash() const; + pool const &multiple() const; + + DriveChunkMultiple(DriveBitMultiple const &bit); + + int size() const; + 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; + bool operator<(const DriveChunkMultiple &other) const; + unsigned int hash() const; }; struct DriveChunk { private: - DriveType type_ = DriveType::NONE; - union - { - int none_; - Const constant_; - DriveChunkWire wire_; - DriveChunkPort port_; - DriveChunkMarker marker_; - DriveChunkMultiple multiple_; - }; + DriveType type_ = DriveType::NONE; + union + { + int none_; + Const constant_; + DriveChunkWire wire_; + DriveChunkPort port_; + DriveChunkMarker marker_; + DriveChunkMultiple multiple_; + }; public: - DriveChunk(); - DriveChunk(DriveChunk const &other); - DriveChunk(DriveChunk &&other); - DriveChunk(DriveBit const &other); - DriveChunk(Const const &constant); - DriveChunk(Const &&constant); - DriveChunk(DriveChunkWire const &wire); - DriveChunk(DriveChunkWire &&wire); - DriveChunk(DriveChunkPort const &port); - DriveChunk(DriveChunkPort &&port); - DriveChunk(DriveChunkMarker const &marker); - DriveChunk(DriveChunkMarker &&marker); - DriveChunk(DriveChunkMultiple const &multiple); - DriveChunk(DriveChunkMultiple &&multiple); - ~DriveChunk(); - - DriveBit operator[](int i) const; - void set_none(int width = 0); - DriveChunk &operator=(DriveChunk const &other); - DriveChunk &operator=(DriveChunk &&other); - DriveChunk &operator=(Const const &constant); - DriveChunk &operator=(Const &&constant); - DriveChunk &operator=(DriveChunkWire const &wire); - DriveChunk &operator=(DriveChunkWire &&wire); - DriveChunk &operator=(DriveChunkPort const &port); - DriveChunk &operator=(DriveChunkPort &&port); - DriveChunk &operator=(DriveChunkMarker const &marker); - DriveChunk &operator=(DriveChunkMarker &&marker); - DriveChunk &operator=(DriveChunkMultiple const &multiple); - DriveChunk &operator=(DriveChunkMultiple &&multiple); - DriveChunk &operator=(DriveBit const &other); - bool can_append(DriveBit const &bit) const; - bool try_append(DriveBit const &bit); - bool try_append(DriveChunk const &chunk); - unsigned int hash() const; - bool operator==(const DriveChunk &other) const; - bool operator!=(const DriveChunk &other) const; - bool operator<(const DriveChunk &other) const; - DriveType type() const; - bool is_none() const; - bool is_constant() const; - bool is_wire() const; - bool is_port() const; - bool is_marker() const; - bool is_multiple() const; - Const &constant(); - Const const &constant() const; - DriveChunkWire &wire(); - DriveChunkWire const &wire() const; - DriveChunkPort &port(); - DriveChunkPort const &port() const; - DriveChunkMarker &marker(); - DriveChunkMarker const &marker() const; - DriveChunkMultiple &multiple(); - DriveChunkMultiple const &multiple() const; - int size() const; + DriveChunk(); + DriveChunk(DriveChunk const &other); + DriveChunk(DriveChunk &&other); + DriveChunk(DriveBit const &other); + DriveChunk(Const const &constant); + DriveChunk(Const &&constant); + DriveChunk(DriveChunkWire const &wire); + DriveChunk(DriveChunkWire &&wire); + DriveChunk(DriveChunkPort const &port); + DriveChunk(DriveChunkPort &&port); + DriveChunk(DriveChunkMarker const &marker); + DriveChunk(DriveChunkMarker &&marker); + DriveChunk(DriveChunkMultiple const &multiple); + DriveChunk(DriveChunkMultiple &&multiple); + ~DriveChunk(); + + DriveBit operator[](int i) const; + void set_none(int width = 0); + DriveChunk &operator=(DriveChunk const &other); + DriveChunk &operator=(DriveChunk &&other); + DriveChunk &operator=(Const const &constant); + DriveChunk &operator=(Const &&constant); + DriveChunk &operator=(DriveChunkWire const &wire); + DriveChunk &operator=(DriveChunkWire &&wire); + DriveChunk &operator=(DriveChunkPort const &port); + DriveChunk &operator=(DriveChunkPort &&port); + DriveChunk &operator=(DriveChunkMarker const &marker); + DriveChunk &operator=(DriveChunkMarker &&marker); + DriveChunk &operator=(DriveChunkMultiple const &multiple); + DriveChunk &operator=(DriveChunkMultiple &&multiple); + DriveChunk &operator=(DriveBit const &other); + bool can_append(DriveBit const &bit) const; + bool try_append(DriveBit const &bit); + bool try_append(DriveChunk const &chunk); + unsigned int hash() const; + bool operator==(const DriveChunk &other) const; + bool operator!=(const DriveChunk &other) const; + bool operator<(const DriveChunk &other) const; + DriveType type() const; + bool is_none() const; + bool is_constant() const; + bool is_wire() const; + bool is_port() const; + bool is_marker() const; + bool is_multiple() const; + Const &constant(); + Const const &constant() const; + DriveChunkWire &wire(); + DriveChunkWire const &wire() const; + DriveChunkPort &port(); + DriveChunkPort const &port() const; + DriveChunkMarker &marker(); + DriveChunkMarker const &marker() const; + DriveChunkMultiple &multiple(); + DriveChunkMultiple const &multiple() const; + int size() const; }; struct DriveSpec { private: - int width_ = 0; - mutable std::vector chunks_; - mutable std::vector bits_; - mutable unsigned int hash_ = 0; + int width_ = 0; + mutable std::vector chunks_; + mutable std::vector bits_; + mutable unsigned int hash_ = 0; public: - inline bool packed() const; + inline bool packed() const; - DriveSpec(); - DriveSpec(DriveChunk const &chunk); - DriveSpec(DriveChunkWire const &chunk); - DriveSpec(DriveChunkPort const &chunk); - DriveSpec(DriveChunkMarker const &chunk); - DriveSpec(DriveChunkMultiple const &chunk); + DriveSpec(); + DriveSpec(DriveChunk const &chunk); + DriveSpec(DriveChunkWire const &chunk); + DriveSpec(DriveChunkPort const &chunk); + DriveSpec(DriveChunkMarker const &chunk); + DriveSpec(DriveChunkMultiple const &chunk); - DriveSpec(DriveBit const &bit); - DriveSpec(DriveBitWire const &bit); - DriveSpec(DriveBitPort const &bit); - DriveSpec(DriveBitMarker const &bit); - DriveSpec(DriveBitMultiple const &bit); + DriveSpec(DriveBit const &bit); + DriveSpec(DriveBitWire const &bit); + DriveSpec(DriveBitPort const &bit); + DriveSpec(DriveBitMarker const &bit); + DriveSpec(DriveBitMultiple const &bit); - DriveSpec(std::vector const &chunks); - DriveSpec(std::vector const &bits); + DriveSpec(std::vector const &chunks); + DriveSpec(std::vector const &bits); - std::vector const &chunks() const; - std::vector const &bits() const; + std::vector const &chunks() const; + std::vector const &bits() const; - int size() const; + int size() const; - void append(DriveBit const &bit); - void append(DriveChunk const &chunk); + void append(DriveBit const &bit); + void append(DriveChunk const &chunk); - void pack() const; - void unpack() const; + void pack() const; + void unpack() const; - DriveBit &operator[](int index); - const DriveBit &operator[](int index) const; + DriveBit &operator[](int index); + const DriveBit &operator[](int index) const; - void clear(); + void clear(); - DriveSpec &operator=(DriveChunk const &chunk); - DriveSpec &operator=(DriveChunkWire const &chunk); - DriveSpec &operator=(DriveChunkPort const &chunk); - DriveSpec &operator=(DriveChunkMarker const &chunk); - DriveSpec &operator=(DriveChunkMultiple const &chunk); + DriveSpec &operator=(DriveChunk const &chunk); + DriveSpec &operator=(DriveChunkWire const &chunk); + DriveSpec &operator=(DriveChunkPort const &chunk); + DriveSpec &operator=(DriveChunkMarker const &chunk); + DriveSpec &operator=(DriveChunkMultiple const &chunk); - DriveSpec &operator=(DriveBit const &bit); - DriveSpec &operator=(DriveBitWire const &bit); - DriveSpec &operator=(DriveBitPort const &bit); - DriveSpec &operator=(DriveBitMarker const &bit); - DriveSpec &operator=(DriveBitMultiple const &bit); + DriveSpec &operator=(DriveBit const &bit); + DriveSpec &operator=(DriveBitWire const &bit); + DriveSpec &operator=(DriveBitPort const &bit); + DriveSpec &operator=(DriveBitMarker const &bit); + DriveSpec &operator=(DriveBitMultiple const &bit); - unsigned int hash() const; + unsigned int hash() const; - bool operator==(DriveSpec const &other) const; + bool operator==(DriveSpec const &other) const; private: - void compute_width(); + void compute_width(); }; struct DriverMap { - CellTypes celltypes; + CellTypes celltypes; - DriverMap(); - DriverMap(Design *design); + DriverMap(); + DriverMap(Design *design); private: - struct DriveBitId - { - int id; - - DriveBitId(); - DriveBitId(int id); - - bool operator==(const DriveBitId &other) const; - bool operator!=(const DriveBitId &other) const; - bool operator<(const DriveBitId &other) const; - unsigned int hash() const; - }; - - struct DriveBitGraph - { - dict first_edges; - dict second_edges; - dict> 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); - }; - - dict wire_offsets; - dict, DriveBitId> port_offsets; - std::map drive_bits; - dict isolated_drive_bits; - int next_offset; - mfp same_driver; - DriveBitGraph connected_drivers; - DriveBitGraph connected_undirected; - DriveBitGraph connected_oriented; - pool oriented_present; - - enum class BitMode - { - NONE = 0, - DRIVEN = 1, - DRIVEN_UNIQUE = 2, - KEEP = 3, - TRISTATE = 4, - DRIVER = 5, - }; - - 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); - void add_port(Cell *cell, IdString const &port, SigSpec const &b); - void orient_undirected(DriveBitId id); - bool keep_wire(Wire *wire); - // Only used a local variables in `orient_undirected`, always cleared, only - // stored to reduce allocations. - pool orient_undirected_seen; - pool orient_undirected_drivers; - dict orient_undirected_distance; + // Internally we represent all DriveBits by mapping them to DriveBitIds + // which use less memory and are cheaper to compare. + struct DriveBitId + { + int id; + + DriveBitId(); + DriveBitId(int id); + + bool operator==(const DriveBitId &other) const; + bool operator!=(const DriveBitId &other) const; + bool operator<(const DriveBitId &other) const; + unsigned int hash() const; + }; + + // Essentially a dict> but using less memory + // and fewer allocations + struct DriveBitGraph + { + dict first_edges; + dict second_edges; + dict> 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` or + // `idict`. + + // Maps wires to the first DriveBitId of the consecutive range used for + // that wire. + dict wire_offsets; + + // Maps cell ports to a the first DriveBitId of the consecutive range used + // for that cell port. + dict, 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 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 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 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 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); + void add_port(Cell *cell, IdString const &port, SigSpec const &b); + void orient_undirected(DriveBitId id); + bool keep_wire(Wire *wire); + + // Only used a local variables in `orient_undirected`, always cleared, only + // stored to reduce allocations. + pool orient_undirected_seen; + pool orient_undirected_drivers; + dict orient_undirected_distance; public: - void add(Module *module); - void add(DriveBit const &a, DriveBit const &b); + void add(Module *module); + void add(DriveBit const &a, DriveBit const &b); template @@ -520,10 +565,11 @@ struct DriverMap for (int i = 0; i != GetSize(a); ++i) add(DriveBit(a[i]), DriveBit(b[i])); } - - void add(SigSpec const &a, SigSpec const &b); - DriveBit operator()(DriveBit const &bit); - DriveSpec operator()(DriveSpec spec); + + // Specialized version that avoids unpacking + void add(SigSpec const &a, SigSpec const &b); + DriveBit operator()(DriveBit const &bit); + DriveSpec operator()(DriveSpec spec); }; YOSYS_NAMESPACE_END