Skip to content

Commit 270655a

Browse files
authored
Allow multiple nonblocking assignments, fix #321 (#421)
1 parent 273d083 commit 270655a

File tree

2 files changed

+91
-38
lines changed

2 files changed

+91
-38
lines changed

lib/src/modules/conditional.dart

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ class Sequential extends _Always {
378378
/// The input clocks used in this block.
379379
final List<Logic> _clks = [];
380380

381+
/// When `false`, an [SignalRedrivenException] will be thrown during
382+
/// simulation if the same signal is driven multiple times within this
383+
/// [Sequential].
384+
final bool allowMultipleAssignments;
385+
381386
/// Constructs a [Sequential] single-triggered by [clk].
382387
///
383388
/// If `reset` is provided, then all signals driven by this block will be
@@ -387,12 +392,21 @@ class Sequential extends _Always {
387392
/// that value instead upon reset. If a signal is in `resetValues` but not
388393
/// driven by any other [Conditional] in this block, it will be driven to the
389394
/// specified reset value.
390-
Sequential(Logic clk, List<Conditional> conditionals,
391-
{Logic? reset,
392-
Map<Logic, dynamic>? resetValues,
393-
String name = 'sequential'})
394-
: this.multi([clk], conditionals,
395-
name: name, reset: reset, resetValues: resetValues);
395+
Sequential(
396+
Logic clk,
397+
List<Conditional> conditionals, {
398+
Logic? reset,
399+
Map<Logic, dynamic>? resetValues,
400+
bool allowMultipleAssignments = true,
401+
String name = 'sequential',
402+
}) : this.multi(
403+
[clk],
404+
conditionals,
405+
name: name,
406+
reset: reset,
407+
resetValues: resetValues,
408+
allowMultipleAssignments: allowMultipleAssignments,
409+
);
396410

397411
/// Constructs a [Sequential] multi-triggered by any of [clks].
398412
///
@@ -403,8 +417,14 @@ class Sequential extends _Always {
403417
/// that value instead upon reset. If a signal is in `resetValues` but not
404418
/// driven by any other [Conditional] in this block, it will be driven to the
405419
/// specified reset value.
406-
Sequential.multi(List<Logic> clks, super._conditionals,
407-
{super.reset, super.resetValues, super.name = 'sequential'}) {
420+
Sequential.multi(
421+
List<Logic> clks,
422+
super._conditionals, {
423+
super.reset,
424+
super.resetValues,
425+
super.name = 'sequential',
426+
this.allowMultipleAssignments = true,
427+
}) {
408428
if (clks.isEmpty) {
409429
throw IllegalConfigurationException('Must provide at least one clock.');
410430
}
@@ -547,12 +567,18 @@ class Sequential extends _Always {
547567
receiverOutput.put(LogicValue.x);
548568
}
549569
} else if (anyClkPosedge) {
550-
final allDrivenSignals = DuplicateDetectionSet<Logic>();
551-
for (final element in conditionals) {
552-
element.execute(allDrivenSignals, null);
553-
}
554-
if (allDrivenSignals.hasDuplicates) {
555-
throw SignalRedrivenException(allDrivenSignals.duplicates);
570+
if (allowMultipleAssignments) {
571+
for (final element in conditionals) {
572+
element.execute(null, null);
573+
}
574+
} else {
575+
final allDrivenSignals = DuplicateDetectionSet<Logic>();
576+
for (final element in conditionals) {
577+
element.execute(allDrivenSignals, null);
578+
}
579+
if (allDrivenSignals.hasDuplicates) {
580+
throw SignalRedrivenException(allDrivenSignals.duplicates);
581+
}
556582
}
557583
}
558584

@@ -642,7 +668,7 @@ abstract class Conditional {
642668
/// which consumes the current value of those drivers. It is used to check
643669
/// that signals are not "written after read", for example.
644670
@protected
645-
void execute(Set<Logic> drivenSignals, void Function(Logic toGuard)? guard);
671+
void execute(Set<Logic>? drivenSignals, void Function(Logic toGuard)? guard);
646672

647673
/// Lists *all* receivers, recursively including all sub-[Conditional]s
648674
/// receivers.
@@ -752,10 +778,11 @@ abstract class Conditional {
752778
{required int context});
753779

754780
/// Drives X to all receivers.
755-
void _driveX(Set<Logic> drivenSignals) {
781+
void _driveX(Set<Logic>? drivenSignals) {
756782
for (final receiver in receivers) {
757783
receiverOutput(receiver).put(LogicValue.x);
758-
if (!drivenSignals.contains(receiver) || receiver.value.isValid) {
784+
if (drivenSignals != null &&
785+
(!drivenSignals.contains(receiver) || receiver.value.isValid)) {
759786
drivenSignals.add(receiver);
760787
}
761788
}
@@ -794,7 +821,7 @@ class ConditionalGroup extends Conditional {
794821
[for (final conditional in conditionals) ...conditional.receivers];
795822

796823
@override
797-
void execute(Set<Logic> drivenSignals, void Function(Logic toGuard)? guard) {
824+
void execute(Set<Logic>? drivenSignals, void Function(Logic toGuard)? guard) {
798825
for (final conditional in conditionals) {
799826
conditional.execute(drivenSignals, guard);
800827
}
@@ -850,7 +877,7 @@ class ConditionalAssign extends Conditional {
850877
late final _receiverOutput = receiverOutput(receiver);
851878

852879
@override
853-
void execute(Set<Logic> drivenSignals,
880+
void execute(Set<Logic>? drivenSignals,
854881
[void Function(Logic toGuard)? guard]) {
855882
if (guard != null) {
856883
guard(driver);
@@ -863,7 +890,8 @@ class ConditionalAssign extends Conditional {
863890
_receiverOutput.put(currentValue);
864891
}
865892

866-
if (!drivenSignals.contains(receiver) || receiver.value.isValid) {
893+
if (drivenSignals != null &&
894+
(!drivenSignals.contains(receiver) || receiver.value.isValid)) {
867895
drivenSignals.add(receiver);
868896
}
869897
}
@@ -1053,7 +1081,7 @@ class Case extends Conditional {
10531081
String get caseType => 'case';
10541082

10551083
@override
1056-
void execute(Set<Logic> drivenSignals, [void Function(Logic)? guard]) {
1084+
void execute(Set<Logic>? drivenSignals, [void Function(Logic)? guard]) {
10571085
if (guard != null) {
10581086
guard(expression);
10591087
for (final item in items) {
@@ -1397,7 +1425,7 @@ class If extends Conditional {
13971425
}
13981426

13991427
@override
1400-
void execute(Set<Logic> drivenSignals, [void Function(Logic)? guard]) {
1428+
void execute(Set<Logic>? drivenSignals, [void Function(Logic)? guard]) {
14011429
if (guard != null) {
14021430
for (final iff in iffs) {
14031431
guard(iff.condition);

test/conditionals_test.dart

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,8 @@ class SingleElseModule extends Module {
391391
}
392392

393393
class SignalRedrivenSequentialModule extends Module {
394-
SignalRedrivenSequentialModule(Logic a, Logic b, Logic d)
394+
SignalRedrivenSequentialModule(Logic a, Logic b, Logic d,
395+
{required bool allowRedrive})
395396
: super(name: 'ffmodule') {
396397
a = addInput('a', a);
397398
b = addInput('b', b);
@@ -400,13 +401,17 @@ class SignalRedrivenSequentialModule extends Module {
400401
d = addInput('d', d, width: d.width);
401402

402403
final k = addOutput('k', width: 8);
403-
Sequential(SimpleClockGenerator(10).clk, [
404-
If(a, then: [
405-
k < k,
406-
q < k,
407-
q < d,
408-
])
409-
]);
404+
Sequential(
405+
SimpleClockGenerator(10).clk,
406+
[
407+
If(a, then: [
408+
k < k,
409+
q < k,
410+
q < d,
411+
])
412+
],
413+
allowMultipleAssignments: allowRedrive,
414+
);
410415
}
411416
}
412417

@@ -419,10 +424,14 @@ class SignalRedrivenSequentialModuleWithX extends Module {
419424

420425
final b = addOutput('b');
421426

422-
Sequential(SimpleClockGenerator(10).clk, [
423-
If(a, then: [b < c]),
424-
If(d, then: [b < c])
425-
]);
427+
Sequential(
428+
SimpleClockGenerator(10).clk,
429+
[
430+
If(a, then: [b < c]),
431+
If(d, then: [b < c])
432+
],
433+
allowMultipleAssignments: false,
434+
);
426435
}
427436
}
428437

@@ -712,9 +721,10 @@ void main() {
712721

713722
test(
714723
'should return SignalRedrivenException when there are multiple drivers '
715-
'for a flop.', () async {
716-
final mod =
717-
SignalRedrivenSequentialModule(Logic(), Logic(), Logic(width: 8));
724+
'for a flop when redrive not allowed.', () async {
725+
final mod = SignalRedrivenSequentialModule(
726+
Logic(), Logic(), Logic(width: 8),
727+
allowRedrive: false);
718728
await mod.build();
719729
final vectors = [
720730
Vector({'a': 1, 'd': 1}, {}),
@@ -729,6 +739,21 @@ void main() {
729739
}
730740
});
731741

742+
test('should allow redrive when allowed', () async {
743+
final mod = SignalRedrivenSequentialModule(
744+
Logic(), Logic(), Logic(width: 8),
745+
allowRedrive: true);
746+
await mod.build();
747+
final vectors = [
748+
Vector({'a': 1, 'd': 1}, {}),
749+
Vector({'a': 1, 'b': 0, 'd': 2}, {'q': 1}),
750+
Vector({'a': 1, 'b': 0, 'd': 3}, {'q': 2}),
751+
];
752+
753+
await SimCompare.checkFunctionalVector(mod, vectors);
754+
SimCompare.checkIverilogVector(mod, vectors);
755+
});
756+
732757
test(
733758
'should return NonSupportedTypeException when '
734759
'simcompare expected output values has invalid runtime type. ', () async {
@@ -749,7 +774,7 @@ void main() {
749774

750775
test(
751776
'should return SignalRedrivenException when driven with '
752-
'x signals and valid signals.', () async {
777+
'x signals and valid signals when redrive not allowed.', () async {
753778
final mod = SignalRedrivenSequentialModuleWithX(Logic(), Logic(), Logic());
754779
await mod.build();
755780
final vectors = [

0 commit comments

Comments
 (0)