-
Notifications
You must be signed in to change notification settings - Fork 78
SystemVerilog block labels #585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,9 @@ abstract class Always extends Module with SystemVerilog { | |
| UnmodifiableListView<Conditional>(_conditionals); | ||
| List<Conditional> _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<Logic, dynamic>? resetValues, super.name = 'always'}) { | ||
| {Logic? reset, | ||
| Map<Logic, dynamic>? 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', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since these two are specific to reset handling, perhaps naming them as such would be better? |
||
| // 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'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: does SystemVerilog require that block labels are unique? within some scope? if so, it would add a little complexity (hopefully it does not require it!) your testing seems to indicate it is not necessary to make them unique -- do you know if that's LRM-legal or just ok with iverilog? any lints we should check for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IEEE 1800-2023 says the following regarding hierarchical names (which includes identifiers used in named blocks):
And block names themselves are described elsewhere:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for finding those sections -- my read of this is that they do need to be unique? That would, unfortunately, mean we need to probably uniquify them during SV generation in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so reading into it and thinking a little more, since each block scope is a new scope, the naming only needs to be uniquified locally within that scope! that means we can use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkorbel1 we can look into this, but maybe this is better as a different issue we can address? I'm not seeing a clean way of addressing this that doesn't make this an unwieldly PR. Not exactly familiar enough with how ROHD is generating blocks to say I have a good recursive solution for this. We could do a list and lookup for current scope but I think that is a substantial change to the code and would maybe make code review less reasonable (I'm also not a dart expert and still learning so I'm trying to keep my lack of current knowledge in mind). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we can meet to sketch out what it looks like? i think once we figure out the pattern it will end up being relatively simple and not too disruptive to apply it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, we would be happy too, we just want to get you unblocked. |
||
| verilog += _alwaysContents(inputs, outputs, assignOperator()); | ||
| verilog += 'end\n'; | ||
| verilog += 'end$blockLabel\n'; | ||
| return verilog; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<Conditional> 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<dynamic, dynamic> 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<dynamic, dynamic> 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<dynamic, dynamic> 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<Conditional>? get defaultItem => _defaultItem; | ||
| List<Conditional>? _defaultItem; | ||
|
|
||
| /// Optional label for the default case block. | ||
| final String? defaultLabel; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. idea: what if we accepted a |
||
|
|
||
| /// 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<Conditional>? 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,25 +279,30 @@ 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) { | ||
| final defaultCaseContents = defaultItem! | ||
| .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'; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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') | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since |
||
| ]; | ||
| } | ||
|
|
||
| Sequential( | ||
|
|
@@ -156,6 +164,7 @@ class FlipFlop extends Module with SystemVerilog { | |
| asyncReset: asyncReset, | ||
| resetValues: | ||
| _reset != null ? {q: _resetValuePort ?? _resetValueConst} : null, | ||
| label: label, | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<Conditional> 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<Conditional> then) : super(Const(1), then); | ||
| Else(List<Conditional> 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<Conditional>? then, List<Conditional>? orElse}) | ||
| If(Logic condition, | ||
| {List<Conditional>? then, | ||
| List<Conditional>? orElse, | ||
| String? ifLabel, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. similar to prior comments: what if we just accepted one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd agree with this, I don't think most people are labelling their |
||
| 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'); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if for the top-level
alwaysblock, we can just use thenameas the label?If we decide that makes sense, should we change other places where we have "label" to "name" as well?