lfsr: Add support for non-self-synchronizing scrambler, add tests for PCIe gen 1/2 scrambler

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-06-10 23:20:42 -07:00
parent 90780aa0b5
commit e36ac879f7
7 changed files with 75 additions and 22 deletions

View File

@@ -169,6 +169,7 @@ PRBS23 Fibonacci, inverted 23 23'h040001 any ITU
PRBS29 Fibonacci, inverted 29 29'h08000001 any PRBS29 Fibonacci, inverted 29 29'h08000001 any
PRBS31 Fibonacci, inverted 31 31'h10000001 any PRBS31 Fibonacci, inverted 31 31'h10000001 any
64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet 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 128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3
*/ */

View File

@@ -28,7 +28,9 @@ module taxi_lfsr_descramble #
// bit-reverse input and output // bit-reverse input and output
parameter logic REVERSE = 1'b1, parameter logic REVERSE = 1'b1,
// width of data bus // width of data bus
parameter DATA_W = 64 parameter DATA_W = 64,
// self-synchronizing
parameter logic SELF_SYNC = 1'b1
) )
( (
input wire logic clk, input wire logic clk,
@@ -141,6 +143,7 @@ PRBS23 Fibonacci, inverted 23 23'h040001 any ITU
PRBS29 Fibonacci, inverted 29 29'h08000001 any PRBS29 Fibonacci, inverted 29 29'h08000001 any
PRBS31 Fibonacci, inverted 31 31'h10000001 any PRBS31 Fibonacci, inverted 31 31'h10000001 any
64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet 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 128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3
*/ */
@@ -157,14 +160,14 @@ taxi_lfsr #(
.LFSR_W(LFSR_W), .LFSR_W(LFSR_W),
.LFSR_POLY(LFSR_POLY), .LFSR_POLY(LFSR_POLY),
.LFSR_GALOIS(LFSR_GALOIS), .LFSR_GALOIS(LFSR_GALOIS),
.LFSR_FEED_FORWARD('1), .LFSR_FEED_FORWARD(SELF_SYNC),
.REVERSE(REVERSE), .REVERSE(REVERSE),
.DATA_W(DATA_W), .DATA_W(DATA_W),
.DATA_IN_EN(1'b1), .DATA_IN_EN(SELF_SYNC),
.DATA_OUT_EN(1'b1) .DATA_OUT_EN(1'b1)
) )
lfsr_inst ( lfsr_inst (
.data_in(data_in), .data_in(SELF_SYNC ? data_in : '0),
.state_in(state_reg), .state_in(state_reg),
.data_out(lfsr_data), .data_out(lfsr_data),
.state_out(lfsr_state) .state_out(lfsr_state)
@@ -173,7 +176,7 @@ lfsr_inst (
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (data_in_valid) begin if (data_in_valid) begin
state_reg <= lfsr_state; state_reg <= lfsr_state;
output_reg <= lfsr_data; output_reg <= SELF_SYNC ? lfsr_data : (lfsr_data ^ data_in);
end end
if (rst) begin if (rst) begin

View File

@@ -28,7 +28,9 @@ module taxi_lfsr_scramble #
// bit-reverse input and output // bit-reverse input and output
parameter logic REVERSE = 1'b1, parameter logic REVERSE = 1'b1,
// width of data bus // width of data bus
parameter DATA_W = 64 parameter DATA_W = 64,
// self-synchronizing
parameter logic SELF_SYNC = 1'b1
) )
( (
input wire logic clk, input wire logic clk,
@@ -141,6 +143,7 @@ PRBS23 Fibonacci, inverted 23 23'h040001 any ITU
PRBS29 Fibonacci, inverted 29 29'h08000001 any PRBS29 Fibonacci, inverted 29 29'h08000001 any
PRBS31 Fibonacci, inverted 31 31'h10000001 any PRBS31 Fibonacci, inverted 31 31'h10000001 any
64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet 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 128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3
*/ */
@@ -160,11 +163,11 @@ taxi_lfsr #(
.LFSR_FEED_FORWARD('0), .LFSR_FEED_FORWARD('0),
.REVERSE(REVERSE), .REVERSE(REVERSE),
.DATA_W(DATA_W), .DATA_W(DATA_W),
.DATA_IN_EN(1'b1), .DATA_IN_EN(SELF_SYNC),
.DATA_OUT_EN(1'b1) .DATA_OUT_EN(1'b1)
) )
lfsr_inst ( lfsr_inst (
.data_in(data_in), .data_in(SELF_SYNC ? data_in : '0),
.state_in(state_reg), .state_in(state_reg),
.data_out(lfsr_data), .data_out(lfsr_data),
.state_out(lfsr_state) .state_out(lfsr_state)
@@ -173,7 +176,7 @@ lfsr_inst (
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (data_in_valid) begin if (data_in_valid) begin
state_reg <= lfsr_state; state_reg <= lfsr_state;
output_reg <= lfsr_data; output_reg <= SELF_SYNC ? lfsr_data : (lfsr_data ^ data_in);
end end
if (rst) begin if (rst) begin

View File

@@ -36,6 +36,7 @@ export PARAM_LFSR_INIT ?= "'1"
export PARAM_LFSR_GALOIS ?= "1'b0" export PARAM_LFSR_GALOIS ?= "1'b0"
export PARAM_REVERSE ?= "1'b1" export PARAM_REVERSE ?= "1'b1"
export PARAM_DATA_W ?= 8 export PARAM_DATA_W ?= 8
export PARAM_SELF_SYNC ?= "1'b1"
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst

View File

@@ -76,7 +76,21 @@ def descramble_64b66b(data, state=0x3ffffffffffffff):
return data_out 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) data_width = len(dut.data_in)
byte_lanes = data_width // 8 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)) 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 assert dscr == block
@@ -122,7 +136,12 @@ if cocotb.SIM_NAME:
# if cocotb.top.LFSR_POLY.value == 0x8000000001: # if cocotb.top.LFSR_POLY.value == 0x8000000001:
if int(cocotb.top.LFSR_W.value) == 58: if int(cocotb.top.LFSR_W.value) == 58:
factory = TestFactory(run_test_descramble) 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() factory.generate_tests()
@@ -145,11 +164,13 @@ def process_f_files(files):
return list(lst.values()) return list(lst.values())
@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w"), [ @pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w", "self_sync"), [
(58, "58'h8000000001", "'1", 0, 1, 8), (58, "58'h8000000001", "'1", 0, 1, 8, 1),
(58, "58'h8000000001", "'1", 0, 1, 64), (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" dut = "taxi_lfsr_descramble"
module = os.path.splitext(os.path.basename(__file__))[0] module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut 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['LFSR_GALOIS'] = f"1'b{lfsr_galois}"
parameters['REVERSE'] = f"1'b{reverse}" parameters['REVERSE'] = f"1'b{reverse}"
parameters['DATA_W'] = data_w 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()} extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}

View File

@@ -36,6 +36,7 @@ export PARAM_LFSR_INIT ?= "'1"
export PARAM_LFSR_GALOIS ?= "1'b0" export PARAM_LFSR_GALOIS ?= "1'b0"
export PARAM_REVERSE ?= "1'b1" export PARAM_REVERSE ?= "1'b1"
export PARAM_DATA_W ?= 8 export PARAM_DATA_W ?= 8
export PARAM_SELF_SYNC ?= "1'b1"
ifeq ($(SIM), icarus) ifeq ($(SIM), icarus)
PLUSARGS += -fst PLUSARGS += -fst

View File

@@ -64,6 +64,20 @@ def scramble_64b66b(data, state=0x3ffffffffffffff):
return data_out 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): async def run_test_scramble(dut, ref_scramble):
data_width = len(dut.data_in) 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)) block = bytearray(itertools.islice(itertools.cycle(range(256)), 1024))
scr = scramble_64b66b(block) scr = ref_scramble(block)
scr_iter = iter(chunks(scr, byte_lanes)) scr_iter = iter(chunks(scr, byte_lanes))
first = True first = True
@@ -108,6 +122,11 @@ if cocotb.SIM_NAME:
factory.add_option("ref_scramble", [scramble_64b66b]) factory.add_option("ref_scramble", [scramble_64b66b])
factory.generate_tests() 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 # cocotb-test
@@ -128,11 +147,13 @@ def process_f_files(files):
return list(lst.values()) return list(lst.values())
@pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w"), [ @pytest.mark.parametrize(("lfsr_w", "lfsr_poly", "lfsr_init", "lfsr_galois", "reverse", "data_w", "self_sync"), [
(58, "58'h8000000001", "'1", 0, 1, 8), (58, "58'h8000000001", "'1", 0, 1, 8, 1),
(58, "58'h8000000001", "'1", 0, 1, 64), (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" dut = "taxi_lfsr_scramble"
module = os.path.splitext(os.path.basename(__file__))[0] module = os.path.splitext(os.path.basename(__file__))[0]
toplevel = dut 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['LFSR_GALOIS'] = f"1'b{lfsr_galois}"
parameters['REVERSE'] = f"1'b{reverse}" parameters['REVERSE'] = f"1'b{reverse}"
parameters['DATA_W'] = data_w 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()} extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}