Skip to content

Pipeline fixes and improvements #447

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

Merged
merged 3 commits into from
Dec 27, 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
1 change: 1 addition & 0 deletions lib/src/exceptions/module/module_exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
// SPDX-License-Identifier: BSD-3-Clause

export 'module_not_built_exception.dart';
export 'port_does_not_exist_exception.dart';
export 'port_width_mismatch_exception.dart';
16 changes: 16 additions & 0 deletions lib/src/exceptions/module/port_does_not_exist_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// port_does_not_exist_exception.dart
// Definition for exception when a port is not present.
//
// 2023 December 26
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';

/// An [Exception] thrown when a port has the wrong width.
class PortDoesNotExistException extends RohdException {
/// Constructs a new [Exception] for when a port is not present.
PortDoesNotExistException(super.message);
}
4 changes: 2 additions & 2 deletions lib/src/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ abstract class Module {
@protected
Logic input(String name) => _inputs.containsKey(name)
? _inputs[name]!
: throw Exception(
: throw PortDoesNotExistException(
'Input name "$name" not found as an input to this Module.');

/// Provides the [input] named [name] if it exists, otherwise `null`.
Expand All @@ -110,7 +110,7 @@ abstract class Module {
/// to consume this within this [Module] as well.
Logic output(String name) => _outputs.containsKey(name)
? _outputs[name]!
: throw Exception(
: throw PortDoesNotExistException(
'Output name "$name" not found as an output of this Module.');

/// Provides the [output] named [name] if it exists, otherwise `null`.
Expand Down
6 changes: 4 additions & 2 deletions lib/src/modules/conditional.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class _SsaLogic extends Logic {

/// Constructs a new SSA node referring to a signal in a specific context.
_SsaLogic(this._ref, this._context)
: super(width: _ref.width, name: _ref.name);
: super(width: _ref.width, name: _ref.name, naming: Naming.mergeable);
}

/// Represents a block of combinational logic.
Expand Down Expand Up @@ -1036,7 +1036,7 @@ Logic cases(Logic expression, Map<dynamic, dynamic> conditions,
}
}

final result = Logic(name: 'result', width: width);
final result = Logic(name: 'result', width: width, naming: Naming.mergeable);

Combinational([
Case(
Expand Down Expand Up @@ -1280,6 +1280,7 @@ ${subPadding}end
phiMappings[localMapping.key] = Logic(
name: '${localMapping.key.name}_phi',
width: localMapping.key.width,
naming: Naming.mergeable,
);
}

Expand Down Expand Up @@ -1570,6 +1571,7 @@ ${padding}end ''');
phiMappings[localMapping.key] = Logic(
name: '${localMapping.key.name}_phi',
width: localMapping.key.width,
naming: Naming.mergeable,
);
}

Expand Down
60 changes: 42 additions & 18 deletions lib/src/modules/pipeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,20 @@ class Pipeline {
late final List<_PipeStage> _stages;

/// Returns the number of stages in this pipeline.
int get _numStages => _stages.length;
///
/// Note that this will be one greater than the number of elements in `stages`
/// during construction, as well as one greater than the number of flop
/// stages. This represents the count of chunks of combinational logic
/// separated by flops.
int get stageCount => _stages.length;

/// A map of reset values for every signal.
late final Map<Logic, dynamic> _resetValues;

/// Tracks whether this [Pipeline] is done being constructed to conditionally
/// run safety checks on API calls.
bool _constructionComplete = false;

/// Constructs a simple pipeline, separating arbitrary combinational logic by
/// flop stages.
///
Expand All @@ -113,6 +122,12 @@ class Pipeline {
/// signals for a given pipe stage. Flops are positive edge triggered
/// based on [clk].
///
/// Then `i`th element of [stages] defines the combinational logic driving a
/// flop of index `i`. That is, the first entry in [stages] drives the
/// first set of flops, so logic defined in the first stage combinationally
/// consumes inputs to the [Pipeline]. The output of the [Pipeline] is
/// driven by flops driven by the last entry of [stages].
///
/// Signals to be pipelined can optionally be specified in the [signals]
/// list. Any signal referenced in a stage via the [PipelineStageInfo]
/// will automatically be included in the entire pipeline.
Expand All @@ -124,7 +139,7 @@ class Pipeline {
/// in [resetValues] should be a type acceptable to [Logic]'s `put` function.
///
/// Each stage can be stalled independently using [stalls], where every index
/// of [stalls] corresponds to the index of the stage to be stalled. When
/// of [stalls] corresponds to the index of the stage to be stalled. When
/// a stage's stall is asserted, the output of that stage will not change.
Pipeline(Logic clk,
{List<List<Conditional> Function(PipelineStageInfo p)> stages = const [],
Expand All @@ -149,31 +164,30 @@ class Pipeline {
_stages = stages.map(_PipeStage.new).toList();
_stages.add(_PipeStage((p) => [])); // output stage

if (_numStages == 0) {
return;
}

_resetValues = Map.from(resetValues);

_setStalls(stalls);

signals.forEach(_add);

for (var stageIndex = 0; stageIndex < _numStages; stageIndex++) {
for (var stageIndex = 0; stageIndex < stageCount; stageIndex++) {
Combinational.ssa((ssa) {
// keep track of the previously registered logics:
final prevRegisteredLogics = _registeredLogics.toSet();

// build the conditionals first so that we populate _registeredLogics
final stageConditionals = _stages[stageIndex]
.operation(PipelineStageInfo._(this, stageIndex, ssa));
final stageConditionals = _stages[stageIndex].operation(
PipelineStageInfo._(this, stageIndex, ssa),
);

// if any new logics were registered, add some extra assignments
// to make up the gap since it didn't get included in prior generations
for (final l in _registeredLogics) {
if (!prevRegisteredLogics.contains(l)) {
for (var i = 0; i < stageIndex; i++) {
_o(l, i) <= _i(l, i);
// make sure to hook up in-to-out through main for .get's
get(l, i) <= _i(l, i);
_o(l, i) <= get(l, i);
}
}
}
Expand All @@ -190,16 +204,18 @@ class Pipeline {
_o(l, stageIndex) <= get(l, stageIndex);
}
}

_constructionComplete = true;
}

/// Sets up the stall signals across [_stages].
void _setStalls(List<Logic?>? stalls) {
if (stalls != null) {
if (stalls.length != _numStages - 1) {
if (stalls.length != stageCount - 1) {
throw Exception('Stall list length (${stalls.length}) must match '
'number of stages (${_numStages - 1}).');
'number of stages (${stageCount - 1}).');
}
for (var i = 0; i < _numStages - 1; i++) {
for (var i = 0; i < stageCount - 1; i++) {
final stall = stalls[i];
if (stall == null) {
continue;
Expand Down Expand Up @@ -230,7 +246,7 @@ class Pipeline {
}

var ffAssignsWithStall =
List<Conditional>.generate(_numStages - 1, (index) {
List<Conditional>.generate(stageCount - 1, (index) {
final stall = _stages[index].stall;
final ffAssign = ffAssigns[index] as ConditionalAssign;
final driver = stall != null
Expand Down Expand Up @@ -283,12 +299,20 @@ class Pipeline {
/// Gets the pipelined version of [logic]. By default [stageIndex] is the
/// last stage (the output of the pipeline).
///
/// If the signal is not already a part of this [Pipeline], the signal will be
/// added to the [Pipeline]. Use [stageIndex] to select the value of [logic]
/// at a specific stage of the pipeline.
/// During construction, if the signal is not already a part of this
/// [Pipeline], the signal will be added to the [Pipeline]. After
/// construction, only signals registered during construction can be accessed.
///
/// Use [stageIndex] to select the value of [logic] at a specific stage of the
/// pipeline. The [stageIndex] must be less than [stageCount].
Logic get(Logic logic, [int? stageIndex]) {
if (!_isRegistered(logic)) {
_add(logic);
if (_constructionComplete) {
throw PortDoesNotExistException(
'Signal $logic was not piped through this Pipeline.');
} else {
_add(logic);
}
}

stageIndex ??= _stages.length - 1;
Expand Down
2 changes: 1 addition & 1 deletion test/comb_math_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class SimplerExampleSsa extends Module {
a = addInput('a', a, width: 8);
addOutput('b', width: 8);

final inner = Logic(name: 'inner', width: 8);
final inner = Logic(name: 'inner', width: 8, naming: Naming.mergeable);

Combinational.ssa((s) => [
s(inner) < 0xf,
Expand Down
2 changes: 1 addition & 1 deletion test/comb_mod_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'package:test/test.dart';

class IncrModule extends Module {
Logic get result => output('result');
IncrModule(Logic toIncr) {
IncrModule(Logic toIncr) : super(name: 'incr') {
toIncr = addInput('toIncr', toIncr, width: toIncr.width);
addOutput('result', width: toIncr.width);
result <= toIncr + 1;
Expand Down
Loading