diff --git a/doc/README.md b/doc/README.md index 47d944b68..5c38d3ff6 100644 --- a/doc/README.md +++ b/doc/README.md @@ -30,7 +30,8 @@ Some in-development items will have opened issues, as well. Feel free to create - [Leading Digit Anticipate](./components/leading_digit_anticipate.md) - Count - [Count bit occurrence](./components/count.md) - - Count pattern occurrence + - [Count pattern occurrence](./components/count_pattern.md) + - Detection - [Edge detection](./components/edge_detector.md) - Sort diff --git a/doc/components/count_pattern.md b/doc/components/count_pattern.md new file mode 100644 index 000000000..274034861 --- /dev/null +++ b/doc/components/count_pattern.md @@ -0,0 +1,49 @@ +# Count + +A CountPattern will count the number of occurrence of a fixed-width `pattern` within a given Logic `bus`. + +It takes a Binary Logic `bus` and counts the occurrences of a fixed-width `pattern` and outputs it as `count`. `pattern` refers to the pattern to be counted in the bus. Ensure that the `pattern` width is smaller than the `bus` width. In addition, with `fromStart` which is set as `true` by default to count the pattern from the start of the `bus`. An `error` flag is added to indicate when `pattern` is not found in the `bus`. + +## Implementation + +To find the number of occurence `count` of a fixed-width `pattern` in a `bus`. The search direction is defined by `fromStart` argument. If `pattern` is not found in the `bus`, the `count` will be set to 0 and `error` will be set to 1. If `pattern` matches, it will be added to `count`. + +## Count Pattern from Start (Scenario 1: Present Once) + +For example, if `bus` is `00111110` and `pattern` is `110`, the output `count` will return `1` as the pattern occurred once in the bus from LSB. + +```dart +final bus = Const(bin('00111110'), width: 8); +final pattern = Const(bin('110'), width: 3); +final countPattern = CountPattern(bus, pattern); + ``` + +## Count Pattern from Start (Scenario 2: Present More Than Once) + +For example, if `bus` is `00110110` and `pattern` is `01`, the output `count` will return `2` as the pattern occurred twice in the bus from LSB. + +```dart +final bus = Const(bin('00110110'), width: 8); +final pattern = Const(bin('01'), width: 2); +final countPattern = CountPattern(bus, pattern); + ``` + +## Count Pattern from Start (Scenario 3: Not Present) + +For example, if `bus` is `00010000` and `pattern` is `110`, the output `count` will return `0` and an error will be generated as the pattern does not exist in the bus. + +```dart +final bus = Const(bin('00010000'), width: 8); +final pattern = Const(bin('110'), width: 3); +final countPattern = CountPattern(bus, pattern); + ``` + +## Count Pattern from End + +For example, if `bus` is `00110111` and `pattern` is `110`, the output `count` will return `1` as the pattern occurred once from the end of the bus. + +```dart +final bus = Const(bin('00110111'), width: 8); +final pattern = Const(bin('110'), width: 3); +final countPattern = CountPattern(bus, pattern, fromStart: false); + ``` \ No newline at end of file diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index ffd1c7f91..9d4bf62f1 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -7,6 +7,7 @@ export 'src/binary_gray.dart'; export 'src/clock_gating.dart'; export 'src/component_config/component_config.dart'; export 'src/count.dart'; +export 'src/count_pattern.dart'; export 'src/edge_detector.dart'; export 'src/encodings/encodings.dart'; export 'src/error_checking/error_checking.dart'; diff --git a/lib/src/count_pattern.dart b/lib/src/count_pattern.dart new file mode 100644 index 000000000..d9490416f --- /dev/null +++ b/lib/src/count_pattern.dart @@ -0,0 +1,81 @@ +// Copyright (C) 2025 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// count_pattern.dart +// Implementation of Count Pattern Functionality. +// +// 2025 June 24 +// Author: Ramli, Nurul Izziany +// + +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/utils.dart'; + +/// [CountPattern] functionality. +/// +/// Takes in a [Logic] `bus` to count occurrences of a fixed-width pattern. +/// Outputs pin `count` contains the number of occurrences of the +/// pattern in the bus. + +class CountPattern extends Module { + /// [_output] is output of CountPattern. + /// Use count for accessing from outside Module. + late Logic _output; + + /// [count] is a getter for output of CountPattern. + Logic get count => _output; + + /// If [generateError] is `true`, an error output will be generated + /// when the pattern is not found in the bus. + final bool generateError; + + /// [error] is a getter for error in CountPattern and is generated when + /// [generateError] is `true`. + /// When pattern is not found, it will result in error `1`. + Logic? get error => tryOutput('error'); + + /// Count the number of occurence of a fixed-width pattern in a bus. + /// + /// Takes in [bus] of type [Logic]. + /// [pattern] is the pattern to be counted in the bus. + /// If [fromStart] is `true`, the count starts from the beginning of the bus. + /// If [fromStart] is `false`, the count starts from the end of the bus. + CountPattern(Logic bus, Logic pattern, + {bool fromStart = true, this.generateError = false}) + : super(definitionName: 'CountPattern_W${bus.width}_P${pattern.width}') { + bus = addInput('bus', bus, width: bus.width); + pattern = addInput('pattern', pattern, width: pattern.width); + + // Initialize count to zero + Logic count = Const(0, width: max(1, log2Ceil(bus.width + 1))); + + for (var i = 0; i <= bus.width - pattern.width; i = i + 1) { + int minBit; + int maxBit; + if (fromStart) { + // Read from start of the bus + minBit = i; + maxBit = i + pattern.width; + } else { + // Read from end of the bus + minBit = bus.width - i - pattern.width; + maxBit = bus.width - i; + } + + // Check if pattern matches, add to count + final valCheck = bus.getRange(minBit, maxBit).eq(pattern); + count += valCheck.zeroExtend(count.width); + } + + _output = addOutput('count', width: count.width); + _output <= count; + + if (generateError) { + // If pattern is not found (count equals to 0), return error + addOutput('error'); + error! <= count.eq(0); + } + } +} diff --git a/test/count_pattern_test.dart b/test/count_pattern_test.dart new file mode 100644 index 000000000..e05a87b44 --- /dev/null +++ b/test/count_pattern_test.dart @@ -0,0 +1,126 @@ +// Copyright (C) 2025 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// count_pattern_test.dart +// Tests for Count Pattern +// +// 2025 July 6 +// Author: Louiz Ang Zhi Lin +// Co-author: Ramli, Nurul Izziany +// +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +void main() { + group('From start, count pattern when pattern is', () { + test('present once', () { + final bus = Const(bin('00111110'), width: 8); + final pattern = Const(bin('110'), width: 3); + final countPattern = CountPattern(bus, pattern, generateError: true); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(1)); + }); + test('present more than once', () { + final bus = Const(bin('00110110'), width: 8); + final pattern = Const(bin('01'), width: 2); + final countPattern = CountPattern(bus, pattern, generateError: true); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(2)); + }); + test('not present', () { + final bus = Const(bin('00010000'), width: 8); + final pattern = Const(bin('110'), width: 3); + final countPattern = CountPattern(bus, pattern, generateError: true); + expect(countPattern.error!.value.toInt(), equals(1)); + expect(countPattern.count.value.toInt(), equals(0)); + }); + }); + group('From end, count pattern when pattern is', () { + test('present', () { + final bus = Const(bin('00110111'), width: 8); + final pattern = Const(bin('110'), width: 3); + final countPattern = + CountPattern(bus, pattern, fromStart: false, generateError: true); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(1)); + }); + test('present more than once', () { + final bus = Const(bin('11011011'), width: 8); + final pattern = Const(bin('10'), width: 2); + final countPattern = + CountPattern(bus, pattern, fromStart: false, generateError: true); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(2)); + }); + test('not present', () { + final bus = Const(bin('101010101'), width: 8); + final pattern = Const(bin('111'), width: 3); + final countPattern = + CountPattern(bus, pattern, fromStart: false, generateError: true); + expect(countPattern.error!.value.toInt(), equals(1)); + expect(countPattern.count.value.toInt(), equals(0)); + }); + }); + group('Dynamic input, from start, count pattern when pattern is', () { + test('present once', () { + final bus = Logic(width: 8); + final pattern = Logic(width: 2); + final countPattern = CountPattern(bus, pattern, generateError: true); + bus.put(bin('00111110')); + pattern.put(bin('01')); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(1)); + }); + test('present more than once', () { + final bus = Logic(width: 8); + final pattern = Logic(width: 2); + final countPattern = CountPattern(bus, pattern, generateError: true); + bus.put(bin('11011111')); + pattern.put(bin('11')); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(5)); + }); + test('not present', () { + final bus = Logic(width: 8); + final pattern = Logic(width: 2); + final countPattern = CountPattern(bus, pattern, generateError: true); + bus.put(bin('11110111')); + pattern.put(bin('00')); + expect(countPattern.error!.value.toInt(), equals(1)); + expect(countPattern.count.value.toInt(), equals(0)); + }); + }); + group('Dynamic input, from end, count pattern when pattern is', () { + test('present once', () { + final bus = Logic(width: 8); + final pattern = Logic(width: 2); + final countPattern = + CountPattern(bus, pattern, fromStart: false, generateError: true); + bus.put(bin('10000001')); + pattern.put(bin('01')); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(1)); + }); + test('present more than once', () { + final bus = Logic(width: 8); + final pattern = Logic(width: 2); + final countPattern = + CountPattern(bus, pattern, fromStart: false, generateError: true); + bus.put(bin('11011101')); + pattern.put(bin('10')); + expect(countPattern.error!.value.toInt(), equals(0)); + expect(countPattern.count.value.toInt(), equals(2)); + }); + test('not present', () { + final bus = Logic(width: 8); + final pattern = Logic(width: 2); + final countPattern = + CountPattern(bus, pattern, fromStart: false, generateError: true); + bus.put(bin('11110111')); + pattern.put(bin('00')); + expect(countPattern.error!.value.toInt(), equals(1)); + expect(countPattern.count.value.toInt(), equals(0)); + }); + }); +}