Skip to content

Commit

Permalink
Reset for flops and try ports (#410)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Sep 14, 2023
1 parent 858daad commit 6c35997
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 69 deletions.
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 @@ -1552,25 +1553,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 @@ -1580,85 +1605,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

0 comments on commit 6c35997

Please sign in to comment.