Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
292bf2d
Avoid Module creation for Constant Gate scenarios
mjayasim9 Apr 5, 2024
81734f2
Removed extra import
mjayasim9 Apr 5, 2024
efa4b95
Added changes based on comments
mjayasim9 Apr 8, 2024
7a3974a
Merge branch 'main' of https://github.com/mjayasim9/rohd into ConstGates
mjayasim9 Jun 18, 2024
cbf0c26
Merge branch 'intel:main' into ConstGates
mjayasim9 Jul 9, 2024
8e7789b
Added fixes based on feedback
mjayasim9 Jul 9, 2024
abb0d87
Merge branch 'main' of https://github.com/mjayasim9/rohd into ConstGates
mjayasim9 Jul 9, 2024
4393d10
Merge branch 'ConstGates' of https://github.com/mjayasim9/rohd into C…
mjayasim9 Jul 9, 2024
33faf6e
Merge branch 'main' of https://github.com/mjayasim9/rohd into ConstGates
mjayasim9 Aug 6, 2024
7862a94
Merge branch 'intel:main' into ConstGates
mjayasim9 Jan 21, 2025
afc42b6
Merge branch 'main' of https://github.com/mjayasim9/rohd into ConstGates
mjayasim9 Jan 21, 2025
7ba4e11
Avoid module creation for Const gate scenarios
mjayasim9 Jan 24, 2025
972c211
Merge branch 'ConstGates' of https://github.com/mjayasim9/rohd into C…
mjayasim9 Jan 24, 2025
137116e
Merge branch 'intel:main' into ConstGates
mjayasim9 Jul 23, 2025
6f2d6ad
Merge remote-tracking branch 'upstream/pr-481' into const_gate_optimi…
mkorbel1 Jul 15, 2026
6f3b23c
further address issues started to be addressed in pr #481
mkorbel1 Jul 15, 2026
96e90d8
more complete addressing of #486
mkorbel1 Jul 15, 2026
93cdaf8
fix tests that changed from optimization
mkorbel1 Jul 15, 2026
27f2de4
improve test coverage, improve coverage reporting to include branch
mkorbel1 Jul 16, 2026
a3245e0
Merge branch 'main' of https://github.com/intel/rohd into const_gate_…
mkorbel1 Jul 16, 2026
8535beb
fix copyright
mkorbel1 Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next release

