/*
Copyright (c) 2016-2023 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`resetall
`timescale 1ns / 1ps
`default_nettype none
/*
* Parametrizable combinatorial parallel LFSR/CRC (Linear Feedback Shift Register/Cyclic Redundancy Check)
*/
module lfsr #
(
//! Ancho del LFSR
parameter LFSR_WIDTH = 31,
//! Polinomio del LFSR
parameter LFSR_POLY = 31'h10000001, // los 31 bits son el grado del polinomio, después los términos 1 y x^y se representan por 1, en este caso: x^31+x^28+1. El término más grande del polinomio (x^31) no se incluye en este parámetro, sino que se genera automáticamente por LFSR_WIDTH
//! Configuracion del LFSR: "GALOIS", "FIBONACCI"
//Fibonacci is generally used for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators, scramblers, and descrambers while Galois is generally used for cyclic redundancy check generators and checkers.
parameter LFSR_CONFIG = "FIBONACCI", // genera secuencias binarias pseudoaleatorias
//! LFSR feed forward enable:
parameter LFSR_FEED_FORWARD = 0, // Enable this for PRBS checking and self- synchronous descrambling.
//! Reversion de bits
parameter REVERSE = 0, //Shifts MSB first by default, set REVERSE for LSB first.
//! Tamaño de los datos de entrada
parameter DATA_WIDTH = 8,
//! Estilo de implementacion: "AUTO", "LOOP", "REDUCTION"
parameter STYLE = "AUTO" // selecciona automáticamente el estilo de implementación del LFSR según el entorno de simulación o síntesis
)
(
input wire [DATA_WIDTH-1:0] data_in, //! Datos de entrada que se desplazarán a través del LFSR
input wire [LFSR_WIDTH-1:0] state_in, //! Estado actual del LFSR
output wire [DATA_WIDTH-1:0] data_out, //! Datos de salida que representan los bits desplazados fuera del LFSR
output wire [LFSR_WIDTH-1:0] state_out //! Próximo estado del LFSR
);
/*
Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR
next state computation, shifting DATA_WIDTH bits per pass through the module. Input data
is XORed with LFSR feedback path, tie data_in to zero if this is not required.
Works in two parts: statically computes a set of bit masks, then uses these bit masks to
select bits for XORing to compute the next state.
Ports:
data_in
Data bits to be shifted through the LFSR (DATA_WIDTH bits)
state_in
LFSR/CRC current state input (LFSR_WIDTH bits)
data_out
Data bits shifted out of LFSR (DATA_WIDTH bits)
state_out
LFSR/CRC next state output (LFSR_WIDTH bits)
Parameters:
LFSR_WIDTH
Specify width of LFSR/CRC register
LFSR_POLY
Specify the LFSR/CRC polynomial in hex format. For example, the polynomial
x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
would be represented as
32'h04c11db7
Note that the largest term (x^32) is suppressed. This term is generated automatically based
on LFSR_WIDTH.
LFSR_CONFIG
Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used
for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators,
scramblers, and descrambers, while Galois is generally used for cyclic redundancy check
generators and checkers.
Fibonacci style (example for 64b66b scrambler, 0x8000000001 = 1+x^39)
DIN (LSB first) (DIN = Datos de Entrada)
|
V
(+)<---------------------------(+)<-----------------------------.
| ^ |
| .----. .----. .----. | .----. .----. .----. |
+->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--'
| '----' '----' '----' '----' '----' '----'
V
DOUT (DOUT = Datos de salida)
Galois style (example for CRC16, 0x8005 = x^15+x^2+1)
,-------------------+-------------------------+----------(+)<-- DIN (MSB first)
| | | ^
| .----. .----. V .----. .----. V .----. |
`->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT
'----' '----' '----' '----' '----'
LFSR_FEED_FORWARD
Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self-
synchronous descrambling.
Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001)
DIN (LSB first)
|
| .----. .----. .----. .----. .----. .----.
+->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--.
| '----' '----' '----' | '----' '----' '----' |
| V |
(+)<---------------------------(+)------------------------------'
|
V
DOUT
Galois feed-forward style
,-------------------+-------------------------+------------+--- DIN (MSB first)
| | | |
| .----. .----. V .----. .----. V .----. V
`->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT
'----' '----' '----' '----' '----'
REVERSE
Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first.
DATA_WIDTH
Specify width of input and output data bus. The module will perform one shift per input
data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH
to the required number of shifts per clock cycle.
STYLE
Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO"
is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate
directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate
and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog
reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction
operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in
Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing
problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO"
will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey
synthesis translate directives.
Settings for common LFSR/CRC implementations:
Name Configuration Length Polynomial Initial value Notes
CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff
CRC16-CCITT Galois 16 16'h1021 16'h1d0f
CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output
CRC32C Galois, bit-reverse 32 32'h1edc6f41 32'hffffffff iSCSI, Intel CRC32 instruction; invert final output
PRBS6 Fibonacci 6 6'h21 any
PRBS7 Fibonacci 7 7'h41 any
PRBS9 Fibonacci 9 9'h021 any ITU V.52
PRBS10 Fibonacci 10 10'h081 any ITU
PRBS11 Fibonacci 11 11'h201 any ITU O.152
PRBS15 Fibonacci, inverted 15 15'h4001 any ITU O.152
PRBS17 Fibonacci 17 17'h04001 any
PRBS20 Fibonacci 20 20'h00009 any ITU V.57
PRBS23 Fibonacci, inverted 23 23'h040001 any ITU O.151
PRBS29 Fibonacci, inverted 29 29'h08000001 any
PRBS31 Fibonacci, inverted 31 31'h10000001 any
64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet
128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3
*/
function [LFSR_WIDTH+DATA_WIDTH-1:0] lfsr_mask(input [31:0] index); //! Funcion que calcula la máscara para la operación LFSR. Recibe un índice como entrada y devuelve una máscara de btis que se utiliza para seleccionar los bits relevantes para el cálculo del próximo estado
reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0]; //! Registro para almacenar el estado de la máscara del LFSR
reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0]; //! Registro para almacenar los datos del LFSR
reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0]; //! Registro del estado de la máscara de salida
reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0]; //! Registro de los datos de la máscara de salida
reg [LFSR_WIDTH-1:0] state_val; //! Registro adicional para almacenar valores de estado
reg [DATA_WIDTH-1:0] data_val; //! Registro adicional para almacenar valores de datos
reg [DATA_WIDTH-1:0] data_mask;
integer i, j;
begin
//! Init bit masks inicializo los bits de la máscara
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
lfsr_mask_state[i] = 0; // inicializa en cero todos los bits del estado de la máscara
lfsr_mask_state[i][i] = 1'b1; // pone en uno los elementos de la diagonal principal (bits relevantes para el cálculo de la máscara)
lfsr_mask_data[i] = 0; // inicializa en cero los bits de datos del LFSR
end
for (i = 0; i < DATA_WIDTH; i = i + 1) begin // inicializa los registros relacionados con la salida de la máscara
output_mask_state[i] = 0; // inicializa en cero los bits del estado de la máscara de salida
if (i < LFSR_WIDTH) begin
output_mask_state[i][i] = 1'b1; // pone en uno los elementos de la diagonal principal cuyo índice sea menor al ancho de LFSR
end
output_mask_data[i] = 0; // inicializa en cero los datos de la máscara de salida
end
//! Simulate shift register
if (LFSR_CONFIG == "FIBONACCI") begin
// Fibonacci configuration
for (data_mask = {1'b1, {DATA_WIDTH-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin // inicializa data_mask con una secuencia binaria que comienza con un 1 seguido de ceros, y luego realiza un desplazamiento a la derecha en cada iteración hasta que data_mask sea cero.
// determine shift in value
// current value in last FF, XOR with input data bit (MSB first)
state_val = lfsr_mask_state[LFSR_WIDTH-1]; //sate_val toma el valor del estado de la máscara lfsr de la última iteración
data_val = lfsr_mask_data[LFSR_WIDTH-1]; //data_val toma el valor de dato de la máscara lfsr de la última iteración
data_val = data_val ^ data_mask; // realiza una operación XOR entre data_val y data_mask
// add XOR inputs from correct indicies
for (j = 1; j < LFSR_WIDTH; j = j + 1) begin
if ((LFSR_POLY >> j) & 1) begin // Verifica si el bit en la posición j del polinomio LFSR es 1.
state_val = lfsr_mask_state[j-1] ^ state_val; //realiza una operación XOR entre state_val y el estado correspondiente en lfsr_mask_state.
data_val = lfsr_mask_data[j-1] ^ data_val; //realiza una operación XOR entre data_val y los datos correspondientes en lfsr_mask_data.
end
end
// shift
for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin //realiza un desplazamiento hacia la derecha en los registros de estado y datos del LFSR.
lfsr_mask_state[j] = lfsr_mask_state[j-1];
lfsr_mask_data[j] = lfsr_mask_data[j-1];
end
for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin //realiza un desplazamiento hacia la derecha en los registros de estado y datos de salida.
output_mask_state[j] = output_mask_state[j-1];
output_mask_data[j] = output_mask_data[j-1];
end
output_mask_state[0] = state_val; // actualiza el primer elemento de output_mask_state con el nuevo state_val.
output_mask_data[0] = data_val; // actualiza el primer elemento de output_mask_data con el nuevo data_val.
if (LFSR_FEED_FORWARD) begin
// only shift in new input data
state_val = {LFSR_WIDTH{1'b0}}; // establece todos los bits de state_val en 0.
data_val = data_mask; // establece data_val en el valor de data_mask para la siguiente iteración.
end
lfsr_mask_state[0] = state_val; // actualiza el primer elemento de lfsr_mask_state con el nuevo state_val.
lfsr_mask_data[0] = data_val; // actualiza el primer elemento de lfsr_mask_data con el nuevo data_val.
end
end else if (LFSR_CONFIG == "GALOIS") begin
// Galois configuration
for (data_mask = {1'b1, {DATA_WIDTH-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin // inicializa data_mask con una secuencia binaria que comienza con un 1 seguido de ceros, y luego realiza un desplazamiento a la derecha en cada iteración hasta que data_mask sea cero.
// determine shift in value
// current value in last FF, XOR with input data bit (MSB first)
state_val = lfsr_mask_state[LFSR_WIDTH-1]; // inicializa state_val con el valor del último estado en el registro de desplazamiento.
data_val = lfsr_mask_data[LFSR_WIDTH-1]; // inicializa data_val con el valor de los datos en el último estado en el registro de desplazamiento.
data_val = data_val ^ data_mask; // realiza una operación XOR entre data_val y data_mask.
// shift
for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin // realiza un desplazamiento hacia la derecha en los registros de estado y datos del LFSR.
lfsr_mask_state[j] = lfsr_mask_state[j-1];
lfsr_mask_data[j] = lfsr_mask_data[j-1];
end
for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin // realiza un desplazamiento hacia la derecha en los registros de salida de la máscara.
output_mask_state[j] = output_mask_state[j-1];
output_mask_data[j] = output_mask_data[j-1];
end
output_mask_state[0] = state_val; // Actualiza el primer elemento de output_mask_state con el nuevo state_val.
output_mask_data[0] = data_val; // Actualiza el primer elemento de output_mask_data con el nuevo data_val.
if (LFSR_FEED_FORWARD) begin
// only shift in new input data
state_val = {LFSR_WIDTH{1'b0}}; // establece todos los bits de state_val en 0.
data_val = data_mask; // establece data_val en el valor de data_mask para la siguiente iteración.
end
lfsr_mask_state[0] = state_val; // Actualiza el primer elemento de lfsr_mask_state con el nuevo state_val.
lfsr_mask_data[0] = data_val; // Actualiza el primer elemento de lfsr_mask_data con el nuevo data_val.
// add XOR inputs at correct indicies
for (j = 1; j < LFSR_WIDTH; j = j + 1) begin // itera sobre los índices del LFSR para realizar la operación XOR en aquellos índices específicos determinados por el polinomio del LFSR
if ((LFSR_POLY >> j) & 1) begin // Verifica si el bit en la posición j del polinomio LFSR es 1.
lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; //realiza una operación XOR entre state_val y el estado correspondiente en lfsr_mask_state.
lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; //realiza una operación XOR entre data_val y los datos correspondientes en lfsr_mask_data.
end
end
end
end else begin
$error("Error: unknown configuration setting!");
$finish;
end
// Reverse bits if selected
if (REVERSE) begin
if (index < LFSR_WIDTH) begin
state_val = 0; // Se inicializa state_val como cero
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
state_val[i] = lfsr_mask_state[LFSR_WIDTH-index-1][LFSR_WIDTH-i-1];
end
data_val = 0;
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
data_val[i] = lfsr_mask_data[LFSR_WIDTH-index-1][DATA_WIDTH-i-1];
end
end else begin
state_val = 0;
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
state_val[i] = output_mask_state[DATA_WIDTH-(index-LFSR_WIDTH)-1][LFSR_WIDTH-i-1];
end
data_val = 0;
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
data_val[i] = output_mask_data[DATA_WIDTH-(index-LFSR_WIDTH)-1][DATA_WIDTH-i-1];
end
end
end else begin
if (index < LFSR_WIDTH) begin
state_val = lfsr_mask_state[index];
data_val = lfsr_mask_data[index];
end else begin
state_val = output_mask_state[index-LFSR_WIDTH];
data_val = output_mask_data[index-LFSR_WIDTH];
end
end
lfsr_mask = {data_val, state_val};
end
endfunction
// synthesis translate_off
`define SIMULATION
// synthesis translate_on
`ifdef SIMULATION
//! "AUTO" style is "REDUCTION" for faster simulation
parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE;
`else
//! "AUTO" style is "LOOP" for better synthesis result
parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE;
`endif
genvar n;
generate
if (STYLE_INT == "REDUCTION") begin
// use Verilog reduction operator
// fast in iverilog
// significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer)
// slightly smaller than generated code with Quartus
// --> better for simulation
for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : lfsr_state
wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n);
assign state_out[n] = ^({data_in, state_in} & mask);
end
for (n = 0; n < DATA_WIDTH; n = n + 1) begin : lfsr_data
wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n+LFSR_WIDTH);
assign data_out[n] = ^({data_in, state_in} & mask);
end
end else if (STYLE_INT == "LOOP") begin
// use nested loops
// very slow in iverilog
// slightly smaller than generated code with ISE
// same size as generated code with Quartus
// --> better for synthesis
for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : lfsr_state
wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n);
reg state_reg;
assign state_out[n] = state_reg;
integer i;
always @* begin
state_reg = 1'b0;
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
if (mask[i]) begin
state_reg = state_reg ^ state_in[i];
end
end
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
if (mask[i+LFSR_WIDTH]) begin
state_reg = state_reg ^ data_in[i];
end
end
end
end
for (n = 0; n < DATA_WIDTH; n = n + 1) begin : lfsr_data
wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n+LFSR_WIDTH);
reg data_reg;
assign data_out[n] = data_reg;
integer i;
always @* begin
data_reg = 1'b0;
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
if (mask[i]) begin
data_reg = data_reg ^ state_in[i];
end
end
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
if (mask[i+LFSR_WIDTH]) begin
data_reg = data_reg ^ data_in[i];
end
end
end
end
end else begin
initial begin
$error("Error: unknown style setting!");
$finish;
end
end
endgenerate
endmodule
`resetall