Skip to content

Commit

Permalink
Sum and Counter (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Sep 16, 2024
1 parent 19eeb38 commit b731bd8
Show file tree
Hide file tree
Showing 25 changed files with 1,676 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/general.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
uses: flutter-actions/setup-flutter@v2
with:
channel: stable
version: 3.16.3
version: 3.24.2

- name: Analyze flutter source
run: tool/gh_actions/analyze_flutter_source.sh
Expand Down Expand Up @@ -151,7 +151,7 @@ jobs:
uses: flutter-actions/setup-flutter@v2
with:
channel: stable
version: 3.16.3
version: 3.24.2

- name: Build static site
run: tool/gh_actions/hcl_site_generation_build.sh
Expand Down
15 changes: 4 additions & 11 deletions confapp/lib/hcl/view/screen/content_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,13 @@ class _SVGeneratorState extends State<SVGenerator> {
);
final key = Key(label);

if (knob is IntConfigKnob || knob is StringConfigKnob) {
if (knob is TextConfigKnob) {
selector = TextFormField(
key: key,
initialValue: (knob is IntConfigKnob && knob.value > 255)
? '0x${knob.value.toRadixString(16)}'
: knob.value.toString(),
initialValue: knob.valueString,
decoration: decoration,
validator: (value) {
if (value!.isEmpty) {
if ((value == null || value.isEmpty) && !knob.allowEmpty) {
return 'Please enter value';
}
return null;
Expand All @@ -91,12 +89,7 @@ class _SVGeneratorState extends State<SVGenerator> {
return;
}

if (knob is IntConfigKnob) {
final newValue = int.tryParse(value.toString());
knob.value = newValue ?? knob.value;
} else {
knob.value = value;
}
knob.setValueFromString(value);
});
},
);
Expand Down
2 changes: 1 addition & 1 deletion confapp/lib/hcl/view/screen/schematic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const _suffix = r"""
svg.call(zoom)
.on("dblclick.zoom", null)
graph = JSON.parse(exmpl);
graph = JSON.parse(exmpl.replaceAll("\\", "__"));
if ("creator" in graph) {
graph = d3.HwSchematic.fromYosys(graph);
}
Expand Down
15 changes: 15 additions & 0 deletions confapp/web/flutter_bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{{flutter_js}}
{{flutter_build_config}}
_flutter.loader.load({
serviceWorkerSettings: {
serviceWorkerVersion: {{flutter_service_worker_version}},
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
},
config: {
canvasKitBaseUrl: 'canvaskit/',
}
});
20 changes: 1 addition & 19 deletions confapp/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,10 @@
<title>confapp</title>
<link rel="manifest" href="manifest.json">

<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>
3 changes: 2 additions & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ Some in-development items will have opened issues, as well. Feel free to create
- Binary-Coded Decimal (BCD)
- [Rotate](./components/rotate.md)
- Counters
- Binary counter
- [Summation](./components/summation.md#sum)
- [Binary counter](./components/summation.md#counter)
- Gray counter
- Pseudorandom
- LFSR
Expand Down
40 changes: 40 additions & 0 deletions doc/components/summation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Summation

ROHD-HCL comes with combinational and sequential components for summing any number of input values, including support for increment/decrement and saturation/roll-over behavior.

## SumInterface

The `SumInterface` is shared between [`Sum`](#sum) and [`Counter`](#counter) components and represents a single element to be summed. Each instance of a `SumInterface` has an associated `amount`, which could either be a fixed constant value (`fixedAmount`) or a dynamic `Logic`. Fixed amounts will do some automatic width inference, unless a `width` is specified. The interface can also optionally include an enable signal. It is implemented as a `PairInterface` where all ports are `fromProvider`. Each interface may be either incrementing or decrementing.

```dart
// An interface with a dynamic 4-bit amount to increment by
SumInterface(width: 4);
```

## Sum

The `Sum` component takes a list of `SumInterface`s and adds them all up. The `saturates` configuration enables saturation behavior, otherwise there will be roll-over at overflow/underflow of the counter at `minValue` and `maxValue`. The sum can also be given an `initialValue` to start at.

Internally, the `Sum` component builds a wider bus which is large enough to hold the biggest possible intermediate value during summation before consideration of overflow and saturation.

Note that the implementation's size and complexity when synthesized depend significantly on the configuration. For example, if everything is a nice power-of-2 number, then the logic is much simpler than otherwise where hardware modulos may be required to properly handle roll-over/under scenarios.

A simpler `Sum.ofLogics` constructor has less configurability, but can be passed a simple `List<Logic>` without needing to construct per-element interfaces.

```dart
// An 8-bit sum of a list of `SumInterface`s
Sum(intfs, width: 8);
```

## Counter

The `Counter` component is a similarly configurable version of the `Sum` which maintains a sequential element to store the previous value.

One additional nice feature of the `Counter` is that it supports a `restart` in addition to the normal `reset`. While `reset` will reset the internal flops, `restart` will re-initialize the internal `Sum` back to the reset value, but still perform the computation on inputs in the current cycle. This is especially useful in case you want to restart a counter while events worth counting may still be occuring.

The `Counter` also has a `Counter.simple` constructor which is intended for very basic scenarios like "count up by 1 each cycle".

```dart
// A counter which increments by 1 each cycle up to 5, then rolls over.
Counter.simple(clk: clk, reset: reset, maxValue: 5);
```
1 change: 1 addition & 0 deletions lib/rohd_hcl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export 'src/models/models.dart';
export 'src/rotate.dart';
export 'src/shift_register.dart';
export 'src/sort.dart';
export 'src/summation/summation.dart';
export 'src/utils.dart';
2 changes: 2 additions & 0 deletions lib/src/component_config/components/component_registry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ List<Configurator> get componentRegistry => [
FifoConfigurator(),
EccConfigurator(),
RoundRobinArbiterConfigurator(),
CounterConfigurator(),
SumConfigurator(),
PriorityArbiterConfigurator(),
RippleCarryAdderConfigurator(),
CarrySaveMultiplierConfigurator(),
Expand Down
1 change: 1 addition & 0 deletions lib/src/component_config/components/components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export 'config_ripple_carry_adder.dart';
export 'config_rotate.dart';
export 'config_round_robin_arbiter.dart';
export 'config_sort.dart';
export 'config_summation.dart';
144 changes: 144 additions & 0 deletions lib/src/component_config/components/config_summation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// config_summation.dart
// Configurators for summation.
//
// 2024 September 6
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_hcl/src/summation/summation_base.dart';

/// A knob for a single sum interface.
class SumInterfaceKnob extends GroupOfKnobs {
/// Whether the sum interface has an enable signal.
ToggleConfigKnob hasEnableKnob = ToggleConfigKnob(value: false);

/// Whether the sum interface has a fixed value.
ToggleConfigKnob isFixedValueKnob = ToggleConfigKnob(value: false);

/// The fixed value of the sum interface, only present when [isFixedValueKnob]
/// is true.
IntConfigKnob fixedValueKnob = IntConfigKnob(value: 1);

/// The width of the sum interface.
IntOptionalConfigKnob widthKnob = IntOptionalConfigKnob(value: 8);

/// Whether the sum interface increments (vs. decrements).
ToggleConfigKnob incrementsKnob = ToggleConfigKnob(value: true);

/// Creates a new sum interface knob.
SumInterfaceKnob() : super({}, name: 'Sum Interface');

@override
Map<String, ConfigKnob<dynamic>> get subKnobs => {
'Has Enable': hasEnableKnob,
'Is Fixed Value': isFixedValueKnob,
if (isFixedValueKnob.value) 'Fixed Value': fixedValueKnob,
'Width': widthKnob,
'Increments': incrementsKnob,
};
}

/// A configurator for a module like [SummationBase].
abstract class SummationConfigurator extends Configurator {
/// The interface knobs.
final ListOfKnobsKnob sumInterfaceKnobs = ListOfKnobsKnob(
count: 1,
generateKnob: (i) => SumInterfaceKnob(),
name: 'Sum Interfaces',
);

/// The width.
final IntOptionalConfigKnob widthKnob = IntOptionalConfigKnob(value: null);

/// The minimum value.
final IntOptionalConfigKnob minValueKnob = IntOptionalConfigKnob(value: 0);

/// The maximum value.
final IntOptionalConfigKnob maxValueKnob = IntOptionalConfigKnob(value: null);

/// Whether the output saturates (vs. rolling over/under).
final ToggleConfigKnob saturatesKnob = ToggleConfigKnob(value: false);

@override
Map<String, ConfigKnob<dynamic>> get knobs => {
'Sum Interfaces': sumInterfaceKnobs,
'Width': widthKnob,
'Minimum Value': minValueKnob,
'Maximum Value': maxValueKnob,
'Saturates': saturatesKnob,
};
}

/// A configurator for [Sum].
class SumConfigurator extends SummationConfigurator {
/// The initial value.
final IntConfigKnob initialValueKnob = IntConfigKnob(value: 0);

@override
Map<String, ConfigKnob<dynamic>> get knobs => {
...super.knobs,
'Initial Value': initialValueKnob,
};

@override
Module createModule() => Sum(
sumInterfaceKnobs.knobs
.map((e) => e as SumInterfaceKnob)
.map((e) => SumInterface(
hasEnable: e.hasEnableKnob.value,
fixedAmount:
e.isFixedValueKnob.value ? e.fixedValueKnob.value : null,
width: e.widthKnob.value,
increments: e.incrementsKnob.value,
))
.toList(),
initialValue: initialValueKnob.value,
width: widthKnob.value,
minValue: minValueKnob.value,
maxValue: maxValueKnob.value,
saturates: saturatesKnob.value,
);

@override
String get name => 'Sum';
}

/// A configurator for [Counter].
class CounterConfigurator extends SummationConfigurator {
/// The reset value.
final IntConfigKnob resetValueKnob = IntConfigKnob(value: 0);

@override
Map<String, ConfigKnob<dynamic>> get knobs => {
...super.knobs,
'Reset Value': resetValueKnob,
};

@override
Module createModule() => Counter(
sumInterfaceKnobs.knobs
.map((e) => e as SumInterfaceKnob)
.map((e) => SumInterface(
hasEnable: e.hasEnableKnob.value,
fixedAmount:
e.isFixedValueKnob.value ? e.fixedValueKnob.value : null,
width: e.widthKnob.value,
increments: e.incrementsKnob.value,
))
.toList(),
resetValue: resetValueKnob.value,
width: widthKnob.value,
minValue: minValueKnob.value,
maxValue: maxValueKnob.value,
saturates: saturatesKnob.value,
clk: Logic(),
reset: Logic(),
);

@override
String get name => 'Counter';
}
2 changes: 2 additions & 0 deletions lib/src/component_config/config_knobs/config_knobs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export 'choice_config_knob.dart';
export 'config_knob.dart';
export 'group_of_knobs_knob.dart';
export 'int_config_knob.dart';
export 'int_optional_config_knob.dart';
export 'list_of_knobs_knob.dart';
export 'string_config_knob.dart';
export 'text_config_knob.dart';
export 'toggle_config_knob.dart';
12 changes: 10 additions & 2 deletions lib/src/component_config/config_knobs/int_config_knob.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,29 @@
import 'package:rohd_hcl/rohd_hcl.dart';

/// A knob to store an [int].
class IntConfigKnob extends ConfigKnob<int> {
class IntConfigKnob extends TextConfigKnob<int> {
/// Creates a new config knob with the specified initial [value].
IntConfigKnob({required super.value});

@override
Map<String, dynamic> toJson() =>
{'value': value > 255 ? '0x${value.toRadixString(16)}' : value};

@override
String get valueString =>
value > 255 ? '0x${value.toRadixString(16)}' : value.toString();

@override
void loadJson(Map<String, dynamic> decodedJson) {
final val = decodedJson['value'];
if (val is String) {
value = int.parse(val);
setValueFromString(val);
} else {
value = val as int;
}
}

@override
void setValueFromString(String valueString) =>
value = int.tryParse(valueString) ?? value;
}
Loading

0 comments on commit b731bd8

Please sign in to comment.