- Improved simulation performance and generated outputs by avoiding module creation for four-state-safe operations involving `Const`s, zero shifts, and muxes with constant controls (<https://github.com/intel/rohd/issues/429>).
- Enforced that `Const` values cannot be changed through `put` or `inject`, including through another `Logic` driven by a `Const` (<https://github.com/intel/rohd/issues/486>).
- Added per-`Const` preferred radix control for generated literals, normalized `Const` names to reflect their displayed radix, and enhanced `LogicValue.toRadixString` to omit its width and radix decorator or digit separators (<https://github.com/intel/rohd/issues/601>).
- Improved generated SystemVerilog to collapse contiguous partial array and range assignments into packed slice assignments when safe (<https://github.com/intel/rohd/pull/676>).
- Added configurable explicit or implicit object and data types for generated SystemVerilog ports, defaulting to `input wire logic`, `output var logic`, and `inout wire logic` (<https://github.com/intel/rohd/pull/678>).
Expand Down
17 changes: 16 additions & 1 deletion lib/src/modules/gates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,22 @@ class LShift extends _ShiftGate {
/// ```SystemVerilog
/// control ? d1 : d0
/// ```
Logic mux(Logic control, Logic d1, Logic d0) => Mux(control, d1, d0).out;
///
/// If [control] is a valid [Const], returns the selected input directly.
Logic mux(Logic control, Logic d1, Logic d0) {
if (control.width != 1) {
throw PortWidthMismatchException(control, 1);
}
if (d0.width != d1.width) {
throw PortWidthMismatchException.equalWidth(d0, d1);
}

if (control is Const && control.value.isValid) {
return control.value == LogicValue.one ? d1 : d0;
}

return Mux(control, d1, d0).out;
}

/// A mux (multiplexer) module.
///
Expand Down
137 changes: 135 additions & 2 deletions lib/src/signals/const.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,16 @@ String _constName(LogicValue value, int? preferredRadix) {
}

/// Represents a [Logic] that never changes value.
///
/// Attempts to assign, [put], or [inject] a new value throw an
/// [UnassignableException], including through another [Logic] driven by the
/// [Const].
class Const extends Logic {
/// The explanation included when an attempt is made to modify a [Const].
static const _unassignableMessage =
'A `Const` value cannot be modified, including through another `Logic` '
'driven by it.';

/// The preferred radix for displaying this constant in generated outputs.
///
/// Supported radices are binary (2), octal (8), decimal (10), and
Expand Down Expand Up @@ -88,12 +97,136 @@ class Const extends Logic {
// we don't care about maintaining this node unless necessary
naming: Naming.unnamed,
) {
_wire.put(value, signalName: name);
_wire
..put(value, signalName: name)
..makeImmutable(this, reason: _unassignableMessage);

makeUnassignable(reason: '`Const` signals are unassignable.');
makeUnassignable(reason: _unassignableMessage);
}

@override
Const clone({String? name}) =>
Const(value, width: width, preferredRadix: preferredRadix);

@override
void put(dynamic val, {bool fill = false}) =>
throw UnassignableException(this, reason: _unassignableMessage);

@override
void inject(dynamic val, {bool fill = false}) =>
throw UnassignableException(this, reason: _unassignableMessage);

/// Verifies that [other] has the same width as this [Const].
void _checkMatchingWidth(Logic other) {
if (width != other.width) {
throw PortWidthMismatchException.equalWidth(this, other);
}
}

@override
Logic operator ~() => Const(~value);

@override
Logic operator &(Logic other) {
_checkMatchingWidth(other);

if (other is Const) {
return Const(value & other.value);
} else if (value.isValid && value.isZero) {
return this;
}

return And2Gate(this, other).out;
}

@override
Logic operator |(Logic other) {
_checkMatchingWidth(other);

if (other is Const) {
return Const(value | other.value);
} else if (value.isValid &&
value == LogicValue.filled(width, LogicValue.one)) {
return this;
}

return Or2Gate(this, other).out;
}

@override
Logic operator ^(Logic other) {
_checkMatchingWidth(other);

if (other is Const) {
return Const(value ^ other.value);
}

return Xor2Gate(this, other).out;
}

@override
Logic operator >>(dynamic other) {
if (Logic._isZeroShiftAmount(other)) {
return this;
} else if (other is Logic && other is! Const) {
return ARShift(this, other).out;
}

return Const(value >> (other is Const ? other.value : other));
}

@override
Logic operator <<(dynamic other) {
if (Logic._isZeroShiftAmount(other)) {
return this;
} else if (other is Logic && other is! Const) {
return LShift(this, other).out;
}

return Const(value << (other is Const ? other.value : other));
}

@override
Logic operator >>>(dynamic other) {
if (Logic._isZeroShiftAmount(other)) {
return this;
} else if (other is Logic && other is! Const) {
return RShift(this, other).out;
}

return Const(value >>> (other is Const ? other.value : other));
}

@override
Logic and() => Const(value.and());

@override
Logic or() => Const(value.or());

@override
Logic xor() => Const(value.xor());

@override
Logic eq(dynamic other) {
if (other is Logic) {
_checkMatchingWidth(other);
return other is Const
? Const(value.eq(other.value))
: Equals(this, other).out;
}

return Const(value.eq(LogicValue.of(other, width: width)));
}

@override
Logic neq(dynamic other) {
if (other is Logic) {
_checkMatchingWidth(other);
return other is Const
? Const(value.neq(other.value))
: NotEquals(this, other).out;
}

return Const(value.neq(LogicValue.of(other, width: width)));
}
}
39 changes: 37 additions & 2 deletions lib/src/signals/logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,22 @@ class Logic {
Logic operator ~() => NotGate(this).out;

/// Logical bitwise AND.
Logic operator &(Logic other) => And2Gate(this, other).out;
Logic operator &(Logic other) {
if (other is Const) {
return other & this;
}

return And2Gate(this, other).out;
}

/// Logical bitwise OR.
Logic operator |(Logic other) => Or2Gate(this, other).out;
Logic operator |(Logic other) {
if (other is Const) {
return other | this;
}

return Or2Gate(this, other).out;
}

/// Logical bitwise XOR.
Logic operator ^(Logic other) => Xor2Gate(this, other).out;
Expand Down Expand Up @@ -478,6 +490,10 @@ class Logic {
///
/// If [isNet] and [other] is constant, then the result will also be a net.
Logic operator >>(dynamic other) {
if (_isZeroShiftAmount(other)) {
return this;
}

if (isNet) {
// many SV simulators don't support shifting of nets, so default this
final shamt = _constShiftAmount(other);
Expand All @@ -498,6 +514,10 @@ class Logic {
///
/// If [isNet] and [other] is constant, then the result will also be a net.
Logic operator <<(dynamic other) {
if (_isZeroShiftAmount(other)) {
return this;
}

if (isNet) {
// many SV simulators don't support shifting of nets, so default this
final shamt = _constShiftAmount(other);
Expand All @@ -518,6 +538,10 @@ class Logic {
///
/// If [isNet] and [other] is constant, then the result will also be a net.
Logic operator >>>(dynamic other) {
if (_isZeroShiftAmount(other)) {
return this;
}

if (isNet) {
// many SV simulators don't support shifting of nets, so default this
final shamt = _constShiftAmount(other);
Expand All @@ -543,6 +567,17 @@ class Logic {
}
}

/// Indicates whether [other] represents a known constant shift of zero.
static bool _isZeroShiftAmount(dynamic other) {
if (other is Const) {
return other.value.isValid && other.value.isZero;
} else if (other is Logic) {
return false;
} else {
return LogicValue.ofInferWidth(other).isZero;
}
}

/// Unary AND.
Logic and() => AndUnary(this).out;

Expand Down
41 changes: 41 additions & 0 deletions lib/src/signals/wire.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@ class _Wire {
/// The current active value of this signal.
LogicValue _currentValue;

/// The [Logic] whose immutability makes this wire immutable.
Logic? _immutableOwner;

/// Additional context explaining why this wire is immutable.
String? _immutableReason;

/// Makes this wire immutable on behalf of [owner].
///
/// If this wire is already immutable, the original owner and reason are
/// preserved.
void makeImmutable(Logic owner, {String? reason}) {
_immutableOwner ??= owner;
_immutableReason ??= reason;
}

/// Throws an [UnassignableException] if this wire is immutable, identifying
/// [signalName] as the signal through which the update was attempted.
void _assertMutable({required String signalName}) {
final immutableOwner = _immutableOwner;
if (immutableOwner != null) {
final attemptedUpdateReason =
'Signal "$signalName" cannot be updated because it is driven by '
'immutable signal "$immutableOwner".';
throw UnassignableException(
immutableOwner,
reason: [
attemptedUpdateReason,
if (_immutableReason != null) _immutableReason,
].join(' '),
);
}
}

/// The last value of this signal before the [Simulator] tick.
///
/// This is useful for detecting when to trigger an edge.
Expand Down Expand Up @@ -143,6 +176,11 @@ class _Wire {
/// Tells this [_Wire] to adopt all the behavior of [other] so that
/// it can replace [other]. Returns the [_Wire] that has adopted everything.
_Wire _adopt(_Wire other) {
if (_immutableOwner == null && other._immutableOwner != null) {
_immutableOwner = other._immutableOwner;
_immutableReason = other._immutableReason;
}

_glitchController.emitter.adopt(other._glitchController.emitter);
other._migrateChangedTriggers(this);

Expand Down Expand Up @@ -218,6 +256,7 @@ class _Wire {
///
/// This function calls [put()] in [Simulator.injectAction()].
void inject(dynamic val, {required String signalName, bool fill = false}) {
_assertMutable(signalName: signalName);
Simulator.injectAction(() => put(val, signalName: signalName, fill: fill));
}

Expand All @@ -233,6 +272,8 @@ class _Wire {
/// This function is used for propagating glitches through connected signals.
/// Use this function for custom definitions of [Module] behavior.
void put(dynamic val, {required String signalName, bool fill = false}) {
_assertMutable(signalName: signalName);

var newValue = LogicValue.of(val, fill: fill, width: width);

if (newValue.width != width) {
Expand Down
Loading
Loading