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

Edge Detector #75

Merged
merged 6 commits into from
Jan 30, 2024
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
2 changes: 1 addition & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Some in-development items will have opened issues, as well. Feel free to create
- [Count bit occurence](./components/count.md)
- Count pattern occurence
- Detection
- Edge detection
- [Edge detection](./components/edge_detector.md)
- Sort
- [Bitonic sort](./components/sort.md#bitonic-sort)
- Arithmetic
Expand Down
3 changes: 3 additions & 0 deletions doc/components/edge_detector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Edge Detection

The `EdgeDetector` is a simple utility to determine whether the current value of a 1-bit signal is different from the value in the previous cycle. It is a fully synchronous design, so it does not asynchronously detect edges. It optionally supports a reset, with an optional reset value. It can be configured to detect positive, negative, or "any" edges.
3 changes: 2 additions & 1 deletion lib/rohd_hcl.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'src/adder.dart';
export 'src/arbiters/arbiters.dart';
export 'src/carry_save_mutiplier.dart';
export 'src/component_config/component_config.dart';
export 'src/count.dart';
export 'src/edge_detector.dart';
export 'src/encodings/encodings.dart';
export 'src/error_checking/error_checking.dart';
export 'src/exceptions.dart';
Expand Down
3 changes: 2 additions & 1 deletion lib/src/component_config/components/component_registry.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// component_registry.dart
Expand All @@ -21,4 +21,5 @@ List<Configurator> get componentRegistry => [
BitonicSortConfigurator(),
OneHotConfigurator(),
RegisterFileConfigurator(),
EdgeDetectorConfigurator(),
];
3 changes: 2 additions & 1 deletion lib/src/component_config/components/components.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'config_carry_save_multiplier.dart';
export 'config_ecc.dart';
export 'config_edge_detector.dart';
export 'config_fifo.dart';
export 'config_one_hot.dart';
export 'config_priority_arbiter.dart';
Expand Down
50 changes: 50 additions & 0 deletions lib/src/component_config/components/config_edge_detector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// config_edge_detector.dart
// Configurator for a EdgeDetector.
//
// 2024 January 29
// Author: Max Korbel <[email protected]>

import 'dart:collection';

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_vf/rohd_vf.dart';

/// A [Configurator] for [EdgeDetector].
class EdgeDetectorConfigurator extends Configurator {
/// A knob controlling the type of edge detector.
final ChoiceConfigKnob<Edge> edgeTypeKnob =
ChoiceConfigKnob(Edge.values, value: Edge.pos);

/// A knob controlling whether there is a reset.
final ToggleConfigKnob hasResetKnob = ToggleConfigKnob(value: true);

/// A knob controlling the reset value.
final ChoiceConfigKnob<dynamic> resetValueKnob =
ChoiceConfigKnob([0, 1, 'Input'], value: 0);

@override
Module createModule() => EdgeDetector(
Logic(),
clk: Logic(),
reset: hasResetKnob.value ? Logic() : null,
resetValue: hasResetKnob.value
? resetValueKnob.value == 'Input'
? Logic()
: resetValueKnob.value
: null,
);

@override
Map<String, ConfigKnob<dynamic>> get knobs => UnmodifiableMapView({
'Edge Type': edgeTypeKnob,
'Has Reset': hasResetKnob,
if (hasResetKnob.value) 'Reset Value': resetValueKnob,
});

@override
final String name = 'Edge Detector';
}
3 changes: 1 addition & 2 deletions lib/src/count.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright (C) 2023 Intel Corporation
// Copyright (C) 2023-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// count.dart
// Implementation of Count Functionality.
//
// 2023 July 11
// Author: Rahul Gautham Putcha <[email protected]>
//

import 'dart:math';

Expand Down
77 changes: 77 additions & 0 deletions lib/src/edge_detector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// edge_detector.dart
// Implementation of edge detectors.
//
// 2024 January 29
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/src/exceptions.dart';
import 'package:rohd_vf/rohd_vf.dart';

/// An edge detector for positive, negative, or any edge on a signal relative to
/// the previous clock cycle.
class EdgeDetector extends Module {
/// The type of edge(s) to detect.
final Edge edgeType;

/// The name of the [edge] output.
String get _edgeName => '${edgeType.name}_edge';

/// High for one cycle when the input signal has an [edgeType] transition.
Logic get edge => output(_edgeName);

/// Creates an edge detector which flags an [edge] when [signal] changes
/// relative to its value in the previous cycle.
///
/// [signal] must be 1-bit.
///
/// If a [reset] is provided, then the first cycle after [reset] is
/// deasserted, [signal] will be compared to [resetValue] (or 0, if not
/// provided).
EdgeDetector(
Logic signal, {
required Logic clk,
Logic? reset,
dynamic resetValue,
this.edgeType = Edge.pos,
String? name,
}) : super(name: name ?? '${edgeType.name}_edge_detector') {
if (signal.width != 1 ||
(resetValue is Logic && resetValue.width != 1) ||
(resetValue is LogicValue && resetValue.width != 1)) {
throw RohdHclException('Can only detect edges on 1-bit signals.');
}

if (reset == null && resetValue != null) {
throw RohdHclException(
'If no reset is provided, then a resetValue cannot be provided.');
}

clk = addInput('clk', clk);
signal = addInput('signal', signal);

if (reset != null) {
reset = addInput('reset', reset);
}

if (resetValue != null && resetValue is Logic) {
resetValue = addInput('resetValue', resetValue);
}

addOutput(_edgeName);

final previousValue = Logic(name: 'previousValue')
..gets(
flop(clk, reset: reset, resetValue: resetValue, signal),
);

edge <=
[
if (edgeType case Edge.pos || Edge.any) ~previousValue & signal,
if (edgeType case Edge.neg || Edge.any) previousValue & ~signal,
].swizzle().or();
}
}
2 changes: 0 additions & 2 deletions test/arbiter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,6 @@ void main() {
rrArbType.constructor(requests, clk: clk, reset: reset);
await arbiter.build();

WaveDumper(arbiter);

final grantCounts = List.generate(arbiter.count, (_) => 0);

for (var i = 0; i < arbiter.count; i++) {
Expand Down
116 changes: 116 additions & 0 deletions test/edge_detector_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// edge_detector_test.dart
// Tests for edge detector.
//
// 2024 January 29
// Author: Max Korbel <[email protected]>

import 'dart:async';

import 'package:rohd/rohd.dart';
import 'package:rohd_hcl/rohd_hcl.dart';
import 'package:rohd_vf/rohd_vf.dart';
import 'package:test/test.dart';

void main() {
tearDown(() async {
await Simulator.reset();
});

Future<void> testEdgeDetector(Edge edgeType) async {
final clk = SimpleClockGenerator(10).clk;
final a = Logic();
final mod = EdgeDetector(a, clk: clk, edgeType: edgeType);
await mod.build();
final edge = mod.edge;

Simulator.setMaxSimTime(200);
unawaited(Simulator.run());

a.inject(0);
await clk.waitCycles(3);
expect(edge.value.toBool(), isFalse);

a.inject(1);

await clk.nextNegedge;

expect(edge.value.toBool(), edgeType == Edge.pos || edgeType == Edge.any);

await clk.nextPosedge;

await clk.nextNegedge;

expect(edge.value.toBool(), isFalse);

await clk.nextPosedge;

a.inject(0);

await clk.nextNegedge;

expect(edge.value.toBool(), edgeType == Edge.neg || edgeType == Edge.any);

await clk.nextPosedge;

await clk.nextNegedge;

expect(edge.value.toBool(), isFalse);

await Simulator.endSimulation();
}

for (final edgeType in Edge.values) {
test('${edgeType.name} edge detector', () async {
await testEdgeDetector(edgeType);
});
}

test('custom reset value', () async {
final clk = SimpleClockGenerator(10).clk;
final a = Logic();
final reset = Logic();
const resetValue = 1;
final mod = EdgeDetector(
a,
clk: clk,
reset: reset,
resetValue: resetValue,
edgeType: Edge.neg,
);
await mod.build();
final edge = mod.edge;

Simulator.setMaxSimTime(200);
unawaited(Simulator.run());

// leave `a` floating intentionally
reset.inject(0);

await clk.waitCycles(3);

reset.inject(1);

await clk.nextPosedge;
reset.inject(0);
a.inject(0);

await clk.nextNegedge;

expect(edge.value.toBool(), isTrue);

await Simulator.endSimulation();
});

test('exception: bad width signal', () {
expect(() => EdgeDetector(Logic(width: 2), clk: Logic()),
throwsA(isA<RohdHclException>()));
});

test('exception: reset value without reset', () {
expect(() => EdgeDetector(Logic(), clk: Logic(), resetValue: 5),
throwsA(isA<RohdHclException>()));
});
}
3 changes: 0 additions & 3 deletions test/fifo_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -642,9 +642,6 @@ void main() {

Directory('tmp_test').createSync();

// await fifoTest.fifo.build();
// WaveDumper(fifoTest.fifo);

final tracker =
FifoTracker(fifoTest.fifo, outputFolder: 'tmp_test', dumpTable: false);

Expand Down
2 changes: 0 additions & 2 deletions test/memory_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,6 @@ void main() {

await mem.build();

WaveDumper(mem);

wrPorts = wrPorts.map((oldWrPort) {
final newWrPort = MaskedDataPortInterface(dataWidth, addrWidth)
..en.put(0);
Expand Down
Loading