Skip to content

Commit d102696

Browse files
authored
Improve SSA error debuggability with hierarchy (#635)
1 parent 61dee59 commit d102696

File tree

7 files changed

+85
-15
lines changed

7 files changed

+85
-15
lines changed

lib/src/module.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ abstract class Module {
353353

354354
/// Converts a [hierarchy] (like used in [_checkValidHierarchy]) into a string
355355
/// that can be used for error messages.
356-
static String _hierarchyListToString(List<Module> hierarchy) =>
356+
static String _hierarchyListToString(Iterable<Module> hierarchy) =>
357357
hierarchy.map((e) => e.name).join('.');
358358

359359
/// Adds a [Module] to this as a subModule.
@@ -1107,6 +1107,11 @@ abstract class Module {
11071107
return hier.toString();
11081108
}
11091109

1110+
/// Returns the hierarchical name of this [Module] with the parent [hierarchy]
1111+
/// included, separated by `.`s, e.g. `top.mid.leaf`. Because it depends on
1112+
/// [hierarchy], this is only valid after [build] has been called.
1113+
String get hierarchicalName => _hierarchyListToString(hierarchy());
1114+
11101115
/// Returns a synthesized version of this [Module].
11111116
///
11121117
/// Currently returns one long file in SystemVerilog, but in the future

lib/src/modules/conditionals/always.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2021-2024 Intel Corporation
1+
// Copyright (C) 2021-2025 Intel Corporation
22
// SPDX-License-Identifier: BSD-3-Clause
33
//
44
// always.dart
@@ -135,8 +135,12 @@ abstract class Always extends Module with SystemVerilog {
135135
}
136136

137137
// share the registration information down
138-
conditional.updateAssignmentMaps(
139-
assignedReceiverToOutputMap, assignedDriverToInputMap);
138+
conditional.updateRegistration(
139+
assignedReceiverToOutputMap: assignedReceiverToOutputMap,
140+
assignedDriverToInputMap: assignedDriverToInputMap,
141+
parentConditional: null,
142+
parentAlways: this,
143+
);
140144
}
141145
}
142146

lib/src/modules/conditionals/conditional.dart

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import 'package:meta/meta.dart';
1111
import 'package:rohd/rohd.dart';
12+
import 'package:rohd/src/modules/conditionals/always.dart';
1213
import 'package:rohd/src/modules/conditionals/ssa.dart';
1314

