Sum constructor
- List<
SumInterface> interfaces, { - dynamic initialValue = 0,
- dynamic maxValue,
- dynamic minValue = 0,
- int? width,
- bool saturates = false,
- String name = 'sum',
Computes a sum across the provided interfaces
.
The width
can be either explicitly provided or inferred from other
values such as a maxValue
, minValue
, or initialValue
that contain
width information (e.g. a LogicValue), or by making it large enough to
fit maxValue
, or by inspecting widths of interfaces
. There must be
enough information provided to determine the width
.
If no maxValue
is provided, one will be inferred by the maximum that can
fit inside of the width
.
It is expected that maxValue
is at least minValue
, or else results may
be unpredictable.
If saturates
is true
, then it will saturate at the maxValue
and
minValue
. If false
, will wrap around (overflow/underflow) at the
maxValue
and minValue
. The equalsMax, equalsMin, overflowed,
and underflowed outputs can be used to determine if the sum is at the
maximum, minimum, (would have) overflowed, or (would have) underflowed,
respectively.
Implementation
Sum(
super.interfaces, {
dynamic initialValue = 0,
super.maxValue,
super.minValue,
super.width,
super.saturates,
super.name = 'sum',
}) : super(initialValue: initialValue) {
addOutput('sum', width: width);
var maxPosMagnitude = SummationBase.biggestVal(width);
var maxNegMagnitude = BigInt.zero;
for (final intf in interfaces) {
final maxMagnitude = intf.fixedAmount != null
? intf.amount.value.toBigInt()
: SummationBase.biggestVal(intf.width);
if (intf.increments) {
maxPosMagnitude += maxMagnitude;
} else {
maxNegMagnitude += maxMagnitude;
}
}
// also consider that initialValue may be less than min or more than max
final maxInitialValueMagnitude = initialValue is Logic
? SummationBase.biggestVal(initialValue.width)
: LogicValue.ofInferWidth(initialValue).toBigInt();
maxPosMagnitude += maxInitialValueMagnitude;
maxNegMagnitude += maxInitialValueMagnitude;
// calculate the largest number that we could have in intermediate
final internalWidth = max(
(maxPosMagnitude + maxNegMagnitude + BigInt.one).bitLength, width + 1);
final initialValueLogicExt = initialValueLogic.zeroExtend(internalWidth);
final minValueLogicExt = minValueLogic.zeroExtend(internalWidth);
final maxValueLogicExt = maxValueLogic.zeroExtend(internalWidth);
// lazy range so that it's not generated if not necessary
late final range = Logic(name: 'range', width: internalWidth)
..gets(maxValueLogicExt - minValueLogicExt + 1);
final zeroPoint = Logic(name: 'zeroPoint', width: internalWidth)
..gets(Const(maxNegMagnitude, width: internalWidth));
final upperSaturation = Logic(name: 'upperSaturation', width: internalWidth)
..gets(maxValueLogicExt + zeroPoint);
final lowerSaturation = Logic(name: 'lowerSaturation', width: internalWidth)
..gets(minValueLogicExt + zeroPoint);
final internalValue = Logic(name: 'internalValue', width: internalWidth);
sum <= (internalValue - zeroPoint).getRange(0, width);
final preAdjustmentValue =
Logic(name: 'preAdjustmentValue', width: internalWidth);
// here we use an `ssa` block to iteratively update the value of
// `internalValue` based on the adjustments from the interfaces and
// saturation/roll-over behavior
//
// For more details, see:
// https://intel.github.io/rohd-website/blog/combinational-ssa/
Combinational.ssa((s) => [
// initialize
s(internalValue) < initialValueLogicExt + zeroPoint,
// perform increments and decrements per-interface
...interfaces
.map((e) => e._combAdjustments(s, internalValue))
.flattened,
// identify if we're at a max/min case
overflowed < s(internalValue).gt(upperSaturation),
underflowed < s(internalValue).lt(lowerSaturation),
// useful as an internal node for debug/visibility
preAdjustmentValue < s(internalValue),
// handle saturation or over/underflow
If.block([
Iff.s(
overflowed,
s(internalValue) <
(saturates
? upperSaturation
: ((s(internalValue) - upperSaturation - 1) % range +
lowerSaturation)),
),
ElseIf.s(
underflowed,
s(internalValue) <
(saturates
? lowerSaturation
: (upperSaturation -
((lowerSaturation - s(internalValue) - 1) % range))),
)
]),
]);
equalsMax <= internalValue.eq(upperSaturation);
equalsMin <= internalValue.eq(lowerSaturation);
}