diff --git a/src/lfsr/rtl/taxi_lfsr.sv b/src/lfsr/rtl/taxi_lfsr.sv index cfb6873..438f31d 100644 --- a/src/lfsr/rtl/taxi_lfsr.sv +++ b/src/lfsr/rtl/taxi_lfsr.sv @@ -1,7 +1,7 @@ // SPDX-License-Identifier: CERN-OHL-S-2.0 /* -Copyright (c) 2016-2025 FPGA Ninja, LLC +Copyright (c) 2016-2026 FPGA Ninja, LLC Authors: - Alex Forencich @@ -31,7 +31,10 @@ module taxi_lfsr # parameter DATA_W = 8, // enable data input and output parameter logic DATA_IN_EN = 1'b1, - parameter logic DATA_OUT_EN = 1'b1 + parameter logic DATA_OUT_EN = 1'b1, + // shift control + parameter STATE_SHIFT_PRE = 0, + parameter STATE_SHIFT_POST = 0 ) ( input wire logic [DATA_W-1:0] data_in, @@ -150,6 +153,21 @@ Specify width of input and output data bus. The module will perform one shift p data bit, so if the input data bus is not required tie data_in to zero and set DATA_W to the required number of shifts per clock cycle. +DATA_IN_EN, DATA_OUT_EN + +Enable data input and/or output ports. Useful for CRC computation where the +data shifted out of the register is not used, or PRBS generation where no data +is shifted in to the register. Disabling unused inputs and outputs will +increase simulation speed. + +STATE_SHIFT_PRE, STATE_SHIFT_POST + +Shift the state before/after shifting the data. Useful for either shifting the +state only, or for performing a split CRC computation on a wide/segmented data +bus. Positive shift values are equivalent to extending the data input port with +zeros. Negative shift amounts shift the state backwards, useful for removing +zero padding and similar. + Settings for common LFSR/CRC implementations: Name Configuration Length Polynomial Initial value Notes @@ -210,75 +228,318 @@ function [OUT_W-1:0][IN_W-1:0] lfsr_mask(); // simulate shift register if (LFSR_GALOIS) begin // Galois configuration - for (data_mask = {1'b1, {DATA_W-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin - // determine shift in value - // current value in last FF, XOR with input data bit (MSB first) - state_val = lfsr_mask_state[LFSR_W-1]; - data_val = lfsr_mask_data[LFSR_W-1]; - data_val = data_val ^ data_mask; - // shift - for (integer j = LFSR_W-1; j > 0; j = j - 1) begin - lfsr_mask_state[j] = lfsr_mask_state[j-1]; - lfsr_mask_data[j] = lfsr_mask_data[j-1]; - end - for (integer j = DATA_W-1; j > 0; j = j - 1) begin - 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; - output_mask_data[0] = data_val; - if (LFSR_FEED_FORWARD) begin - // only shift in new input data - state_val = '0; - data_val = data_mask; - end - lfsr_mask_state[0] = state_val; - lfsr_mask_data[0] = data_val; + // Shift state alone before shifting data + if (STATE_SHIFT_PRE > 0) begin + // forward shift + for (integer i = 0; i < STATE_SHIFT_PRE; i = i + 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_W-1]; + data_val = lfsr_mask_data[LFSR_W-1]; - // add XOR inputs at correct indicies - for (integer j = 1; j < LFSR_W; j = j + 1) begin - if (LFSR_POLY[j]) begin - lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; - lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + // shift + for (integer j = LFSR_W-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end else if (STATE_SHIFT_PRE < 0) begin + // reverse shift + for (integer i = 0; i < -STATE_SHIFT_PRE; i = i + 1) begin + state_val = lfsr_mask_state[0]; + data_val = lfsr_mask_data[0]; + + // add XOR inputs at correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + + // shift + for (integer j = 0; j < LFSR_W-1; j = j + 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j+1]; + lfsr_mask_data[j] = lfsr_mask_data[j+1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[LFSR_W-1] = state_val; + lfsr_mask_data[LFSR_W-1] = data_val; + end + end + + // Shift data + if (DATA_IN_EN || DATA_OUT_EN) begin + for (data_mask = {1'b1, {DATA_W-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_W-1]; + data_val = lfsr_mask_data[LFSR_W-1]; + data_val = data_val ^ data_mask; + + // shift + for (integer j = LFSR_W-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (integer j = DATA_W-1; j > 0; j = j - 1) begin + 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; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = data_mask; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end + + // Shift state alone after shifting data + if (STATE_SHIFT_POST > 0) begin + // forward shift + for (integer i = 0; i < STATE_SHIFT_POST; i = i + 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_W-1]; + data_val = lfsr_mask_data[LFSR_W-1]; + + // shift + for (integer j = LFSR_W-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end else if (STATE_SHIFT_POST < 0) begin + // reverse shift + for (integer i = 0; i < -STATE_SHIFT_POST; i = i + 1) begin + state_val = lfsr_mask_state[0]; + data_val = lfsr_mask_data[0]; + + // add XOR inputs at correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + + // shift + for (integer j = 0; j < LFSR_W-1; j = j + 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j+1]; + lfsr_mask_data[j] = lfsr_mask_data[j+1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[LFSR_W-1] = state_val; + lfsr_mask_data[LFSR_W-1] = data_val; end end end else begin // Fibonacci configuration - for (data_mask = {1'b1, {DATA_W-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin - // determine shift in value - // current value in last FF, XOR with input data bit (MSB first) - state_val = lfsr_mask_state[LFSR_W-1]; - data_val = lfsr_mask_data[LFSR_W-1]; - data_val = data_val ^ data_mask; - // add XOR inputs from correct indicies - for (integer j = 1; j < LFSR_W; j = j + 1) begin - if (LFSR_POLY[j]) begin - state_val = lfsr_mask_state[j-1] ^ state_val; - data_val = lfsr_mask_data[j-1] ^ data_val; + // Shift state alone before shifting data + if (STATE_SHIFT_PRE > 0) begin + // forward shift + for (integer i = 0; i < STATE_SHIFT_PRE; i = i + 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_W-1]; + data_val = lfsr_mask_data[LFSR_W-1]; + + // add XOR inputs from correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (integer j = LFSR_W-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + end + end else if (STATE_SHIFT_PRE < 0) begin + // reverse shift + for (integer i = 0; i < -STATE_SHIFT_PRE; i = i + 1) begin + state_val = lfsr_mask_state[0]; + data_val = lfsr_mask_data[0]; + + // shift + for (integer j = 0; j < LFSR_W-1; j = j + 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j+1]; + lfsr_mask_data[j] = lfsr_mask_data[j+1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[LFSR_W-1] = state_val; + lfsr_mask_data[LFSR_W-1] = data_val; + + // add XOR inputs from correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end end end + end - // shift - for (integer j = LFSR_W-1; j > 0; j = j - 1) begin - lfsr_mask_state[j] = lfsr_mask_state[j-1]; - lfsr_mask_data[j] = lfsr_mask_data[j-1]; + // Shift data + if (DATA_IN_EN || DATA_OUT_EN) begin + for (data_mask = {1'b1, {DATA_W-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_W-1]; + data_val = lfsr_mask_data[LFSR_W-1]; + data_val = data_val ^ data_mask; + + // add XOR inputs from correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (integer j = LFSR_W-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (integer j = DATA_W-1; j > 0; j = j - 1) begin + 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; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = data_mask; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; end - for (integer j = DATA_W-1; j > 0; j = j - 1) begin - output_mask_state[j] = output_mask_state[j-1]; - output_mask_data[j] = output_mask_data[j-1]; + end + + // Shift state alone after shifting data + if (STATE_SHIFT_POST > 0) begin + for (integer i = 0; i < STATE_SHIFT_POST; i = i + 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_W-1]; + data_val = lfsr_mask_data[LFSR_W-1]; + + // add XOR inputs from correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (integer j = LFSR_W-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; end - output_mask_state[0] = state_val; - output_mask_data[0] = data_val; - if (LFSR_FEED_FORWARD) begin - // only shift in new input data - state_val = '0; - data_val = data_mask; + end else if (STATE_SHIFT_POST < 0) begin + // reverse shift + for (integer i = 0; i < -STATE_SHIFT_POST; i = i + 1) begin + state_val = lfsr_mask_state[0]; + data_val = lfsr_mask_data[0]; + + // shift + for (integer j = 0; j < LFSR_W-1; j = j + 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j+1]; + lfsr_mask_data[j] = lfsr_mask_data[j+1]; + end + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = '0; + data_val = '0; + end + lfsr_mask_state[LFSR_W-1] = state_val; + lfsr_mask_data[LFSR_W-1] = data_val; + + // add XOR inputs from correct indicies + for (integer j = 1; j < LFSR_W; j = j + 1) begin + if (LFSR_POLY[j]) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end end - lfsr_mask_state[0] = state_val; - lfsr_mask_data[0] = data_val; end end diff --git a/src/lfsr/rtl/taxi_lfsr_crc.sv b/src/lfsr/rtl/taxi_lfsr_crc.sv index fd0222f..3f39a61 100644 --- a/src/lfsr/rtl/taxi_lfsr_crc.sv +++ b/src/lfsr/rtl/taxi_lfsr_crc.sv @@ -131,7 +131,7 @@ DATA_W Specify width of input 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_W to the -required number of shifts per clock cycle. +required number of shifts per clock cycle. Settings for common LFSR/CRC implementations: @@ -157,7 +157,9 @@ taxi_lfsr #( .REVERSE(REVERSE), .DATA_W(DATA_W), .DATA_IN_EN(1'b1), - .DATA_OUT_EN(1'b0) + .DATA_OUT_EN(1'b0), + .STATE_SHIFT_PRE(0), + .STATE_SHIFT_POST(0) ) lfsr_inst ( .data_in(data_in), diff --git a/src/lfsr/rtl/taxi_lfsr_descramble.sv b/src/lfsr/rtl/taxi_lfsr_descramble.sv index f6e35ba..243fabd 100644 --- a/src/lfsr/rtl/taxi_lfsr_descramble.sv +++ b/src/lfsr/rtl/taxi_lfsr_descramble.sv @@ -164,7 +164,9 @@ taxi_lfsr #( .REVERSE(REVERSE), .DATA_W(DATA_W), .DATA_IN_EN(SELF_SYNC), - .DATA_OUT_EN(1'b1) + .DATA_OUT_EN(1'b1), + .STATE_SHIFT_PRE(0), + .STATE_SHIFT_POST(0) ) lfsr_inst ( .data_in(SELF_SYNC ? data_in : '0), diff --git a/src/lfsr/rtl/taxi_lfsr_prbs_check.sv b/src/lfsr/rtl/taxi_lfsr_prbs_check.sv index 38c4ee4..3ab4ffc 100644 --- a/src/lfsr/rtl/taxi_lfsr_prbs_check.sv +++ b/src/lfsr/rtl/taxi_lfsr_prbs_check.sv @@ -167,7 +167,9 @@ taxi_lfsr #( .REVERSE(REVERSE), .DATA_W(DATA_W), .DATA_IN_EN(1'b1), - .DATA_OUT_EN(1'b1) + .DATA_OUT_EN(1'b1), + .STATE_SHIFT_PRE(0), + .STATE_SHIFT_POST(0) ) lfsr_inst ( .data_in(INVERT ? ~data_in : data_in), diff --git a/src/lfsr/rtl/taxi_lfsr_prbs_gen.sv b/src/lfsr/rtl/taxi_lfsr_prbs_gen.sv index 1275d2f..dfdbcf6 100644 --- a/src/lfsr/rtl/taxi_lfsr_prbs_gen.sv +++ b/src/lfsr/rtl/taxi_lfsr_prbs_gen.sv @@ -161,7 +161,9 @@ taxi_lfsr #( .REVERSE(REVERSE), .DATA_W(DATA_W), .DATA_IN_EN(1'b0), - .DATA_OUT_EN(1'b1) + .DATA_OUT_EN(1'b1), + .STATE_SHIFT_PRE(0), + .STATE_SHIFT_POST(0) ) lfsr_inst ( .data_in('0), diff --git a/src/lfsr/rtl/taxi_lfsr_scramble.sv b/src/lfsr/rtl/taxi_lfsr_scramble.sv index f0b778e..0d48c0b 100644 --- a/src/lfsr/rtl/taxi_lfsr_scramble.sv +++ b/src/lfsr/rtl/taxi_lfsr_scramble.sv @@ -164,7 +164,9 @@ taxi_lfsr #( .REVERSE(REVERSE), .DATA_W(DATA_W), .DATA_IN_EN(SELF_SYNC), - .DATA_OUT_EN(1'b1) + .DATA_OUT_EN(1'b1), + .STATE_SHIFT_PRE(0), + .STATE_SHIFT_POST(0) ) lfsr_inst ( .data_in(SELF_SYNC ? data_in : '0), diff --git a/src/lfsr/tb/taxi_lfsr/Makefile b/src/lfsr/tb/taxi_lfsr/Makefile index 6509501..b28411a 100644 --- a/src/lfsr/tb/taxi_lfsr/Makefile +++ b/src/lfsr/tb/taxi_lfsr/Makefile @@ -37,6 +37,8 @@ export PARAM_REVERSE ?= "1'b1" export PARAM_DATA_W ?= 8 export PARAM_DATA_IN_EN ?= "1'b1" export PARAM_DATA_OUT_EN ?= "1'b1" +export PARAM_STATE_SHIFT_PRE ?= 0 +export PARAM_STATE_SHIFT_POST ?= 0 ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/src/lfsr/tb/taxi_lfsr/test_taxi_lfsr.py b/src/lfsr/tb/taxi_lfsr/test_taxi_lfsr.py index 2e66635..b173ae0 100644 --- a/src/lfsr/tb/taxi_lfsr/test_taxi_lfsr.py +++ b/src/lfsr/tb/taxi_lfsr/test_taxi_lfsr.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: CERN-OHL-S-2.0 """ -Copyright (c) 2023-2025 FPGA Ninja, LLC +Copyright (c) 2023-2026 FPGA Ninja, LLC Authors: - Alex Forencich @@ -37,8 +37,40 @@ def chunks(lst, n, padvalue=None): return itertools.zip_longest(*[iter(lst)]*n, fillvalue=padvalue) -def crc32(data): - return zlib.crc32(data) & 0xffffffff +def crc32(data, crc=0xffffffff, poly=0xedb88320): + # return zlib.crc32(data) & 0xffffffff + for d in data: + crc = crc ^ d + for bit in range(0, 8): + if crc & 1: + crc = (crc >> 1) ^ poly + else: + crc = crc >> 1 + return ~crc & 0xffffffff + + +def crc32_shift(crc, count, poly=0xedb88320): + crc = ~crc & 0xffffffff + + if count > 0: + # shift forwards + for i in range(count): + if crc & 1: + crc = (crc >> 1) ^ poly + else: + crc = crc >> 1 + + elif count < 0: + # shift backwards + for i in range(-count): + if crc & 0x80000000: + crc ^= poly + crc = (crc << 1) | 1 + else: + crc = crc << 1 + crc = crc & 0xffffffff + + return ~crc & 0xffffffff def crc32c(data, crc=0xffffffff, poly=0x82f63b78): @@ -149,12 +181,65 @@ async def run_test_prbs(dut, ref_prbs): await Timer(10, 'ns') +async def run_test_shift_crc(dut): + + data_width = len(dut.data_in) + byte_lanes = data_width // 8 + + state_width = len(dut.state_in) + state_mask = 2**state_width-1 + + shift = int(dut.STATE_SHIFT_PRE.value) + if shift > 0 and shift & 0x80000000: + shift -= 0x100000000 + + tb = TB(dut) + + await Timer(10, 'ns') + + val = 0x12345678 + + dut.state_in.value = ~val & state_mask + dut.data_in.value = 0 + await Timer(10, 'ns') + + crc = ~int(dut.state_out.value) & state_mask + ref = crc32_shift(val, shift) + + tb.log.info("Shifted CRC: 0x%x (ref: 0x%x)", crc, ref) + + assert crc == ref + + await Timer(10, 'ns') + + for k in range(10): + + val = crc32(bytearray(k)) + + dut.state_in.value = ~val & state_mask + dut.data_in.value = 0 + await Timer(10, 'ns') + + crc = ~int(dut.state_out.value) & state_mask + ref = crc32_shift(val, shift) + + tb.log.info("Shifted CRC: 0x%x (ref: 0x%x)", crc, ref) + + assert crc == ref + + await Timer(10, 'ns') + + if getattr(cocotb, 'top', None) is not None: if cocotb.top.LFSR_POLY.value == 0x4c11db7: - factory = TestFactory(run_test_crc) - factory.add_option("ref_crc", [crc32]) - factory.generate_tests() + if cocotb.top.STATE_SHIFT_PRE.value == 0: + factory = TestFactory(run_test_crc) + factory.add_option("ref_crc", [crc32]) + factory.generate_tests() + else: + factory = TestFactory(run_test_shift_crc) + factory.generate_tests() if cocotb.top.LFSR_POLY.value == 0x1edc6f41: factory = TestFactory(run_test_crc) @@ -191,17 +276,19 @@ def process_f_files(files): return list(lst.values()) -@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_galois", "reverse", "data_w"), [ - (32, "32'h4c11db7", 1, 1, 8), - (32, "32'h4c11db7", 1, 1, 64), - (32, "32'h1edc6f41", 1, 1, 8), - (32, "32'h1edc6f41", 1, 1, 64), - (9, "9'h021", 0, 0, 8), - (9, "9'h021", 0, 0, 64), - (31, "31'h10000001", 0, 0, 8), - (31, "31'h10000001", 0, 0, 64), +@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_galois", "reverse", "data_w", "data_shift", "pre_shift"), [ + (32, "32'h4c11db7", 1, 1, 8, 1, 0), + (32, "32'h4c11db7", 1, 1, 64, 1, 0), + (32, "32'h4c11db7", 1, 1, 64, 0, 8), + (32, "32'h4c11db7", 1, 1, 64, 0, -8), + (32, "32'h1edc6f41", 1, 1, 8, 1, 0), + (32, "32'h1edc6f41", 1, 1, 64, 1, 0), + (9, "9'h021", 0, 0, 8, 1, 0), + (9, "9'h021", 0, 0, 64, 1, 0), + (31, "31'h10000001", 0, 0, 8, 1, 0), + (31, "31'h10000001", 0, 0, 64, 1, 0), ]) -def test_taxi_lfsr(request, lfsr_w, lfsr_poly, lfsr_galois, reverse, data_w): +def test_taxi_lfsr(request, lfsr_w, lfsr_poly, lfsr_galois, reverse, data_w, data_shift, pre_shift): dut = "taxi_lfsr" module = os.path.splitext(os.path.basename(__file__))[0] toplevel = dut @@ -220,8 +307,10 @@ def test_taxi_lfsr(request, lfsr_w, lfsr_poly, lfsr_galois, reverse, data_w): parameters['LFSR_FEED_FORWARD'] = "1'b0" parameters['REVERSE'] = f"1'b{reverse}" parameters['DATA_W'] = data_w - parameters['DATA_IN_EN'] = "1'b1" - parameters['DATA_OUT_EN'] = "1'b1" + parameters['DATA_IN_EN'] = f"1'b{data_shift}" + parameters['DATA_OUT_EN'] = f"1'b{data_shift}" + parameters['STATE_SHIFT_PRE'] = pre_shift + parameters['STATE_SHIFT_POST'] = 0 extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}