1415
/// Represents an some logical assignments or actions that will only happen
@@ -28,20 +29,67 @@ abstract class Conditional {
2829
/// This is used for things like [Sequential]'s pre-tick values.
2930
Map<Logic, LogicValue> _driverValueOverrideMap = {};
3031

32+
/// The [Conditional] that contains this [Conditional], if there is one.
33+
///
34+
/// This is only initialized after it's been included inside an [Always].
35+
late final Conditional? _parentConditional;
36+
37+
/// The [Always] parent of this [Conditional], if there is one.
38+
///
39+
/// This is only initialized after it's been included inside an [Always].
40+
late final Always _parentAlways;
41+
42+
/// A string representing the hierarchical path to this [Conditional],
43+
/// including the module path to the [_parentAlways] and the names of
44+
/// [Conditional]s within that [Always] down to `this` one.
45+
///
46+
/// If this [Conditional] is not yet registered within an [Always], or if the
47+
/// [_parentAlways] has not yet been built, this will only include the runtime
48+
/// type of this [Conditional].
49+
@protected
50+
@internal
51+
String get hierarchyString => [
52+
if (_isRegistered && _parentAlways.hasBuilt)
53+
_parentConditional?.hierarchyString ?? _parentAlways.hierarchicalName,
54+
runtimeType
55+
].join('.');
56+
57+
/// Indicates whether [updateRegistration] has been called on this
58+
/// [Conditional] already.
59+
bool _isRegistered = false;
60+
61+
/// Updates registration information for the [Conditional], passed down from
62+
/// the parent [Always] or [Conditional].
63+
///
3164
/// Updates the values of [_assignedReceiverToOutputMap] and
3265
/// [_assignedDriverToInputMap] and passes them down to all sub-[Conditional]s
3366
/// as well.
3467
@internal
35-
void updateAssignmentMaps(
36-
Map<Logic, Logic> assignedReceiverToOutputMap,
37-
Map<Logic, Logic> assignedDriverToInputMap,
38-
) {
68+
void updateRegistration({
69+
required Map<Logic, Logic> assignedReceiverToOutputMap,
70+
required Map<Logic, Logic> assignedDriverToInputMap,
71+
required Conditional? parentConditional,
72+
required Always parentAlways,
73+
}) {
74+
if (_isRegistered) {
75+
throw InvalidConditionalException('Conditional $this is already included'
76+
' as part of another block: $hierarchyString');
77+
}
78+
3979
_assignedReceiverToOutputMap = assignedReceiverToOutputMap;
4080
_assignedDriverToInputMap = assignedDriverToInputMap;
81+
_parentConditional = parentConditional;
82+
_parentAlways = parentAlways;
4183
for (final conditional in conditionals) {
42-
conditional.updateAssignmentMaps(
43-
assignedReceiverToOutputMap, assignedDriverToInputMap);
84+
conditional.updateRegistration(
85+
assignedReceiverToOutputMap: assignedReceiverToOutputMap,
86+
assignedDriverToInputMap: assignedDriverToInputMap,
87+
parentConditional: this,
88+
parentAlways: parentAlways,
89+
);
4490
}
91+
92+
_isRegistered = true;
4593
}
4694

4795
/// Updates the value of [_driverValueOverrideMap] and passes it down to all
@@ -204,7 +252,7 @@ abstract class Conditional {
204252
receiverOutput.put(LogicValue.x);
205253
}
206254
} on WriteAfterReadException catch (e) {
207-
throw e.cloneWithAddedPath(' at (driving X) $this');
255+
throw e.cloneWithAddedPath(' at (driving X) $this [$hierarchyString]');
208256
}
209257

210258
drivenSignals?.addAll(receivers);

lib/src/modules/conditionals/conditional_assign.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class ConditionalAssign extends Conditional {
6666
_receiverOutput.put(currentValue);
6767
}
6868
} on WriteAfterReadException catch (e) {
69-
throw e.cloneWithAddedPath(' at $this');
69+
throw e.cloneWithAddedPath(' at $this [$hierarchyString]');
7070
}
7171

7272
if (drivenSignals != null &&

test/comb_mod_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ void main() {
245245
} on Exception catch (e) {
246246
expect(e, isA<WriteAfterReadException>());
247247
expect(e.toString(), contains('internalReadEn '));
248+
expect(
249+
e.toString(), contains('singlereadandwriterf.rf_read.If.Case'));
248250
}
249251
}
250252
});

test/conditionals_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2021-2023 Intel Corporation
1+
// Copyright (C) 2021-2025 Intel Corporation
22
// SPDX-License-Identifier: BSD-3-Clause
33
//
44
// conditionals_test.dart
@@ -700,8 +700,8 @@ void main() {
700700

701701
test('should return exception if a conditional is used multiple times.',
702702
() async {
703-
expect(
704-
() => MultipleConditionalModule(Logic(), Logic()), throwsException);
703+
expect(() => MultipleConditionalModule(Logic(), Logic()),
704+
throwsA(isA<InvalidConditionalException>()));
705705
});
706706
});
707707

test/module_test.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,15 @@ void main() {
386386
}
387387
});
388388
});
389+
390+
test('hierarchicalName', () async {
391+
final topInput = Logic();
392+
final top = FlexibleModule(name: 'top')..addInput('a', topInput);
393+
final mid = FlexibleModule(name: 'mid')..addInput('a', top.input('a'));
394+
final sub = FlexibleModule(name: 'sub')..addInput('a', mid.input('a'));
395+
396+
await top.build();
397+
398+
expect(sub.hierarchicalName, 'top.mid.sub');
399+
});
389400
}

0 commit comments

Comments
 (0)