diff --git a/doc/README.md b/doc/README.md index 73d05ead..cc31d3a8 100644 --- a/doc/README.md +++ b/doc/README.md @@ -56,9 +56,10 @@ Some in-development items will have opened issues, as well. Feel free to create - Pseudorandom - LFSR - Error checking & correction - - ECC + - [ECC](./components/ecc.md) - CRC - [Parity](./components/parity.md) + - Interleaving - Data flow - Ready/Valid - Connect/Disconnect diff --git a/doc/components/ecc.md b/doc/components/ecc.md new file mode 100644 index 00000000..9c2b220f --- /dev/null +++ b/doc/components/ecc.md @@ -0,0 +1,14 @@ +# Error Correcting Codes (ECC) + +ROHD-HCL implements [Hamming codes](https://en.wikipedia.org/wiki/Hamming_code) for error correction and detection. The Wikipedia article does a good job explaining the background and functionality of Hamming codes, for those unfamiliar. + +An error correcting code is a code that can be included in a transmission with some data that enables the receiver to check whether there was an error (up to some number of bit flips), and possibly correct the error (up to a limit). There is a trade-off where increasing the number of additional bits included in the code improves error checking/correction, but costs more bits (area/power/storage). + +ROHD-HCL only has Hamming codes currently, but there are many types of error correcting codes. The `HammingEccTransmitter` and `HammingEccReceiver` can support any data width, and support the following configurations: + +| Type | Errors detectable | Errors correctable | Extra parity bit | +|------|-------------------|--------------------|------------------| +| Single Error Correction (SEC) | 1 | 1 | No | +| Double Error Detection (SEDDED) | 2 | 0 | No | +| Single Error Correction, Double Error Detection (SECDED) | 2 | 1 | Yes | +| Triple Error Detection (SEDDEDTED) | 3 | 0 | Yes | diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 37ab1158..2afd3387 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -7,6 +7,7 @@ export 'src/carry_save_mutiplier.dart'; export 'src/component_config/component_config.dart'; export 'src/count.dart'; export 'src/encodings/encodings.dart'; +export 'src/error_checking/error_checking.dart'; export 'src/exceptions.dart'; export 'src/fifo.dart'; export 'src/find.dart'; @@ -14,7 +15,6 @@ export 'src/interfaces/interfaces.dart'; export 'src/memory/memories.dart'; export 'src/models/models.dart'; export 'src/multiplier.dart'; -export 'src/parity.dart'; export 'src/ripple_carry_adder.dart'; export 'src/rotate.dart'; export 'src/shift_register.dart'; diff --git a/lib/src/component_config/components/component_registry.dart b/lib/src/component_config/components/component_registry.dart index eb021f1e..5068b471 100644 --- a/lib/src/component_config/components/component_registry.dart +++ b/lib/src/component_config/components/component_registry.dart @@ -13,8 +13,9 @@ import 'package:rohd_hcl/rohd_hcl.dart'; List get componentRegistry => [ RotateConfigurator(), FifoConfigurator(), - PriorityArbiterConfigurator(), + EccConfigurator(), RoundRobinArbiterConfigurator(), + PriorityArbiterConfigurator(), RippleCarryAdderConfigurator(), CarrySaveMultiplierConfigurator(), BitonicSortConfigurator(), diff --git a/lib/src/component_config/components/components.dart b/lib/src/component_config/components/components.dart index 7804d474..6788b841 100644 --- a/lib/src/component_config/components/components.dart +++ b/lib/src/component_config/components/components.dart @@ -2,6 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause export 'config_carry_save_multiplier.dart'; +export 'config_ecc.dart'; export 'config_fifo.dart'; export 'config_one_hot.dart'; export 'config_priority_arbiter.dart'; diff --git a/lib/src/component_config/components/config_ecc.dart b/lib/src/component_config/components/config_ecc.dart new file mode 100644 index 00000000..8f1cd30e --- /dev/null +++ b/lib/src/component_config/components/config_ecc.dart @@ -0,0 +1,39 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// config_ecc.dart +// Configurator for ECC. +// +// 2024 January 18 +// Author: Max Korbel + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// A [Configurator] for [HammingEccReceiver] and [HammingEccTransmitter]. +class EccConfigurator extends Configurator { + /// A knob controlling the [HammingType]. + final ChoiceConfigKnob typeKnob = + ChoiceConfigKnob(HammingType.values, value: HammingType.sec); + + /// A knob controlling the data width. + final IntConfigKnob dataWidthKnob = IntConfigKnob(value: 4); + + @override + Module createModule() => HammingEccReceiver( + HammingEccTransmitter( + Logic(width: dataWidthKnob.value), + hammingType: typeKnob.value, + ).transmission, + hammingType: typeKnob.value, + ); + + @override + late final Map> knobs = { + 'Data Width': dataWidthKnob, + 'Hamming Type': typeKnob, + }; + + @override + final String name = 'ECC'; +} diff --git a/lib/src/error_checking/ecc.dart b/lib/src/error_checking/ecc.dart new file mode 100644 index 00000000..6023bdab --- /dev/null +++ b/lib/src/error_checking/ecc.dart @@ -0,0 +1,239 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// ecc.dart +// Error correcting code hardware generators. +// +// 2024 January 18 +// Author: Max Korbel + +import 'dart:math'; + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Type of Hamming code, with different characteristics for error correction, +/// error detection, and number of check bits required. +enum HammingType { + /// Single error correction (SEC), but cannot detect double bit errors. + sec._(hasExtraParityBit: false, hasCorrection: true), + + /// Double error detection (DED): can detect up to double-bit errors, but + /// performs no correction. + sedded._(hasExtraParityBit: false, hasCorrection: false), + + /// Single error correction, double error detection (SECDED). + secded._(hasExtraParityBit: true, hasCorrection: true), + + /// Triple error detection (TED), can detect up to triple-bit errors, but + /// performs no correction. + seddedted._(hasExtraParityBit: true, hasCorrection: false); + + /// Indicates whether this type requires an additional parity bit. + final bool hasExtraParityBit; + + /// Indicates whether this type supports correction of errors. + final bool hasCorrection; + + /// Constrcut a [HammingType] with given characteristics. + const HammingType._( + {required this.hasExtraParityBit, required this.hasCorrection}); + + /// The number of extra parity bits required for this type. + int get _extraParityBits => hasExtraParityBit ? 1 : 0; +} + +/// Returns whether [n] is a power of two. +bool _isPowerOfTwo(int n) => n != 0 && (n & (n - 1) == 0); + +/// A transmitter for data which generates a Hamming code for error detection +/// and possibly correction. +class HammingEccTransmitter extends ErrorCheckingTransmitter { + /// The type of Hamming code to use. + final HammingType hammingType; + + /// Creates a [transmission] which includes a [code] that protects [data] with + /// the specified [hammingType]. + HammingEccTransmitter(super.data, + {super.name = 'hamming_ecc_tx', this.hammingType = HammingType.sec}) + : super( + definitionName: 'hamming_ecc_transmitter_${hammingType.name}', + codeWidth: + _parityBitsRequired(data.width) + hammingType._extraParityBits); + + /// Calculates the number of parity bits required for a Hamming code to + /// protect [dataWidth] bits. + static int _parityBitsRequired(int dataWidth) { + var m = 0; + double k; + do { + m++; + k = pow(2, m) - m - 1; + } while (k < dataWidth); + return m; + } + + @override + @protected + Logic calculateCode() { + final parityBits = List.generate(code.width, (index) => null); + final dataBits = List.generate( + data.width, (index) => Logic(name: 'd${index + 1}')..gets(data[index])); + + final hammingCodeWidth = code.width - hammingType._extraParityBits; + + var dataIdx = 0; + for (var i = 1; + i <= transmission.width - hammingType._extraParityBits; + i++) { + if (!_isPowerOfTwo(i)) { + final ilv = LogicValue.ofInt(i, hammingCodeWidth); + + for (var p = 0; p < hammingCodeWidth; p++) { + if (ilv[p].toBool()) { + if (parityBits[p] == null) { + parityBits[p] = dataBits[dataIdx]; + } else { + parityBits[p] = parityBits[p]! ^ dataBits[dataIdx]; + } + } + } + dataIdx++; + } + } + + var calculatedCode = [ + for (var i = 0; i < hammingCodeWidth; i++) + Logic(name: 'p${1 << i}')..gets(parityBits[i]!), + ].rswizzle(); + + if (hammingType.hasExtraParityBit) { + // extra parity bit is calculated by calculating parity across entire rest + // of the transmission + final pExtra = Logic(name: 'pExtra') + ..gets(ParityTransmitter([calculatedCode, data].swizzle()).code); + calculatedCode = [pExtra, calculatedCode].swizzle(); + } + + return calculatedCode; + } +} + +/// A receiver for transmissions sent with a Hamming code for error detection +/// and possibly correction. +class HammingEccReceiver extends ErrorCheckingReceiver { + /// The type of Hamming code to use to understand the original [transmission]. + final HammingType hammingType; + + /// Consumes a [transmission] which includes a [code] that can check whether + /// the [originalData] contains errors and possibly correct it to + /// [correctedData], depending on the specified [hammingType]. + HammingEccReceiver(super.transmission, + {super.name = 'hamming_ecc_rx', this.hammingType = HammingType.sec}) + : super( + codeWidth: _codeWidthFromTransmissionWidth( + transmission.width - hammingType._extraParityBits) + + hammingType._extraParityBits, + definitionName: 'hamming_ecc_receiver_${hammingType.name}', + supportsErrorCorrection: hammingType.hasCorrection, + ) { + final tx = HammingEccTransmitter(originalData, hammingType: hammingType); + final hammingCode = + hammingType.hasExtraParityBit ? code.getRange(0, -1) : code; + final expectedHammingCode = + tx.hammingType.hasExtraParityBit ? tx.code.getRange(0, -1) : tx.code; + + _syndrome <= hammingCode ^ expectedHammingCode; + final hammingError = _syndrome.or(); + + final hammingTransmissionWidth = + transmission.width - hammingType._extraParityBits; + + if (hammingType.hasCorrection) { + final correction = + Logic(name: 'correction', width: hammingTransmissionWidth) + ..gets( + (Const(1, width: hammingTransmissionWidth + 1) << _syndrome) + .getRange(1), + ); + + final encodingToDataMap = _encodingToData(); + + _correctedData <= + [ + for (var i = 1; i <= hammingTransmissionWidth; i++) + if (encodingToDataMap.containsKey(i)) + Logic(name: 'd${encodingToDataMap[i]! + 1}') + ..gets(originalData[encodingToDataMap[i]] ^ correction[i - 1]) + ].rswizzle(); + } + + Logic? extraErr; + if (hammingType.hasExtraParityBit) { + extraErr = ParityReceiver(transmission).uncorrectableError; + } + + switch (hammingType) { + case HammingType.sec: + _correctableError <= hammingError; + _uncorrectableError <= Const(0); + case HammingType.sedded: + _correctableError <= Const(0); + _uncorrectableError <= hammingError; + case HammingType.secded: + // error location(s) -> meaning + // ---------------- | ---------------- + // extra & !hamming -> bit flip on extra parity, hamming can ignore + // correctable single-bit + // extra & hamming -> error on hamming region occurred, + // correctable single-bit + // !extra & !hamming -> no error + // !extra & hamming -> extra parity has no error, but hamming does + // double-bit error, uncorrectable + _correctableError <= extraErr!; + _uncorrectableError <= ~extraErr & hammingError; + case HammingType.seddedted: + _correctableError <= Const(0); + _uncorrectableError <= extraErr! | hammingError; + } + } + + /// The "syndrome" used to decode an error pattern in Hamming parity bits + /// into a correction pattern. + late final Logic _syndrome = + Logic(name: 'syndrome', width: code.width - hammingType._extraParityBits); + + /// The number of Hamming code bits that must have been included in a + /// transmission of [transmissionWidth], not including any extra parity bit. + static int _codeWidthFromTransmissionWidth(int transmissionWidth) => + log2Ceil(transmissionWidth + 1); + + /// Builds a mapping from Hamming bits encoding to data position (1-indexed). + Map _encodingToData() { + final mapping = {}; + var dataIdx = 0; + for (var encodedIdx = 1; encodedIdx <= transmission.width; encodedIdx++) { + if (!_isPowerOfTwo(encodedIdx)) { + mapping[encodedIdx] = dataIdx++; + } + } + return mapping; + } + + @override + @protected + Logic calculateCorrectableError() => _correctableError; + late final Logic _correctableError = Logic(); + + @override + @protected + Logic? calculateCorrectedData() => + hammingType.hasCorrection ? _correctedData : null; + late final Logic _correctedData = Logic(width: originalData.width); + + @override + @protected + Logic calculateUncorrectableError() => _uncorrectableError; + late final Logic _uncorrectableError = Logic(); +} diff --git a/lib/src/error_checking/error_checker.dart b/lib/src/error_checking/error_checker.dart new file mode 100644 index 00000000..ea1a5d62 --- /dev/null +++ b/lib/src/error_checking/error_checker.dart @@ -0,0 +1,126 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// error_checker.dart +// Abstract definitions for error checking transmitters and receivers. +// +// 2024 January 18 + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; + +/// A transmitter for data which includes a [code] for checking and possibly +/// correcting data at the receiving end. +abstract class ErrorCheckingTransmitter extends Module { + /// The code that in addition to original data that enables error detection. + late final Logic code = output('code'); + + /// Generates the code to be provided by the transmitter, to be implemented by + /// implementations. + @protected + Logic calculateCode(); + + /// The full bus to transmit including the original data and the [code]. + Logic get transmission => output('transmission'); + + /// The input port data provied to this transmitter. + /// + /// Should only be used by implementations of transmitters internally. + @protected + late final Logic data; + + /// Creates a transmitter for [data]. + ErrorCheckingTransmitter(Logic data, + {required int codeWidth, required super.name, super.definitionName}) { + this.data = addInput('data', data, width: data.width); + + addOutput('code', width: codeWidth); + addOutput('transmission', width: codeWidth + data.width) <= + [code, this.data].swizzle(); + + code <= calculateCode(); + } +} + +/// A receiver for data which includes a [code] for checking and possibly +/// correcting. +abstract class ErrorCheckingReceiver extends Module { + /// Whether or not there was an error (correctable or uncorrectable). + Logic get error => output('error'); + + /// Whether there was a correctable error. + late final Logic correctableError = output('correctable_error'); + + /// Implementation to calculate whether something is a [correctableError]. + @protected + Logic calculateCorrectableError(); + + /// Whether there was an uncorrectable error. + late final Logic uncorrectableError = output('uncorrectable_error'); + + /// Implementation to calculate whether something is an [uncorrectableError]. + @protected + Logic calculateUncorrectableError(); + + /// The code included in the transmitted bus which is used to check for and + /// handle errors. + Logic get code => output('code'); + + /// The original data before any correction. + Logic get originalData => output('original_data'); + + /// The [originalData] with any possible corrections applied. + /// + /// If [uncorrectableError], then this data is unreliable. If this does not + /// [supportsErrorCorrection], then this will be `null` and the port won't + /// exist. + late final Logic? correctedData = + supportsErrorCorrection ? output('corrected_data') : null; + + /// Implementation to calculate corrected data, if supported. + /// + /// Returns null if not [supportsErrorCorrection]. + @protected + Logic? calculateCorrectedData(); + + /// The input port bus provied to this receiver. + /// + /// Should only be used by implementations of receivers internally. + @protected + late final Logic transmission; + + /// Whether or not data correction is supported. + final bool supportsErrorCorrection; + + /// Creates a receiver for [transmission]. + ErrorCheckingReceiver(Logic transmission, + {required int codeWidth, + required this.supportsErrorCorrection, + required super.name, + super.definitionName}) + : assert(codeWidth > 0, 'Must provide non-empty code.') { + this.transmission = + addInput('transmission', transmission, width: transmission.width); + + addOutput('code', width: codeWidth) <= + this.transmission.slice(-1, -codeWidth); + addOutput('original_data', width: transmission.width - codeWidth) <= + this.transmission.slice(-1 - codeWidth, 0); + + if (supportsErrorCorrection) { + addOutput('corrected_data', width: originalData.width); + } + + addOutput('correctable_error'); + addOutput('uncorrectable_error'); + addOutput('error'); + + correctableError <= calculateCorrectableError(); + uncorrectableError <= calculateUncorrectableError(); + error <= correctableError | uncorrectableError; + + if (supportsErrorCorrection) { + correctedData! <= calculateCorrectedData()!; + } + } +} diff --git a/lib/src/error_checking/error_checking.dart b/lib/src/error_checking/error_checking.dart new file mode 100644 index 00000000..8d9f3172 --- /dev/null +++ b/lib/src/error_checking/error_checking.dart @@ -0,0 +1,6 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause + +export 'ecc.dart'; +export 'error_checker.dart'; +export 'parity.dart'; diff --git a/lib/src/error_checking/parity.dart b/lib/src/error_checking/parity.dart new file mode 100644 index 00000000..7294c917 --- /dev/null +++ b/lib/src/error_checking/parity.dart @@ -0,0 +1,62 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// parity.dart +// Implementation of Parity modules. +// +// 2023 August 20 +// Author: Rahul Gautham Putcha + +import 'package:meta/meta.dart'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Encode data to transport with Parity bits +class ParityTransmitter extends ErrorCheckingTransmitter { + /// The [parity] bit computed for the provided data. + @Deprecated('Use `code` instead.') + Logic get parity => code; + + /// Creates a transmitter that sends data with a parity bit. + ParityTransmitter(super.data, {super.name = 'parity_tx'}) + : super(codeWidth: 1); + + @override + @protected + Logic calculateCode() => data.xor(); +} + +/// Check for error & Receive data on transmitted data via parity +class ParityReceiver extends ErrorCheckingReceiver { + /// Constructs a [Module] which checks data that has been transmitted with + /// correct parity. This will split the transmitted data in [transmission] + /// into 2 parts: the [originalData], and the error bit upon which [error] is + /// calculated for parity error checking. + ParityReceiver(super.transmission, {super.name = 'parity_rx'}) + : super(codeWidth: 1, supportsErrorCorrection: false); + + @override + @protected + Logic calculateCorrectableError() => Const(0); + + @override + @protected + Logic? calculateCorrectedData() => null; + + @override + @protected + Logic calculateUncorrectableError() => ~originalData.xor().eq(code); + + /// [checkError] is an getter for parity result with `0` for success and `1` + /// for fail + @Deprecated('Use `error` or `uncorrectableError` instead.') + Logic get checkError => error; + + /// The original [data] (without [parity]). + @Deprecated('Use `originalData` instead.') + Logic get data => originalData; + + /// [parity] is an getter for parity Bit received upon data transmission + @Deprecated('Use `code` instead.') + Logic get parity => code; +} diff --git a/lib/src/parity.dart b/lib/src/parity.dart deleted file mode 100644 index 159ff050..00000000 --- a/lib/src/parity.dart +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2023 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// parity.dart -// Implementation of Parity modules. -// -// 2023 August 20 -// Author: Rahul Gautham Putcha -// - -import 'package:rohd/rohd.dart'; - -/// Encode data to transport with Parity bits -class ParityTransmitter extends Module { - /// The [data] including the [parity] bit (as the most significant bit). - Logic get data => _output; - late final Logic _output; - - /// The [parity] bit computed for the provided data. - Logic get parity => output('parity'); - - /// Construct a [Module] for generating transmit data [data]. Combines given - /// [Logic] named [bus] with a [parity] bit for error check after - /// transmission. - ParityTransmitter(Logic bus) { - bus = addInput('bus', bus, width: bus.width); - addOutput('parity'); - parity <= bus.xor(); - final transmitData = [parity, bus].swizzle(); - _output = addOutput('transmitData', width: transmitData.width); - _output <= transmitData; - } -} - -/// Check for error & Receive data on transmitted data via parity -class ParityReceiver extends Module { - /// [checkError] is an getter for parity result with `0` for success and `1` - /// for fail - Logic get checkError => _checkError; - late Logic _checkError; - - /// The original [data] (without [parity]). - Logic get data => _data; - late Logic _data; - - /// [parity] is an getter for parity Bit received upon data transmission - Logic get parity => _parity; - late Logic _parity; - - /// Constructs a [Module] which checks data that has been transmitted with - /// parity. This will split the transmitted data in [bus] into 2 parts: the - /// original [data], and the error bit upon which [checkError] is calculated - /// for parity error checking. - ParityReceiver(Logic bus) { - bus = addInput('bus', bus, width: bus.width); - - // Slice from 1 from least significant bit to the end - final transmittedData = bus.slice(-2, 0); - final parityBit = bus[-1]; - final parityError = ~transmittedData.xor().eq(parityBit); - - _data = addOutput('transmittedData', width: transmittedData.width); - _parity = addOutput('parity', width: parityBit.width); - _checkError = addOutput('checkError', width: parityError.width); - - _data <= transmittedData; - _parity <= parityBit; - _checkError <= parityError; - } -} diff --git a/test/configurator_test.dart b/test/configurator_test.dart index 3e403f91..3fed1dba 100644 --- a/test/configurator_test.dart +++ b/test/configurator_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // configurator_test.dart @@ -249,4 +249,12 @@ void main() { expect(sv, contains('if((toSort1 < toSort3)) begin')); }); }); + + test('hamming ecc configurator', () async { + final cfg = EccConfigurator(); + cfg.typeKnob.value = HammingType.secded; + cfg.dataWidthKnob.value = 11; + final sv = await cfg.generateSV(); + expect(sv, contains('input logic [15:0] transmission')); + }); } diff --git a/test/ecc_test.dart b/test/ecc_test.dart new file mode 100644 index 00000000..88ba684f --- /dev/null +++ b/test/ecc_test.dart @@ -0,0 +1,105 @@ +// Copyright (C) 2023-2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// ecc_test.dart +// Tests for error correcting code hardware generators. +// +// 2024 January 18 +// Author: Max Korbel + +import 'dart:math'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/error_checking/ecc.dart'; +import 'package:test/test.dart'; + +void main() { + for (final hammingType in HammingType.values) { + test('$hammingType tx to rx', () async { + final rand = Random(123); + + final canCorrectSingleBit = hammingType.hasCorrection; + final canDetectDoubleBit = hammingType != HammingType.sec; + final canDetectTripleBit = hammingType == HammingType.seddedted; + + for (var dataWidth = 1; dataWidth < 20; dataWidth++) { + final inputData = Logic(width: dataWidth) + ..put(rand.nextLogicValue(width: dataWidth)); + + final tx = HammingEccTransmitter(inputData, hammingType: hammingType); + final sentTransmission = tx.transmission; + + final errorInjectionVector = Logic(width: sentTransmission.width); + final receivedTransmission = sentTransmission ^ errorInjectionVector; + + final rx = + HammingEccReceiver(receivedTransmission, hammingType: hammingType); + + await rx.build(); + + // test no error + errorInjectionVector.put(0); + expect(rx.uncorrectableError.value.toBool(), isFalse); + expect(rx.correctableError.value.toBool(), isFalse); + expect(rx.correctedData?.value, + canCorrectSingleBit ? inputData.value : null); + expect(rx.originalData.value, inputData.value); + + // test every 1-bit flip + for (var i = 0; i < sentTransmission.width; i++) { + inputData.put(rand.nextLogicValue(width: dataWidth)); + errorInjectionVector.put(BigInt.one << i); + + expect(rx.uncorrectableError.value.toBool(), !canCorrectSingleBit); + expect(rx.correctableError.value.toBool(), canCorrectSingleBit); + expect(rx.correctedData?.value, + canCorrectSingleBit ? inputData.value : null); + expect(rx.originalData.value, + receivedTransmission.value.getRange(0, inputData.width)); + } + + if (canDetectDoubleBit) { + // test every 2-bit flip + for (var i = 0; i < sentTransmission.width; i++) { + for (var j = i + 1; j < sentTransmission.width; j++) { + inputData.put(rand.nextLogicValue(width: dataWidth)); + errorInjectionVector.put((BigInt.one << i) | (BigInt.one << j)); + + expect(rx.uncorrectableError.value.toBool(), isTrue); + expect(rx.correctableError.value.toBool(), isFalse); + // don't care what corrected data is... + expect(rx.originalData.value, + receivedTransmission.value.getRange(0, inputData.width)); + } + } + } + + if (canDetectTripleBit) { + // test every 3-bit flip + for (var i = 0; i < sentTransmission.width; i++) { + for (var j = i + 1; j < sentTransmission.width; j++) { + for (var k = j + 1; k < sentTransmission.width; k++) { + inputData.put(rand.nextLogicValue(width: dataWidth)); + errorInjectionVector.put( + (BigInt.one << i) | (BigInt.one << j) | (BigInt.one << k)); + + expect(rx.uncorrectableError.value.toBool(), isTrue); + expect(rx.correctableError.value.toBool(), isFalse); + // don't care what corrected data is... + expect(rx.originalData.value, + receivedTransmission.value.getRange(0, inputData.width)); + } + } + } + } + } + }); + } + + test('Hamming(7,4)', () async { + final tx = HammingEccTransmitter(Logic(width: 4)); + final rx = HammingEccReceiver(tx.transmission); + expect(tx.transmission.width, 7); + expect(rx.code.width, 3); + }); +} diff --git a/test/parity_test.dart b/test/parity_test.dart index 7e2860cd..f9848617 100644 --- a/test/parity_test.dart +++ b/test/parity_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // arbiter_test.dart @@ -10,7 +10,6 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_hcl/src/parity.dart'; import 'package:test/test.dart'; void main() { @@ -22,22 +21,28 @@ void main() { final parityTransmitter = ParityTransmitter(vector); vector.put(bin('00000000')); - expect(parityTransmitter.data.value, LogicValue.ofString('000000000')); + expect( + parityTransmitter.transmission.value, LogicValue.ofString('000000000')); vector.put(bin('00000001')); - expect(parityTransmitter.data.value, LogicValue.ofString('100000001')); + expect( + parityTransmitter.transmission.value, LogicValue.ofString('100000001')); vector.put(bin('10000001')); - expect(parityTransmitter.data.value, LogicValue.ofString('010000001')); + expect( + parityTransmitter.transmission.value, LogicValue.ofString('010000001')); vector.put(bin('10001001')); - expect(parityTransmitter.data.value, LogicValue.ofString('110001001')); + expect( + parityTransmitter.transmission.value, LogicValue.ofString('110001001')); vector.put(bin('11111101')); - expect(parityTransmitter.data.value, LogicValue.ofString('111111101')); + expect( + parityTransmitter.transmission.value, LogicValue.ofString('111111101')); vector.put(bin('11111111')); - expect(parityTransmitter.data.value, LogicValue.ofString('011111111')); + expect( + parityTransmitter.transmission.value, LogicValue.ofString('011111111')); }); test('parity receiver checking', () async { @@ -48,26 +53,26 @@ void main() { final parityReceiver = ParityReceiver(vector); vector.put(bin('000000000')); - expect(parityReceiver.data.value, LogicValue.ofString('00000000')); - expect(parityReceiver.parity.value, LogicValue.ofString('0')); - expect(parityReceiver.checkError.value, LogicValue.ofString('0')); + expect(parityReceiver.originalData.value, LogicValue.ofString('00000000')); + expect(parityReceiver.code.value, LogicValue.ofString('0')); + expect(parityReceiver.error.value, LogicValue.ofString('0')); vector.put(bin('011111111')); - expect(parityReceiver.data.value, LogicValue.ofString('11111111')); - expect(parityReceiver.parity.value, LogicValue.ofString('0')); - expect(parityReceiver.checkError.value, LogicValue.ofString('0')); + expect(parityReceiver.originalData.value, LogicValue.ofString('11111111')); + expect(parityReceiver.code.value, LogicValue.ofString('0')); + expect(parityReceiver.error.value, LogicValue.ofString('0')); vector.put(bin('111111101')); - expect(parityReceiver.data.value, LogicValue.ofString('11111101')); - expect(parityReceiver.parity.value, LogicValue.ofString('1')); - expect(parityReceiver.checkError.value, LogicValue.ofString('0')); + expect(parityReceiver.originalData.value, LogicValue.ofString('11111101')); + expect(parityReceiver.code.value, LogicValue.ofString('1')); + expect(parityReceiver.error.value, LogicValue.ofString('0')); vector.put(bin('111110101')); - expect(parityReceiver.data.value, LogicValue.ofString('11110101')); + expect(parityReceiver.originalData.value, LogicValue.ofString('11110101')); // This is set to check the incorrect parity bit on purpose - expect(parityReceiver.parity.value, LogicValue.ofString('1')); + expect(parityReceiver.code.value, LogicValue.ofString('1')); // checkError equals to `1` means the parity check fail and // have noted error in the transmitted data - expect(parityReceiver.checkError.value, LogicValue.ofString('1')); + expect(parityReceiver.error.value, LogicValue.ofString('1')); }); }