Skip to content
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

Reset for flops and try ports #410

Merged
merged 3 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/src/interfaces/interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class Interface<TagType> {
? _ports[name]!
: throw Exception('Port name "$name" not found on this interface.');

/// Provides the [port] named [name] if it exists, otherwise `null`.
Logic? tryPort(String name) => _ports[name];

/// Connects [module]'s inputs and outputs up to [srcInterface] and this
/// [Interface].
///
Expand Down
11 changes: 10 additions & 1 deletion lib/src/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,19 @@ abstract class Module {
/// Accesses the [Logic] associated with this [Module]s input port
/// named [name].
///
/// Logic within this [Module] should consume this signal.
/// Only logic within this [Module] should consume this signal.
@protected
Logic input(String name) => _inputs.containsKey(name)
? _inputs[name]!
: throw Exception(
'Input name "$name" not found as an input to this Module.');

/// Provides the [input] named [name] if it exists, otherwise `null`.
///
/// Only logic within this [Module] should consume this signal.
@protected
Logic? tryInput(String name) => _inputs[name];

/// Accesses the [Logic] associated with this [Module]s output port
/// named [name].
///
Expand All @@ -108,6 +114,9 @@ abstract class Module {
: throw Exception(
'Output name "$name" not found as an output of this Module.');

/// Provides the [output] named [name] if it exists, otherwise `null`.
Logic? tryOutput(String name) => _outputs[name];

/// Returns true iff [net] is the same [Logic] as the input port of this
/// [Module] with the same name.
bool isInput(Logic net) =>
Expand Down
159 changes: 112 additions & 47 deletions lib/src/modules/conditional.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// conditional.dart
// Definitions of conditionallly executed hardware constructs (if/else statements, always_comb, always_ff, etc.)
// Definitions of conditionallly executed hardware constructs
// (if/else statements, always_comb, always_ff, etc.)
//
// 2021 May 7
// Author: Max Korbel <[email protected]>
Expand Down Expand Up @@ -1548,25 +1549,49 @@ ${padding}end ''');

/// Constructs a positive edge triggered flip flop on [clk].
///
/// It returns [FlipFlop.q]. When optional [en] is provided, an additional
/// input will be created for flop. If optional [en] is high or not provided,
/// output will vary as per input[d]. For low [en], output remains frozen
/// irrespective of input [d]
Logic flop(Logic clk, Logic d, {Logic? en}) => FlipFlop(clk, d, en: en).q;
/// It returns [FlipFlop.q].
///
/// When the optional [en] is provided, an additional input will be created for
/// flop. If optional [en] is high or not provided, output will vary as per
/// input[d]. For low [en], output remains frozen irrespective of input [d].
///
/// When the optional [reset] is provided, the flop will be reset (active-high).
/// If no [resetValue] is provided, the reset value is always `0`. Otherwise,
/// it will reset to the provided [resetValue].
Logic flop(
Logic clk,
Logic d, {
Logic? en,
Logic? reset,
dynamic resetValue,
}) =>
FlipFlop(
clk,
d,
en: en,
reset: reset,
resetValue: resetValue,
).q;

/// Represents a single flip-flop with no reset.
class FlipFlop extends Module with CustomSystemVerilog {
/// Name for the enable input of this flop
late final String _enName;
final String _enName = Module.unpreferredName('en');

/// Name for the clk of this flop.
late final String _clkName;
final String _clkName = Module.unpreferredName('clk');

/// Name for the input of this flop.
late final String _dName;
final String _dName = Module.unpreferredName('d');

/// Name for the output of this flop.
late final String _qName;
final String _qName = Module.unpreferredName('q');

/// Name for the reset of this flop.
final String _resetName = Module.unpreferredName('reset');

/// Name for the reset value of this flop.
final String _resetValueName = Module.unpreferredName('resetValue');

/// The clock, posedge triggered.
late final Logic _clk = input(_clkName);
Expand All @@ -1576,85 +1601,125 @@ class FlipFlop extends Module with CustomSystemVerilog {
/// If enable is high or enable is not provided then flop output will vary
/// on the basis of clock [_clk] and input [_d]. If enable is low, then
/// output of the flop remains frozen irrespective of the input [_d].
late final Logic _en = input(_enName);
late final Logic? _en = tryInput(_enName);

/// Optional reset input to the flop.
late final Logic? _reset = tryInput(_resetName);

/// The input to the flop.
late final Logic _d = input(_dName);

/// The output of the flop.
late final Logic q = output(_qName);

/// To track if optional enable is provided or not.
late final bool _isEnableProvided;
/// The reset value for this flop, if it was a port.
Logic? _resetValuePort;

/// The reset value for this flop, if it was a constant.
///
/// Only initialized if a constant value is provided.
late LogicValue _resetValueConst;

/// Constructs a flip flop which is positive edge triggered on [clk].
///
/// When optional [en] is provided, an additional input will be created for
/// flop. If optional [en] is high or not provided, output will vary as per
/// input[d]. For low [en], output remains frozen irrespective of input [d]
FlipFlop(Logic clk, Logic d, {Logic? en, super.name = 'flipflop'}) {
///
/// When the optional [reset] is provided, the flop will be reset active-high.
/// If no [resetValue] is provided, the reset value is always `0`. Otherwise,
/// it will reset to the provided [resetValue]. The type of [resetValue] must
/// be a valid driver of a [ConditionalAssign] (e.g. [Logic], [LogicValue],
/// [int], etc.).
FlipFlop(
Logic clk,
Logic d, {
Logic? en,
Logic? reset,
dynamic resetValue,
super.name = 'flipflop',
}) {
if (clk.width != 1) {
throw Exception('clk must be 1 bit');
}

_clkName = Module.unpreferredName('clk');
_dName = Module.unpreferredName('d');
_qName = Module.unpreferredName('q');

addInput(_clkName, clk);
addInput(_dName, d, width: d.width);
addOutput(_qName, width: d.width);

if (en != null) {
if (en.width != 1) {
throw PortWidthMismatchException(en, 1);
}
_enName = Module.unpreferredName('en');
addInput(_enName, en);
_isEnableProvided = true;
}

_setupWithEnable();
} else {
_isEnableProvided = false;
if (reset != null) {
addInput(_resetName, reset);

_setup();
if (resetValue != null && resetValue is Logic) {
_resetValuePort = addInput(_resetValueName, resetValue, width: d.width);
} else {
_resetValueConst = LogicValue.of(resetValue ?? 0, width: d.width);
}
}

_setup();
}

/// Performs setup for custom functional behavior.
void _setup() {
Sequential(_clk, [q < _d]);
}
var contents = [q < _d];

if (_en != null) {
contents = [If(_en!, then: contents)];
}

/// Performs setup for custom functional behavior with enable
void _setupWithEnable() {
Sequential(_clk, [
If(_en, then: [q < _d])
]);
Sequential(
_clk,
contents,
reset: _reset,
resetValues:
_reset != null ? {q: _resetValuePort ?? _resetValueConst} : null,
);
}

@override
String instantiationVerilog(String instanceType, String instanceName,
Map<String, String> inputs, Map<String, String> outputs) {
if (_isEnableProvided) {
if (inputs.length != 3 || outputs.length != 1) {
throw Exception('FlipFlop has exactly three inputs and one output.');
}
} else {
if (inputs.length != 2 || outputs.length != 1) {
throw Exception('FlipFlop has exactly two inputs and one output.');
}
var expectedInputs = 2;
if (_en != null) {
expectedInputs++;
}
if (_reset != null) {
expectedInputs++;
}
if (_resetValuePort != null) {
expectedInputs++;
}

if (inputs.length != expectedInputs || outputs.length != 1) {
throw Exception(
'FlipFlop has exactly $expectedInputs inputs and one output.');
}

final clk = inputs[_clkName]!;
final d = inputs[_dName]!;
final q = outputs[_qName]!;

if (_isEnableProvided) {
final en = inputs[_enName]!;
return 'always_ff @(posedge $clk) if($en) $q <= $d; // $instanceName';
} else {
return 'always_ff @(posedge $clk) $q <= $d; // $instanceName';
final svBuffer = StringBuffer('always_ff @(posedge $clk) ');

if (_reset != null) {
final resetValueString = _resetValuePort != null
? inputs[_resetValueName]!
: _resetValueConst.toString();
svBuffer
.write('if(${inputs[_resetName]}) $q <= $resetValueString; else ');
}

if (_en != null) {
svBuffer.write('if(${inputs[_enName]!}) ');
}

svBuffer.write('$q <= $d; // $instanceName');

return svBuffer.toString();
}
}
2 changes: 2 additions & 0 deletions lib/src/values/logic_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ abstract class LogicValue implements Comparable<LogicValue> {
return LogicValue.ofIterable(val).zeroExtend(width);
}
}
} else if (val == null) {
throw LogicValueConstructionException('Cannot construct from `null`.');
} else {
throw LogicValueConstructionException('Unrecognized value type "$val" - '
'Unknown type ${val.runtimeType}'
Expand Down
Loading