diff --git a/lib/src/modules/conditionals/always.dart b/lib/src/modules/conditionals/always.dart index 0fe6b3f99..b9855ca75 100644 --- a/lib/src/modules/conditionals/always.dart +++ b/lib/src/modules/conditionals/always.dart @@ -21,6 +21,9 @@ abstract class Always extends Module with SystemVerilog { UnmodifiableListView(_conditionals); List _conditionals; + /// Optional block label + final String? label; + /// A mapping from internal receiver signals to designated [Module] outputs. @protected @internal @@ -47,7 +50,10 @@ abstract class Always extends Module with SystemVerilog { /// driven by any other [Conditional] in this block, it will be driven to the /// specified reset value. Always(this._conditionals, - {Logic? reset, Map? resetValues, super.name = 'always'}) { + {Logic? reset, + Map? resetValues, + super.name = 'always', + this.label}) { // create a registration of all inputs and outputs of this module var idx = 0; @@ -105,8 +111,10 @@ abstract class Always extends Module with SystemVerilog { reset, // then use it for assigning receiver then: allResetCondAssigns, + ifLabel: '${label}_if', // else assign zero as resetValue orElse: conditionals, + elseLabel: '${label}_else', ), ]; } @@ -176,12 +184,14 @@ abstract class Always extends Module with SystemVerilog { ports.entries.where((element) => this.inputs.containsKey(element.key))); final outputs = Map.fromEntries(ports.entries .where((element) => this.outputs.containsKey(element.key))); + final blockLabel = + label == null ? '' : ' : ${Sanitizer.sanitizeSV(label!)}'; var verilog = ''; verilog += '// $instanceName\n'; - verilog += '${alwaysVerilogStatement(inputs)} begin\n'; + verilog += '${alwaysVerilogStatement(inputs)} begin$blockLabel\n'; verilog += _alwaysContents(inputs, outputs, assignOperator()); - verilog += 'end\n'; + verilog += 'end$blockLabel\n'; return verilog; } } diff --git a/lib/src/modules/conditionals/case.dart b/lib/src/modules/conditionals/case.dart index 590437391..1c6fdf8c6 100644 --- a/lib/src/modules/conditionals/case.dart +++ b/lib/src/modules/conditionals/case.dart @@ -11,6 +11,7 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/modules/conditionals/ssa.dart'; +import 'package:rohd/src/utilities/sanitizer.dart'; /// Represents a single case within a [Case] block. class CaseItem { @@ -20,8 +21,11 @@ class CaseItem { /// A [List] of [Conditional]s to execute when [value] is matched. final List then; + /// An optional label for this case body. + final String? label; + /// Executes [then] when [value] matches. - CaseItem(this.value, this.then); + CaseItem(this.value, this.then, {this.label}); @override String toString() => '$value : $then'; @@ -39,7 +43,8 @@ class CaseItem { Logic cases(Logic expression, Map conditions, {int? width, ConditionalType conditionalType = ConditionalType.none, - dynamic defaultValue}) { + dynamic defaultValue, + String? label}) { for (final conditionValue in [ ...conditions.values, if (defaultValue != null) defaultValue @@ -81,6 +86,7 @@ Logic cases(Logic expression, Map conditions, } final result = Logic(name: 'result', width: width, naming: Naming.mergeable); + var labelNum = 0; Combinational([ Case( @@ -91,10 +97,12 @@ Logic cases(Logic expression, Map conditions, condition.key is Logic ? condition.key as Logic : Const(condition.key, width: expression.width), - [result < condition.value]) + [result < condition.value], + label: label == null ? null : '${label}_case${labelNum++}') ], conditionalType: conditionalType, - defaultItem: defaultValue != null ? [result < defaultValue] : null) + defaultItem: defaultValue != null ? [result < defaultValue] : null, + defaultLabel: label == null ? null : '${label}_default') ]); return result; @@ -119,6 +127,9 @@ class Case extends Conditional { List? get defaultItem => _defaultItem; List? _defaultItem; + /// Optional label for the default case block. + final String? defaultLabel; + /// The type of case block this is, for special attributes /// (e.g. [ConditionalType.unique], [ConditionalType.priority]). /// @@ -130,7 +141,8 @@ class Case extends Conditional { /// If none of [items] match, then [defaultItem] is executed. Case(this.expression, this.items, {List? defaultItem, - this.conditionalType = ConditionalType.none}) + this.conditionalType = ConditionalType.none, + this.defaultLabel}) : _defaultItem = defaultItem { for (final item in items) { if (item.value.width != expression.width) { @@ -267,14 +279,16 @@ class Case extends Conditional { final subPadding = Conditional.calcPadding(indent + 2); for (final item in items) { final conditionName = inputsNameMap[driverInput(item.value).name]; + final caseLabel = + item.label == null ? '' : ' : ${Sanitizer.sanitizeSV(item.label!)}'; final caseContents = item.then .map((conditional) => conditional.verilogContents( indent + 4, inputsNameMap, outputsNameMap, assignOperator)) .join('\n'); verilog.write(''' -$subPadding$conditionName : begin +$subPadding$conditionName : begin$caseLabel $caseContents -${subPadding}end +${subPadding}end$caseLabel '''); } if (defaultItem != null) { @@ -282,10 +296,13 @@ ${subPadding}end .map((conditional) => conditional.verilogContents( indent + 4, inputsNameMap, outputsNameMap, assignOperator)) .join('\n'); + final defaultCaseLabel = defaultLabel == null + ? '' + : ' : ${Sanitizer.sanitizeSV(defaultLabel!)}'; verilog.write(''' -${subPadding}default : begin +${subPadding}default : begin$defaultCaseLabel $defaultCaseContents -${subPadding}end +${subPadding}end$defaultCaseLabel '''); } verilog.write('${padding}endcase\n'); @@ -379,7 +396,7 @@ class CaseZ extends Case { /// /// If none of [items] match, then [defaultItem] is executed. CaseZ(super.expression, super.items, - {super.defaultItem, super.conditionalType}); + {super.defaultItem, super.conditionalType, super.defaultLabel}); @override String get caseType => 'casez'; diff --git a/lib/src/modules/conditionals/combinational.dart b/lib/src/modules/conditionals/combinational.dart index 823202341..9efb3e7ba 100644 --- a/lib/src/modules/conditionals/combinational.dart +++ b/lib/src/modules/conditionals/combinational.dart @@ -26,7 +26,8 @@ class Combinational extends Always { /// If any "write after read" occurs, then a [WriteAfterReadException] will /// be thrown since it could lead to a mismatch between simulation and /// synthesis. See [Combinational.ssa] for more details. - Combinational(super._conditionals, {super.name = 'combinational'}) { + Combinational(super._conditionals, + {super.name = 'combinational', super.label}) { _execute(); // for initial values for (final driver in assignedDriverToInputMap.keys) { driver.glitch.listen((args) { @@ -89,7 +90,8 @@ class Combinational extends Always { /// that it will not be noticeable. factory Combinational.ssa( List Function(Logic Function(Logic signal) s) construct, - {String name = 'combinational_ssa'}) { + {String name = 'combinational_ssa', + String? label}) { final context = _ssaContextCounter++; final ssas = []; @@ -109,7 +111,7 @@ class Combinational extends Always { // no need to keep any of this old info around anymore signalToSsaDrivers.clear(); - return Combinational(conditionals, name: name); + return Combinational(conditionals, name: name, label: label); } /// A map from [SsaLogic]s to signals that they drive. diff --git a/lib/src/modules/conditionals/flop.dart b/lib/src/modules/conditionals/flop.dart index cd9aa8750..3da93e4a4 100644 --- a/lib/src/modules/conditionals/flop.dart +++ b/lib/src/modules/conditionals/flop.dart @@ -31,6 +31,7 @@ Logic flop( Logic? reset, dynamic resetValue, bool asyncReset = false, + String? label, }) => FlipFlop( clk, @@ -39,6 +40,7 @@ Logic flop( reset: reset, resetValue: resetValue, asyncReset: asyncReset, + label: label, ).q; /// Represents a single flip-flop with no reset. @@ -92,6 +94,9 @@ class FlipFlop extends Module with SystemVerilog { /// reset. If no `reset` is provided, this will have no effect. final bool asyncReset; + /// An optional label for the blocks in this flop. + final String? label; + /// Constructs a flip flop which is positive edge triggered on [clk]. /// /// When optional [en] is provided, an additional input will be created for @@ -115,6 +120,7 @@ class FlipFlop extends Module with SystemVerilog { dynamic resetValue, this.asyncReset = false, super.name = 'flipflop', + this.label, }) { if (clk.width != 1) { throw Exception('clk must be 1 bit'); @@ -146,7 +152,9 @@ class FlipFlop extends Module with SystemVerilog { var contents = [q < _d]; if (_en != null) { - contents = [If(_en!, then: contents)]; + contents = [ + If(_en!, then: contents, ifLabel: label == null ? null : '${label}_en') + ]; } Sequential( @@ -156,6 +164,7 @@ class FlipFlop extends Module with SystemVerilog { asyncReset: asyncReset, resetValues: _reset != null ? {q: _resetValuePort ?? _resetValueConst} : null, + label: label, ); } diff --git a/lib/src/modules/conditionals/if.dart b/lib/src/modules/conditionals/if.dart index 064a20a43..0d8c4cb4b 100644 --- a/lib/src/modules/conditionals/if.dart +++ b/lib/src/modules/conditionals/if.dart @@ -11,6 +11,7 @@ import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd/src/modules/conditionals/ssa.dart'; +import 'package:rohd/src/utilities/sanitizer.dart'; /// A conditional block to execute only if [condition] is satisified. /// @@ -22,8 +23,11 @@ class ElseIf { /// The [Conditional]s to execute if [condition] is satisfied. final List then; + /// An optional block label. + final String? label; + /// If [condition] is 1, then [then] will be executed. - ElseIf(this.condition, this.then) { + ElseIf(this.condition, this.then, {this.label}) { if (condition.width != 1) { throw PortWidthMismatchException(condition, 1); } @@ -32,7 +36,8 @@ class ElseIf { /// If [condition] is 1, then [then] will be executed. /// /// Use this constructor when you only have a single [then] condition. - ElseIf.s(Logic condition, Conditional then) : this(condition, [then]); + ElseIf.s(Logic condition, Conditional then, {String? label}) + : this(condition, [then], label: label); } /// A conditional block to execute only if `condition` is satisified. @@ -46,13 +51,14 @@ typedef Iff = ElseIf; class Else extends Iff { /// If none of the proceding [Iff] or [ElseIf] are executed, then /// [then] will be executed. - Else(List then) : super(Const(1), then); + Else(List then, {String? label}) + : super(Const(1), then, label: label); /// If none of the proceding [Iff] or [ElseIf] are executed, then /// [then] will be executed. /// /// Use this constructor when you only have a single [then] condition. - Else.s(Conditional then) : this([then]); + Else.s(Conditional then, {String? label}) : this([then], label: label); } /// Represents a chain of blocks of code to be conditionally executed, like @@ -82,10 +88,14 @@ class If extends Conditional { /// If [condition] is high, then [then] executes, otherwise [orElse] is /// executed. - If(Logic condition, {List? then, List? orElse}) + If(Logic condition, + {List? then, + List? orElse, + String? ifLabel, + String? elseLabel}) : this.block([ - Iff(condition, then ?? []), - if (orElse != null) Else(orElse), + Iff(condition, then ?? [], label: ifLabel), + if (orElse != null) Else(orElse, label: elseLabel), ]); /// If [condition] is high, then [then] is excutes, @@ -198,10 +208,12 @@ class If extends Conditional { indent + 2, inputsNameMap, outputsNameMap, assignOperator)) .join('\n'); final condition = iff is! Else ? '($conditionName)' : ''; + final iffLabel = + iff.label == null ? '' : ' : ${Sanitizer.sanitizeSV(iff.label!)}'; verilog.write(''' -$padding$header$condition begin +$padding$header$condition begin$iffLabel $ifContents -${padding}end '''); +${padding}end$iffLabel '''); } verilog.write('\n'); diff --git a/lib/src/modules/conditionals/sequential.dart b/lib/src/modules/conditionals/sequential.dart index a07d92bfa..7f7970064 100644 --- a/lib/src/modules/conditionals/sequential.dart +++ b/lib/src/modules/conditionals/sequential.dart @@ -165,6 +165,7 @@ class Sequential extends Always { bool asyncReset = false, bool allowMultipleAssignments = true, String name = 'sequential', + String? label, }) : this.multi( [clk], conditionals, @@ -173,6 +174,7 @@ class Sequential extends Always { asyncReset: asyncReset, resetValues: resetValues, allowMultipleAssignments: allowMultipleAssignments, + label: label, ); /// Constructs a [Sequential] multi-triggered by any of [posedgeTriggers] and @@ -210,6 +212,7 @@ class Sequential extends Always { super.name = 'sequential', this.allowMultipleAssignments = true, List negedgeTriggers = const [], + super.label, }) : super(reset: reset) { _registerInputTriggers([ ...posedgeTriggers, diff --git a/test/block_labels_test.dart b/test/block_labels_test.dart new file mode 100644 index 000000000..5a4dd5dfa --- /dev/null +++ b/test/block_labels_test.dart @@ -0,0 +1,393 @@ +// Copyright (C) 2021-2025 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// block_labels_test.dart +// Unit test for block labels (e.g. always_comb, always_ff, case) +// +// 2025 March 6 +// Author: Andrew Capatina + +import 'package:rohd/rohd.dart'; +import 'package:rohd/src/utilities/simcompare.dart'; +import 'package:test/test.dart'; + +class LabeledCasesModule extends Module { + Logic control; + Logic a; + Logic b; + String caseLabel; + + LabeledCasesModule( + this.control, this.a, this.b, [this.caseLabel='cases']) { + control = addInput('control', control); + a = addInput('a', a); + b = addInput('b', b); + final out = addOutput('out'); + out <= cases( + control, {LogicValue.zero: a, LogicValue.one: b}, + label: caseLabel); + } +} + +class LabeledCaseModule extends Module { + Logic a; + Logic b; + String firstCaseItemLabel; + String secondCaseItemLabel; + String defaultLabel; + + LabeledCaseModule( + this.a, this.b, [this.firstCaseItemLabel='caseItem1', + this.secondCaseItemLabel='caseItem2', this.defaultLabel='default1']) { + a = addInput('a', a); + b = addInput('b', b); + final out = addOutput('out'); + + final aXorB = a ^ b; + Combinational([ + Case( + aXorB, + [ + CaseItem(Const(LogicValue.ofString('0')), + [out < 1], label: firstCaseItemLabel), + CaseItem(Const(LogicValue.ofString('1')), + [out < 0], label: secondCaseItemLabel) + ], defaultItem: [out < 0], defaultLabel: defaultLabel), + ]); + } +} + +class LabeledIfModule extends Module { + Logic a; + Logic b; + + Logic get out => output('out'); + + String alwaysCombLabel; + String ifLabel; + String ifElseLabel; + String elseLabel; + + LabeledIfModule( + this.a, this.b, [this.alwaysCombLabel='comb_1', this.ifLabel='if_1', + this.ifElseLabel='if_else_1', this.elseLabel='else_1']) { + a = addInput('a', a); + b = addInput('b', b); + final out = addOutput('out'); + + Combinational([ + If.block([ + Iff(a.eq(0) & b.eq(0), [ + out < 0, + ], label: ifLabel), + ElseIf(a.eq(1) & b.eq(0), [ + out < 1, + ], label: ifElseLabel), + Else([ + out < 0, + ], label: elseLabel) + ]), + ], label: alwaysCombLabel); + } +} + +class LabeledChainSequentialModule extends Module { + Logic reset; + Logic d; + Logic get q => output('q'); + final clk = SimpleClockGenerator(10).clk; + + String firstFfLabel; + String secondFfLabel; + + LabeledChainSequentialModule( + this.reset, this.d, [this.firstFfLabel='ff_1', + this.secondFfLabel='ff_2']) { + reset = addInput('reset', reset); + d = addInput('d', d); + final q = addOutput('q'); + + final qInternal = LogicNet(); + Sequential( + clk, + reset: reset, + resetValues: { + qInternal: 0 + }, + [ + qInternal < d + ], + label: firstFfLabel + ); + Sequential( + clk, + reset: reset, + resetValues: { + q: 0 + }, + [ + q < qInternal + ], + label: secondFfLabel + ); + } +} + +class LabeledMultiBlockModule extends Module { + Logic a; + Logic b; + Logic c; + Logic d; + + Logic get out_0 => output('out_0'); + Logic get out_1 => output('out_1'); + + String firstBlockLabel; + String secondBlockLabel; + + LabeledMultiBlockModule( + this.a, this.b, this.c, this.d, [this.firstBlockLabel='block_0', + this.secondBlockLabel='block_1']) { + a = addInput('a', a); + b = addInput('b', b); + c = addInput('c', c); + d = addInput('d', d); + final out_0 = addOutput('out_0'); + final out_1 = addOutput('out_1'); + + Combinational([out_0 < a & b], label: firstBlockLabel); + Combinational([out_1 < c ^ d], label: secondBlockLabel); + } +} + +class LabeledSsaModule extends Module { + String firstBlockLabel; + String secondBlockLabel; + + LabeledSsaModule(Logic a, [this.firstBlockLabel='block_0', + this.secondBlockLabel='block_1']) { + a = addInput('a', a, width: a.width); + final b = addOutput('b', width: a.width); + final c = addOutput('c', width: a.width); + + final intermediate_0 = Logic(name: 'intermediate_0', width: a.width); + final intermediate_1 = Logic(name: 'intermediate_1', width: a.width); + + final inc_0 = IncrModule(intermediate_0); + final inc_1 = IncrModule(intermediate_1); + + Combinational.ssa((s) => [ + s(intermediate_0) < a, + s(intermediate_0) < inc_0.result, + s(intermediate_0) < inc_0.result, + ], label: firstBlockLabel); + + Combinational.ssa((s) => [ + s(intermediate_1) < c, + s(intermediate_1) < inc_1.result, + s(intermediate_1) < inc_1.result, + ], label: secondBlockLabel); + b <= intermediate_0; + c <= intermediate_1; + } +} + +class IncrModule extends Module { + Logic get result => output('result'); + IncrModule(Logic toIncr) : super(name: 'incr') { + toIncr = addInput('toIncr', toIncr, width: toIncr.width); + addOutput('result', width: toIncr.width); + result <= toIncr + 1; + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + group('case statements', () { + final shorthandCaseVectors = [ + Vector({'control': 0, 'a': 0, 'b': 0}, {'out': 0}), + Vector({'control': 0, 'a': 0, 'b': 1}, {'out': 0}), + Vector({'control': 0, 'a': 1, 'b': 0}, {'out': 1}), + Vector({'control': 0, 'a': 1, 'b': 1}, {'out': 1}), + Vector({'control': 1, 'a': 0, 'b': 0}, {'out': 0}), + Vector({'control': 1, 'a': 0, 'b': 1}, {'out': 1}), + Vector({'control': 1, 'a': 1, 'b': 0}, {'out': 0}), + Vector({'control': 1, 'a': 1, 'b': 1}, {'out': 1}), + ]; + final caseVectors = [ + Vector({'a': 0, 'b': 0}, {'out': 1}), + Vector({'a': 0, 'b': 1}, {'out': 0}), + Vector({'a': 1, 'b': 0}, {'out': 0}), + Vector({'a': 1, 'b': 1}, {'out': 1}), + ]; + test('valid shorthand case', () async { + final gtm = LabeledCasesModule(Logic(), Logic(), Logic()); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, shorthandCaseVectors); + final simResult = SimCompare.iverilogVector(gtm, shorthandCaseVectors); + expect(simResult, equals(true)); + }); + test('valid case', () async { + + final gtm = LabeledCaseModule(Logic(), Logic()); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, caseVectors); + final simResult = SimCompare.iverilogVector(gtm, caseVectors); + expect(simResult, equals(true)); + }); + + test('same case items labels', () async { + final gtm = LabeledCaseModule( + Logic(), Logic(), 'caseItem2'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, caseVectors); + final simResult = SimCompare.iverilogVector( + gtm, caseVectors, buildOnly: true); + expect(simResult, equals(false)); + }); + + test('same case item and default label', () async { + final gtm = LabeledCaseModule( + Logic(), Logic(), 'caseItem1', 'caseItem2', 'caseItem1'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, caseVectors); + final simResult = SimCompare.iverilogVector( + gtm, caseVectors, buildOnly: true); + expect(simResult, equals(false)); + }); + }); + + group('if/else if/else blocks', () { + final vectors = [ + Vector({'a': 0, 'b': 0}, {'out': 0}), + Vector({'a': 0, 'b': 1}, {'out': 0}), + Vector({'a': 1, 'b': 0}, {'out': 1}), + Vector({'a': 1, 'b': 1}, {'out': 0}), + ]; + test('valid case', () async { + final gtm = LabeledIfModule( + Logic(), Logic()); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector(gtm, vectors); + expect(simResult, equals(true)); + }); + + test('same if and else if labels', () async { + final gtm = LabeledIfModule( + Logic(), Logic(), 'comb_1', 'if_1', 'if_1'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector( + gtm, vectors, buildOnly: true); + expect(simResult, equals(false)); + }); + + test('same if and else labels', () async { + final gtm = LabeledIfModule( + Logic(), Logic(), 'comb_1', 'if_1', 'else_if_1', 'if_1'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector( + gtm, vectors, buildOnly: true); + expect(simResult, equals(false)); + }); + + test('same else if and else labels', () async { + final gtm = LabeledIfModule( + Logic(), Logic(), 'comb_1', 'if_1', 'else_if_1', 'else_if_1'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector( + gtm, vectors, buildOnly: true); + expect(simResult, equals(false)); + }); + }); + + group('seqeuntial blocks', () { + final vectors = [ + Vector({'reset': 1}, {}), + Vector({}, {'q': 0}), + Vector({'reset': 0, 'd': 0}, {}), + Vector({}, {'q': 0}), + Vector({'reset': 0, 'd': 1}, {}), + Vector({}, {'q': 0}), + Vector({'reset': 0, 'd': 1}, {}), + Vector({}, {'q': 1}), + Vector({'reset': 1, 'd': 0}, {}), + Vector({}, {'q': 0}), + Vector({'reset': 1, 'd': 1}, {}), + Vector({}, {'q': 0}), + ]; + test('valid labels', () async { + final gtm = LabeledChainSequentialModule( + Logic(), Logic()); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector(gtm, vectors); + expect(simResult, equals(true)); + }); + + test('same labels', () async { + final gtm = LabeledChainSequentialModule( + Logic(), Logic(), 'ff_0', 'ff_0'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector( + gtm, vectors, buildOnly: true); + expect(simResult, equals(false)); + }); + }); + + group('multi block modules with same scope', () { + test('valid labels', () async { + final gtm = LabeledMultiBlockModule( + Logic(), Logic(), Logic(), Logic()); + await gtm.build(); + final vectors = [ + Vector({'a': 1, 'b': 0, 'c': 1, 'd': 0}, {'out_0': 0, 'out_1': 1}) + ]; + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector(gtm, vectors); + expect(simResult, equals(true)); + }); + + test('blocks of same scope with same name', () async { + final gtm = LabeledMultiBlockModule( + Logic(), Logic(), Logic(), Logic(), 'block_0', 'block_0'); + await gtm.build(); + final vectors = [ + Vector({'a': 1, 'b': 0, 'c': 1, 'd': 0}, {'out_0': 0, 'out_1': 1}) + ]; + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector( + gtm, vectors, buildOnly: true); + expect(simResult, equals(false)); + }); + }); + + group('combinational ssa', () { + final vectors = [ + Vector({'a': 3}, {'b': LogicValue.x, 'c': LogicValue.x}) + ]; + test('valid labels', () async { + final gtm = LabeledSsaModule(Logic(width: 8)); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector(gtm, vectors); + expect(simResult, equals(true)); + }); + + test('same block labels', () async { + final gtm = LabeledSsaModule(Logic(width: 8), 'block_0', 'block_0'); + await gtm.build(); + await SimCompare.checkFunctionalVector(gtm, vectors); + final simResult = SimCompare.iverilogVector( + gtm, vectors, buildOnly: true); + expect(simResult, equals(false)); + }); + }); +}