From e36ac879f75e04b8f816bb4eb63e50897b415a40 Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Tue, 10 Jun 2025 23:20:42 -0700 Subject: [PATCH] lfsr: Add support for non-self-synchronizing scrambler, add tests for PCIe gen 1/2 scrambler Signed-off-by: Alex Forencich --- src/lfsr/rtl/taxi_lfsr.sv | 1 + src/lfsr/rtl/taxi_lfsr_descramble.sv | 13 ++++--- src/lfsr/rtl/taxi_lfsr_scramble.sv | 11 ++++-- src/lfsr/tb/taxi_lfsr_descramble/Makefile | 1 + .../test_taxi_lfsr_descramble.py | 38 +++++++++++++++---- src/lfsr/tb/taxi_lfsr_scramble/Makefile | 1 + .../test_taxi_lfsr_scramble.py | 32 +++++++++++++--- 7 files changed, 75 insertions(+), 22 deletions(-) diff --git a/src/lfsr/rtl/taxi_lfsr.sv b/src/lfsr/rtl/taxi_lfsr.sv index 1934c8b..3b957e6 100644 --- a/src/lfsr/rtl/taxi_lfsr.sv +++ b/src/lfsr/rtl/taxi_lfsr.sv @@ -169,6 +169,7 @@ PRBS23 Fibonacci, inverted 23 23'h040001 any ITU PRBS29 Fibonacci, inverted 29 29'h08000001 any PRBS31 Fibonacci, inverted 31 31'h10000001 any 64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet +pcie Galois, bit-reverse 16 16'h0039 16'hffff PCIe gen 1/2 128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3 */ diff --git a/src/lfsr/rtl/taxi_lfsr_descramble.sv b/src/lfsr/rtl/taxi_lfsr_descramble.sv index 0aa4f90..f6e35ba 100644 --- a/src/lfsr/rtl/taxi_lfsr_descramble.sv +++ b/src/lfsr/rtl/taxi_lfsr_descramble.sv @@ -28,7 +28,9 @@ module taxi_lfsr_descramble # // bit-reverse input and output parameter logic REVERSE = 1'b1, // width of data bus - parameter DATA_W = 64 + parameter DATA_W = 64, + // self-synchronizing + parameter logic SELF_SYNC = 1'b1 ) ( input wire logic clk, @@ -141,6 +143,7 @@ PRBS23 Fibonacci, inverted 23 23'h040001 any ITU PRBS29 Fibonacci, inverted 29 29'h08000001 any PRBS31 Fibonacci, inverted 31 31'h10000001 any 64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet +pcie Galois, bit-reverse 16 16'h0039 16'hffff PCIe gen 1/2 128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3 */ @@ -157,14 +160,14 @@ taxi_lfsr #( .LFSR_W(LFSR_W), .LFSR_POLY(LFSR_POLY), .LFSR_GALOIS(LFSR_GALOIS), - .LFSR_FEED_FORWARD('1), + .LFSR_FEED_FORWARD(SELF_SYNC), .REVERSE(REVERSE), .DATA_W(DATA_W), - .DATA_IN_EN(1'b1), + .DATA_IN_EN(SELF_SYNC), .DATA_OUT_EN(1'b1) ) lfsr_inst ( - .data_in(data_in), + .data_in(SELF_SYNC ? data_in : '0), .state_in(state_reg), .data_out(lfsr_data), .state_out(lfsr_state) @@ -173,7 +176,7 @@ lfsr_inst ( always_ff @(posedge clk) begin if (data_in_valid) begin state_reg <= lfsr_state; - output_reg <= lfsr_data; + output_reg <= SELF_SYNC ? lfsr_data : (lfsr_data ^ data_in); end if (rst) begin diff --git a/src/lfsr/rtl/taxi_lfsr_scramble.sv b/src/lfsr/rtl/taxi_lfsr_scramble.sv index 7a2d305..8dc52be 100644 --- a/src/lfsr/rtl/taxi_lfsr_scramble.sv +++ b/src/lfsr/rtl/taxi_lfsr_scramble.sv @@ -28,7 +28,9 @@ module taxi_lfsr_scramble # // bit-reverse input and output parameter logic REVERSE = 1'b1, // width of data bus - parameter DATA_W = 64 + parameter DATA_W = 64, + // self-synchronizing + parameter logic SELF_SYNC = 1'b1 ) ( input wire logic clk, @@ -141,6 +143,7 @@ PRBS23 Fibonacci, inverted 23 23'h040001 any ITU PRBS29 Fibonacci, inverted 29 29'h08000001 any PRBS31 Fibonacci, inverted 31 31'h10000001 any 64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet +pcie Galois, bit-reverse 16 16'h0039 16'hffff PCIe gen 1/2 128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3 */ @@ -160,11 +163,11 @@ taxi_lfsr #( .LFSR_FEED_FORWARD('0), .REVERSE(REVERSE), .DATA_W(DATA_W), - .DATA_IN_EN(1'b1), + .DATA_IN_EN(SELF_SYNC), .DATA_OUT_EN(1'b1) ) lfsr_inst ( - .data_in(data_in), + .data_in(SELF_SYNC ? data_in : '0), .state_in(state_reg), .data_out(lfsr_data), .state_out(lfsr_state) @@ -173,7 +176,7 @@ lfsr_inst ( always_ff @(posedge clk) begin if (data_in_valid) begin state_reg <= lfsr_state; - output_reg <= lfsr_data; + output_reg <= SELF_SYNC ? lfsr_data : (lfsr_data ^ data_in); end if (rst) begin diff --git a/src/lfsr/tb/taxi_lfsr_descramble/Makefile b/src/lfsr/tb/taxi_lfsr_descramble/Makefile index 8cdcebd..7b06e36 100644 --- a/src/lfsr/tb/taxi_lfsr_descramble/Makefile +++ b/src/lfsr/tb/taxi_lfsr_descramble/Makefile @@ -36,6 +36,7 @@ export PARAM_LFSR_INIT ?= "'1" export PARAM_LFSR_GALOIS ?= "1'b0" export PARAM_REVERSE ?= "1'b1" export PARAM_DATA_W ?= 8 +export PARAM_SELF_SYNC ?= "1'b1" ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/src/lfsr/tb/taxi_lfsr_descramble/test_taxi_lfsr_descramble.py b/src/lfsr/tb/taxi_lfsr_descramble/test_taxi_lfsr_descramble.py index 434a7c7..6395d42 100644 --- a/src/lfsr/tb/taxi_lfsr_descramble/test_taxi_lfsr_descramble.py +++ b/src/lfsr/tb/taxi_lfsr_descramble/test_taxi_lfsr_descramble.py @@ -76,7 +76,21 @@ def descramble_64b66b(data, state=0x3ffffffffffffff): return data_out -async def run_test_descramble(dut, ref_scramble): +def scramble_pcie(data, state=0xffff, poly=0x9c00): + data_out = bytearray() + for d in data: + b = 0 + for i in range(8): + if state & 1: + state = (state >> 1) ^ poly + b = b | (1 << i) + else: + state = state >> 1 + data_out.append(b ^ d) + return data_out + + +async def run_test_descramble(dut, ref_scramble, ref_descramble): data_width = len(dut.data_in) byte_lanes = data_width // 8 @@ -87,9 +101,9 @@ async def run_test_descramble(dut, ref_scramble): block = bytearray(itertools.islice(itertools.cycle(range(256)), 1024)) - scr = scramble_64b66b(block) + scr = ref_scramble(block) - dscr = descramble_64b66b(scr) + dscr = ref_descramble(scr) assert dscr == block @@ -122,7 +136,12 @@ if cocotb.SIM_NAME: # if cocotb.top.LFSR_POLY.value == 0x8000000001: if int(cocotb.top.LFSR_W.value) == 58: factory = TestFactory(run_test_descramble) - factory.add_option("ref_scramble", [scramble_64b66b]) + factory.add_option(("ref_scramble", "ref_descramble"), [(scramble_64b66b, descramble_64b66b)]) + factory.generate_tests() + + if cocotb.top.LFSR_POLY.value == 0x0039: + factory = TestFactory(run_test_descramble) + factory.add_option(("ref_scramble", "ref_descramble"), [(scramble_pcie, scramble_pcie)]) factory.generate_tests() @@ -145,11 +164,13 @@ def process_f_files(files): return list(lst.values()) -@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w"), [ - (58, "58'h8000000001", "'1", 0, 1, 8), - (58, "58'h8000000001", "'1", 0, 1, 64), +@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w", "self_sync"), [ + (58, "58'h8000000001", "'1", 0, 1, 8, 1), + (58, "58'h8000000001", "'1", 0, 1, 64, 1), + (16, "16'h0039", "'1", 1, 1, 8, 0), + (16, "16'h0039", "'1", 1, 1, 64, 0), ]) -def test_taxi_lfsr_descramble(request, lfsr_w, lfsr_poly, lfsr_init, lfsr_galois, reverse, data_w): +def test_taxi_lfsr_descramble(request, lfsr_w, lfsr_poly, lfsr_init, lfsr_galois, reverse, data_w, self_sync): dut = "taxi_lfsr_descramble" module = os.path.splitext(os.path.basename(__file__))[0] toplevel = dut @@ -169,6 +190,7 @@ def test_taxi_lfsr_descramble(request, lfsr_w, lfsr_poly, lfsr_init, lfsr_galois parameters['LFSR_GALOIS'] = f"1'b{lfsr_galois}" parameters['REVERSE'] = f"1'b{reverse}" parameters['DATA_W'] = data_w + parameters['SELF_SYNC'] = f"1'b{self_sync}" extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} diff --git a/src/lfsr/tb/taxi_lfsr_scramble/Makefile b/src/lfsr/tb/taxi_lfsr_scramble/Makefile index 9e645f1..702a412 100644 --- a/src/lfsr/tb/taxi_lfsr_scramble/Makefile +++ b/src/lfsr/tb/taxi_lfsr_scramble/Makefile @@ -36,6 +36,7 @@ export PARAM_LFSR_INIT ?= "'1" export PARAM_LFSR_GALOIS ?= "1'b0" export PARAM_REVERSE ?= "1'b1" export PARAM_DATA_W ?= 8 +export PARAM_SELF_SYNC ?= "1'b1" ifeq ($(SIM), icarus) PLUSARGS += -fst diff --git a/src/lfsr/tb/taxi_lfsr_scramble/test_taxi_lfsr_scramble.py b/src/lfsr/tb/taxi_lfsr_scramble/test_taxi_lfsr_scramble.py index 0f3363e..4faeafc 100644 --- a/src/lfsr/tb/taxi_lfsr_scramble/test_taxi_lfsr_scramble.py +++ b/src/lfsr/tb/taxi_lfsr_scramble/test_taxi_lfsr_scramble.py @@ -64,6 +64,20 @@ def scramble_64b66b(data, state=0x3ffffffffffffff): return data_out +def scramble_pcie(data, state=0xffff, poly=0x9c00): + data_out = bytearray() + for d in data: + b = 0 + for i in range(8): + if state & 1: + state = (state >> 1) ^ poly + b = b | (1 << i) + else: + state = state >> 1 + data_out.append(b ^ d) + return data_out + + async def run_test_scramble(dut, ref_scramble): data_width = len(dut.data_in) @@ -75,7 +89,7 @@ async def run_test_scramble(dut, ref_scramble): block = bytearray(itertools.islice(itertools.cycle(range(256)), 1024)) - scr = scramble_64b66b(block) + scr = ref_scramble(block) scr_iter = iter(chunks(scr, byte_lanes)) first = True @@ -108,6 +122,11 @@ if cocotb.SIM_NAME: factory.add_option("ref_scramble", [scramble_64b66b]) factory.generate_tests() + if cocotb.top.LFSR_POLY.value == 0x0039: + factory = TestFactory(run_test_scramble) + factory.add_option("ref_scramble", [scramble_pcie]) + factory.generate_tests() + # cocotb-test @@ -128,11 +147,13 @@ def process_f_files(files): return list(lst.values()) -@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w"), [ - (58, "58'h8000000001", "'1", 0, 1, 8), - (58, "58'h8000000001", "'1", 0, 1, 64), +@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w", "self_sync"), [ + (58, "58'h8000000001", "'1", 0, 1, 8, 1), + (58, "58'h8000000001", "'1", 0, 1, 64, 1), + (16, "16'h0039", "'1", 1, 1, 8, 0), + (16, "16'h0039", "'1", 1, 1, 64, 0), ]) -def test_taxi_lfsr_scramble(request, lfsr_w, lfsr_poly, lfsr_init, lfsr_galois, reverse, data_w): +def test_taxi_lfsr_scramble(request, lfsr_w, lfsr_poly, lfsr_init, lfsr_galois, reverse, data_w, self_sync): dut = "taxi_lfsr_scramble" module = os.path.splitext(os.path.basename(__file__))[0] toplevel = dut @@ -152,6 +173,7 @@ def test_taxi_lfsr_scramble(request, lfsr_w, lfsr_poly, lfsr_init, lfsr_galois, parameters['LFSR_GALOIS'] = f"1'b{lfsr_galois}" parameters['REVERSE'] = f"1'b{reverse}" parameters['DATA_W'] = data_w + parameters['SELF_SYNC'] = f"1'b{self_sync}" extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}