Skip to content

Commit c4157d1

Browse files
mkorbel1mjayasim9
authored andcommitted
Fix bug where generated SV has lint issues with plus and shift-left due to SV width expansion (intel#423)
1 parent 7d63353 commit c4157d1

File tree

4 files changed

+82
-30
lines changed

4 files changed

+82
-30
lines changed

lib/src/modules/gates.dart

+51-18
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ abstract class _TwoInputBitwiseGate extends Module with InlineSystemVerilog {
141141
late final Logic _in1 = input(_in1Name);
142142

143143
/// The output of this gate.
144-
late final Logic out = output(_outName);
144+
late final Logic out = _outputSvWidthExpansion
145+
// this is sub-optimal, but it's tricky to make special SV for it
146+
? BusSubset(output(_outName), 0, width - 1).subset
147+
: output(_outName);
145148

146149
/// The output of this gate.
147150
///
@@ -155,6 +158,13 @@ abstract class _TwoInputBitwiseGate extends Module with InlineSystemVerilog {
155158
/// The `String` representing the operation to perform in generated code.
156159
final String _opStr;
157160

161+
/// The width of the inputs and outputs for this operation.
162+
final int width;
163+
164+
/// If true, then the output generated SystemVerilog may have a larger width
165+
/// than the inputs, which should be considered in generated verilog.
166+
final bool _outputSvWidthExpansion;
167+
158168
/// Constructs a two-input bitwise gate for an abitrary custom functional
159169
/// implementation.
160170
///
@@ -163,22 +173,23 @@ abstract class _TwoInputBitwiseGate extends Module with InlineSystemVerilog {
163173
/// String between the two input signal names (e.g. if [_opStr] was "&",
164174
/// generated SystemVerilog may look like "a & b").
165175
_TwoInputBitwiseGate(this._op, this._opStr, Logic in0, dynamic in1,
166-
{String name = 'gate2'})
167-
: super(name: name) {
176+
{String name = 'gate2', bool outputSvWidthExpansion = false})
177+
: width = in0.width,
178+
_outputSvWidthExpansion = outputSvWidthExpansion,
179+
super(name: name) {
168180
if (in1 is Logic && in0.width != in1.width) {
169-
throw Exception('Input widths must match,'
170-
' but found $in0 and $in1 with different widths.');
181+
throw PortWidthMismatchException.equalWidth(in0, in1);
171182
}
172183

173-
final in1Logic = in1 is Logic ? in1 : Const(in1, width: in0.width);
184+
final in1Logic = in1 is Logic ? in1 : Const(in1, width: width);
174185

175186
_in0Name = Module.unpreferredName('in0_${in0.name}');
176187
_in1Name = Module.unpreferredName('in1_${in1Logic.name}');
177188
_outName = Module.unpreferredName('${in0.name}_${name}_${in1Logic.name}');
178189

179-
addInput(_in0Name, in0, width: in0.width);
180-
addInput(_in1Name, in1Logic, width: in1Logic.width);
181-
addOutput(_outName, width: in0.width);
190+
addInput(_in0Name, in0, width: width);
191+
addInput(_in1Name, in1Logic, width: width);
192+
addOutput(_outName, width: width + (_outputSvWidthExpansion ? 1 : 0));
182193

183194
_setup();
184195
}
@@ -315,7 +326,7 @@ class _ShiftGate extends Module with InlineSystemVerilog {
315326
late final String _inName;
316327

317328
/// Name for the shift amount input port of this module.
318-
late final String _shiftAmountName;
329+
late String _shiftAmountName;
319330

320331
/// Name for the output port of this module.
321332
late final String _outName;
@@ -338,6 +349,13 @@ class _ShiftGate extends Module with InlineSystemVerilog {
338349
/// Whether or not this gate operates on a signed number.
339350
final bool signed;
340351

352+
/// The width of the output for this operation.
353+
final int width;
354+
355+
/// If true, then the output generated SystemVerilog may have a larger width
356+
/// than the inputs, which should be considered in generated verilog.
357+
final bool _outputSvWidthExpansion;
358+
341359
/// Constructs a two-input shift gate for an abitrary custom functional
342360
/// implementation.
343361
///
@@ -346,21 +364,34 @@ class _ShiftGate extends Module with InlineSystemVerilog {
346364
/// String between the two input signal names (e.g. if [_opStr] was ">>",
347365
/// generated SystemVerilog may look like "a >> b").
348366
_ShiftGate(this._op, this._opStr, Logic in_, dynamic shiftAmount,
349-
{String name = 'gate2', this.signed = false})
350-
: super(name: name) {
367+
{String name = 'gate2',
368+
this.signed = false,
369+
bool outputSvWidthExpansion = false})
370+
: width = in_.width,
371+
_outputSvWidthExpansion = outputSvWidthExpansion,
372+
super(name: name) {
351373
final shiftAmountLogic = shiftAmount is Logic
352374
? shiftAmount
353-
: Const(LogicValue.ofInferWidth(shiftAmount));
375+
: Const(_outputSvWidthExpansion
376+
? LogicValue.of(shiftAmount, width: width)
377+
: LogicValue.ofInferWidth(shiftAmount));
354378

355379
_inName = Module.unpreferredName('in_${in_.name}');
356-
_shiftAmountName =
357-
Module.unpreferredName('shiftAmount_${shiftAmountLogic.name}');
380+
381+
_shiftAmountName = 'shiftAmount_${shiftAmountLogic.name}';
382+
if (!_outputSvWidthExpansion) {
383+
// if we have width expansion, then we want to avoid any constants as
384+
// the shift amount since that gets complicated...
385+
// so as a proxy for now, just always shove a shiftAmount here
386+
_shiftAmountName = Module.unpreferredName(_shiftAmountName);
387+
}
388+
358389
_outName =
359390
Module.unpreferredName('${in_.name}_${name}_${shiftAmountLogic.name}');
360391

361392
addInput(_inName, in_, width: in_.width);
362393
addInput(_shiftAmountName, shiftAmountLogic, width: shiftAmountLogic.width);
363-
addOutput(_outName, width: in_.width);
394+
addOutput(_outName, width: width);
364395

365396
_setup();
366397
}
@@ -437,7 +468,8 @@ class Add extends _TwoInputBitwiseGate {
437468
///
438469
/// [in1] can be either a [Logic] or [int].
439470
Add(Logic in0, dynamic in1, {String name = 'add'})
440-
: super((a, b) => a + b, '+', in0, in1, name: name);
471+
: super((a, b) => a + b, '+', in0, in1,
472+
name: name, outputSvWidthExpansion: true);
441473
}
442474

443475
/// A two-input subtraction module.
@@ -574,7 +606,8 @@ class ARShift extends _ShiftGate {
574606
class LShift extends _ShiftGate {
575607
/// Calculates the value of [in_] shifted left by [shiftAmount].
576608
LShift(Logic in_, dynamic shiftAmount, {String name = 'lshift'})
577-
: super((a, shamt) => a << shamt, '<<', in_, shiftAmount, name: name);
609+
: super((a, shamt) => a << shamt, '<<', in_, shiftAmount,
610+
name: name, outputSvWidthExpansion: true);
578611
}
579612

580613
/// Performs a multiplexer/ternary operation.

lib/src/signals/logic.dart

-8
Original file line numberDiff line numberDiff line change
@@ -322,23 +322,15 @@ class Logic {
322322
Logic pow(dynamic exponent) => Power(this, exponent).out;
323323

324324
/// Addition.
325-
///
326-
/// WARNING: Signed math is not fully tested.
327325
Logic operator +(dynamic other) => Add(this, other).out;
328326

329327
/// Subtraction.
330-
///
331-
/// WARNING: Signed math is not fully tested.
332328
Logic operator -(dynamic other) => Subtract(this, other).out;
333329

334330
/// Multiplication.
335-
///
336-
/// WARNING: Signed math is not fully tested.
337331
Logic operator *(dynamic other) => Multiply(this, other).out;
338332

339333
/// Division.
340-
///
341-
/// WARNING: Signed math is not fully tested.
342334
Logic operator /(dynamic other) => Divide(this, other).out;
343335

344336
/// Modulo operation.

lib/src/values/logic_value.dart

+5
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,11 @@ abstract class LogicValue implements Comparable<LogicValue> {
737737
final modifiedEndIndex =
738738
IndexUtilities.wrapIndex(endIndex, width, allowWidth: true);
739739

740+
// if we're getting the whole thing, just return itself immediately
741+
if (modifiedStartIndex == 0 && modifiedEndIndex == width) {
742+
return this;
743+
}
744+
740745
IndexUtilities.validateRange(modifiedStartIndex, modifiedEndIndex);
741746

742747
return _getRange(modifiedStartIndex, modifiedEndIndex);

test/math_test.dart

+26-4
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,37 @@ void main() {
7373
await Simulator.reset();
7474
});
7575

76+
test('sv expansion does slices', () async {
77+
final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
78+
await gtm.build();
79+
80+
final sv = gtm.generateSynth();
81+
final lines = sv.split('\n');
82+
83+
// should never assign directly off a +
84+
expect(lines.where(RegExp(r'plus.*\+').hasMatch), isEmpty);
85+
86+
// ensure the width of intermediate signals appropriate for add
87+
for (final line in lines) {
88+
if (RegExp('logic.*a_add').hasMatch(line)) {
89+
expect(line, contains('8:0'));
90+
}
91+
}
92+
93+
// ensure we never lshift by a constant directly
94+
for (final line in lines) {
95+
if (RegExp('assign.*a_lshift.*const.*=').hasMatch(line)) {
96+
expect(line, contains('shiftAmount'));
97+
}
98+
}
99+
});
100+
76101
group('simcompare', () {
77102
Future<void> runMathVectors(List<Vector> vectors) async {
78103
final gtm = MathTestModule(Logic(width: 8), Logic(width: 8));
79104
await gtm.build();
80105
await SimCompare.checkFunctionalVector(gtm, vectors);
81-
final simResult = SimCompare.iverilogVector(gtm, vectors);
82-
expect(simResult, equals(true));
106+
SimCompare.checkIverilogVector(gtm, vectors);
83107
}
84108

85109
test('power', () async {
@@ -105,8 +129,6 @@ void main() {
105129
Vector({'a': 1, 'b': 1}, {'a_plus_b': 2}),
106130
Vector({'a': 6, 'b': 7}, {'a_plus_b': 13}),
107131
Vector({'a': 6}, {'a_plus_const': 11}),
108-
// Vector({'a': -6, 'b': 7}, {'a_plus_b': 1}),
109-
// Vector({'a': -6, 'b': 2}, {'a_plus_b': -4}),
110132
]);
111133
});
112134

0 commit comments

Comments
 (0)