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

Adds linear feedback shift register component #108

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
114 changes: 114 additions & 0 deletions lib/src/linear_feedback_shift_register.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (C) 2023 Intel Corporation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove blank (uncommented) lines in the header?


// SPDX-License-Identifier: BSD-3-Clause

//

// linear_feedback_shift_register.dart

// Implementation of Galois Linear Feedback Shift Register.

//

// 2024 October 1

// Author: Omonefe Itietie <[email protected]>

//

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<Logic> 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a doc comment to the constructor

{required Logic clk,
required this.state,
required this.shifts,
required this.taps,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Careful using this. for inputs of a module, since we want to replace them using addInput

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<Logic, dynamic>? resetValues;

if (reset != null) {
reset = addInput('reset', reset);
if (resetValue != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the original shift register was recently upgraded to allow for a List resetValue to control each element of the register (https://github.com/intel/rohd-hcl/blob/main/lib/src/shift_register.dart). However, since the LFSR is always 1-wide, maybe a different API makes sense. The current resetValue just allows you to basically set all flops to 0 or 1. Should we allow a bitvector of width == shifts to initialize the LFSR?

if (resetValue is Logic) {
resetValue =
addInput('resetValue', resetValue, width: resetValue.width);
}
resetValues = {};
}
}

var dataStage = dataIn;
var conds = <Conditional>[];

// 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: [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This If (capitalized) creates a Conditional object which only executes under certain conditions (e.g. within a Sequential or Combinational), but the object is not included in one, so it will never take effect.

state < state ^ taps // Perform XOR and assign back to state
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If taps is a Logic, then it should be created as an input to the module. I'm not sure it's necessary to support dynamic re-muxing of the LFSR, in which case the taps could be a non-Logic (e.g. a List<bool>, or maybe some int/BigInt where the bits correspond to the polynomial

]);

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;
}
}
195 changes: 195 additions & 0 deletions test/linear_feedback_shift_register_test.dart
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>

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];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how did you determine the expected values?


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();
});
}
Loading