diff --git a/lib/src/linear_feedback_shift_register.dart b/lib/src/linear_feedback_shift_register.dart new file mode 100644 index 00000000..904a6478 --- /dev/null +++ b/lib/src/linear_feedback_shift_register.dart @@ -0,0 +1,114 @@ +// Copyright (C) 2023 Intel Corporation + +// SPDX-License-Identifier: BSD-3-Clause + +// + +// linear_feedback_shift_register.dart + +// Implementation of Galois Linear Feedback Shift Register. + +// + +// 2024 October 1 + +// Author: Omonefe Itietie + +// + +import 'dart:collection'; +import 'package:rohd/rohd.dart'; + +/// Galois Linear Feedback Shift Register +class LinearFeedbackShiftRegister extends Module { + /// Contains polynomial size for LFSR + final int width; + + /// Contains seed of LFSR (starting value) + Logic state; + + /// Data names for signals + final String dataName; + + /// Contains bit string that will be used to calculate the output + final Logic taps; + + /// Output for shift register + Logic get dataOut => output('${dataName}_out'); + + /// The number of stages in this shift register. + final int shifts; + + /// A [List] of [output]s where the `n`'th entry corresponds to a version of + /// the input data after passing through `n + 1` flops. + late final List stages = UnmodifiableListView( + [for (var i = 0; i < shifts; i++) output(_stageName(i))]); + + /// The name of the signal (and output pin) for the [i]th stage. + String _stageName(int i) => '${dataName}_stage_$i'; + + LinearFeedbackShiftRegister(Logic dataIn, + {required Logic clk, + required this.state, + required this.shifts, + required this.taps, + Logic? enable, + Logic? reset, + dynamic resetValue, + this.dataName = 'data'}) + : width = dataIn.width, + super(name: '${dataName}_lfsr') { + dataIn = addInput('${dataName}_in', dataIn, width: width); + clk = addInput('clk', clk); + addOutput('${dataName}_out', width: width); + + Map? resetValues; + + if (reset != null) { + reset = addInput('reset', reset); + if (resetValue != null) { + if (resetValue is Logic) { + resetValue = + addInput('resetValue', resetValue, width: resetValue.width); + } + resetValues = {}; + } + } + + var dataStage = dataIn; + var conds = []; + + // Create the LFSR logic for each shift stage + for (var i = 0; i < shifts; i++) { + final stageI = addOutput(_stageName(i), width: width); + + conds.add(stageI < dataStage); + resetValues?[stageI] = resetValue; + + Logic lsb = state.getRange(0, 1); // Get LSB (least significant bit) + state = [Const(0, width: 1), state.getRange(1, width)].swizzle(); + + If(lsb.eq(Const(1)), then: [ + state < state ^ taps // Perform XOR and assign back to state + ]); + + dataStage = stageI; + } + + // Enable logic if needed + if (enable != null) { + enable = addInput('enable', enable); + conds = [If(enable, then: conds)]; + } + + // Sequential logic block + Sequential( + clk, + reset: reset, + resetValues: resetValues, + conds, + ); + // Connect the final stage to the output + dataOut <= dataStage; + } +} diff --git a/test/linear_feedback_shift_register_test.dart b/test/linear_feedback_shift_register_test.dart new file mode 100644 index 00000000..8095866f --- /dev/null +++ b/test/linear_feedback_shift_register_test.dart @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// linear_feedback_shift_register_test.dart +// Tests for linear feedback shift register +// +// 2024 October 1 +// Author: Omonefe Itietie + +import 'dart:async'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/src/linear_feedback_shift_register.dart'; +import 'package:rohd_vf/rohd_vf.dart'; +import 'package:test/test.dart'; + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + test('Test LFSR creation', () async { + final dataIn = Logic(width: 4)..put(bin('1000')); // initial state + final clk = SimpleClockGenerator(6).clk; + final state = Logic(width: 4)..put(bin('1000')); + const shifts = 6; + final taps = Logic(width: 4)..put(bin('1010')); // tap positions + final lfsr = LinearFeedbackShiftRegister(dataIn, + clk: clk, state: state, shifts: shifts, taps: taps); + + final dataOut = lfsr.dataOut; + + await lfsr.build(); + + final expectedData = [8, 12, 6, 3, 9, 4, 2, 1]; + + unawaited(Simulator.run()); + await clk.waitCycles(5); + + for (var i = 0; i < expectedData.length; i++) { + unawaited(clk + .waitCycles(1) + .then((value) => expect(dataOut.value.toInt(), expectedData[i]))); + } + + await Simulator.endSimulation(); + }); + + test('Test LFSR naming', () async { + final lfsr = LinearFeedbackShiftRegister(Logic(), + clk: Logic(), + state: Logic(), + shifts: 2, + taps: Logic(), + dataName: 'test'); + + expect(lfsr.name, contains('test')); + expect(lfsr.dataOut.name, contains('test')); + expect( + // ignore: invalid_use_of_protected_member + lfsr.inputs.keys.where((element) => element.contains('test')).length, + 1); + }); + + test('Test LFSR with 0 initial state and 0 taps returns as 0', () async { + final dataIn = Logic(width: 4)..put(bin('0000')); // initial state + final clk = SimpleClockGenerator(6).clk; + final state = Logic(width: 4)..put(bin('0000')); + const shifts = 6; + final taps = Logic(width: 4)..put(bin('0000')); // tap positions + final lfsr = LinearFeedbackShiftRegister(dataIn, + clk: clk, state: state, shifts: shifts, taps: taps); + + final dataOut = lfsr.dataOut; + + await lfsr.build(); + + final expectedData = [0, 0, 0, 0]; + + unawaited(Simulator.run()); + await clk.waitCycles(5); + + for (var i = 0; i < expectedData.length; i++) { + unawaited(clk + .waitCycles(1) + .then((value) => expect(dataOut.value.toInt(), expectedData[i]))); + } + + await Simulator.endSimulation(); + }); + + test('Test LFSR with enable signal and reset', () async { + final dataIn = Logic(width: 4)..put(bin('1110')); + final clk = SimpleClockGenerator(6).clk; + final state = Logic(width: 4)..put(bin('1110')); + final enable = Logic(); // Create the enable signal + final reset = Logic(); // Create a reset signal + const shifts = 6; + final taps = Logic(width: 4)..put(bin('1010')); + + final lfsr = LinearFeedbackShiftRegister(dataIn, + clk: clk, + state: state, + shifts: shifts, + taps: taps, + enable: enable, + reset: reset); + + final dataOut = lfsr.dataOut; + + await lfsr.build(); + + final expectedData = [0, 0, 0, 0, 0, 14]; + + unawaited(Simulator.run()); + + // Apply reset + reset.put(1); + await clk.nextPosedge; // Wait for reset to propagate + reset.put(0); // Remove reset + + // Apply enable + enable.put(1); + + for (var i = 0; i < expectedData.length; i++) { + await clk.nextPosedge; + expect( + dataOut.value.toInt(), expectedData[i]); // Check output when enabled + } + + await Simulator.endSimulation(); + }); + + test('Test LFSR with reset value', () async { + final dataIn = Logic(width: 4)..put(bin('1010')); + final clk = SimpleClockGenerator(6).clk; + final state = Logic(width: 4)..put(bin('1010')); + final reset = Logic(); // Create a reset signal + const shifts = 6; + final taps = Logic(width: 4)..put(bin('1000')); + + final lfsr = LinearFeedbackShiftRegister(dataIn, + clk: clk, state: state, shifts: shifts, taps: taps, reset: reset); + + final dataOut = lfsr.dataOut; + + await lfsr.build(); + + final expectedData = [0, 0, 0, 0, 0, 0, 0]; + + unawaited(Simulator.run()); + + // Apply reset + reset.put(1); + await clk.nextPosedge; // Wait for reset to propagate + + for (var i = 0; i < expectedData.length; i++) { + await clk.nextPosedge; + expect( + dataOut.value.toInt(), expectedData[i]); // Check output when enabled + } + + await Simulator.endSimulation(); + }); + + test('Test Enabled LFSR', () async { + final dataIn = Logic(width: 4)..put(bin('1110')); + final clk = SimpleClockGenerator(6).clk; + final state = Logic(width: 4)..put(bin('1110')); + final enable = Logic(); // Create the enable signal + const shifts = 6; + final taps = Logic(width: 4)..put(bin('1110')); + + final lfsr = LinearFeedbackShiftRegister(dataIn, + clk: clk, state: state, shifts: shifts, taps: taps, enable: enable); + + final dataOut = lfsr.dataOut; + + await lfsr.build(); + + final expectedData = [14, 7, 12, 6, 3]; + + unawaited(Simulator.run()); + + // Apply enable + enable.put(1); + await clk.waitCycles(5); + + for (var i = 0; i < expectedData.length; i++) { + unawaited(clk + .waitCycles(1) + .then((value) => expect(dataOut.value.toInt(), expectedData[i]))); + } + + await Simulator.endSimulation(); + }); +}