diff --git a/doc/README.md b/doc/README.md index cc31d3a8b..89473dd84 100644 --- a/doc/README.md +++ b/doc/README.md @@ -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 diff --git a/doc/components/edge_detector.md b/doc/components/edge_detector.md new file mode 100644 index 000000000..7984bd989 --- /dev/null +++ b/doc/components/edge_detector.md @@ -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. diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 2afd3387b..e16a51e56 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'src/adder.dart'; @@ -6,6 +6,7 @@ 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'; diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index 5068b4718..4b270fb74 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // component_registry.dart @@ -21,4 +21,5 @@ List get componentRegistry => [ BitonicSortConfigurator(), OneHotConfigurator(), RegisterFileConfigurator(), + EdgeDetectorConfigurator(), ]; diff --git a/lib/src/component_config/components/components.dart b/lib/src/component_config/components/components.dart index 6788b841f..1f51c899a 100644 --- a/lib/src/component_config/components/components.dart +++ b/lib/src/component_config/components/components.dart @@ -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'; diff --git a/lib/src/component_config/components/config_edge_detector.dart b/lib/src/component_config/components/config_edge_detector.dart new file mode 100644 index 000000000..ec061ea31 --- /dev/null +++ b/lib/src/component_config/components/config_edge_detector.dart @@ -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 + +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 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 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> get knobs => UnmodifiableMapView({ + 'Edge Type': edgeTypeKnob, + 'Has Reset': hasResetKnob, + if (hasResetKnob.value) 'Reset Value': resetValueKnob, + }); + + @override + final String name = 'Edge Detector'; +} diff --git a/lib/src/count.dart b/lib/src/count.dart index 351799520..44f0c4833 100644 --- a/lib/src/count.dart +++ b/lib/src/count.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // count.dart @@ -6,7 +6,6 @@ // // 2023 July 11 // Author: Rahul Gautham Putcha -// import 'dart:math'; diff --git a/lib/src/edge_detector.dart b/lib/src/edge_detector.dart new file mode 100644 index 000000000..7c8fc7878 --- /dev/null +++ b/lib/src/edge_detector.dart @@ -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 + +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(); + } +} diff --git a/test/arbiter_test.dart b/test/arbiter_test.dart index 02d1a6fcf..23d3b80b0 100644 --- a/test/arbiter_test.dart +++ b/test/arbiter_test.dart @@ -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++) { diff --git a/test/edge_detector_test.dart b/test/edge_detector_test.dart new file mode 100644 index 000000000..4a48b80a5 --- /dev/null +++ b/test/edge_detector_test.dart @@ -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 + +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 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())); + }); + + test('exception: reset value without reset', () { + expect(() => EdgeDetector(Logic(), clk: Logic(), resetValue: 5), + throwsA(isA())); + }); +} diff --git a/test/fifo_test.dart b/test/fifo_test.dart index 0e09548c9..57fc80e19 100644 --- a/test/fifo_test.dart +++ b/test/fifo_test.dart @@ -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); diff --git a/test/memory_test.dart b/test/memory_test.dart index 7d4822666..5e3318312 100644 --- a/test/memory_test.dart +++ b/test/memory_test.dart @@ -185,8 +185,6 @@ void main() { await mem.build(); - WaveDumper(mem); - wrPorts = wrPorts.map((oldWrPort) { final newWrPort = MaskedDataPortInterface(dataWidth, addrWidth) ..en.put(0);