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);
}