This is a quick post about PCIe scrambler as part of the physical layer. The polynomial is

X^16 + X^5 + X^4 + X^3 + 1.

Example image

From Wiki, This type of LSFR is called Galois LSFR where bits at taps(xor) is inverted when bit is 1.

Named after the French mathematician Évariste Galois, an LFSR in Galois configuration, which is also known as modular, internal XORs, or one-to-many LFSR, is an alternate structure that can generate the same output stream as a conventional LFSR (but offset in time).[5] In the Galois configuration, when the system is clocked, bits that are not taps are shifted one position to the right unchanged. The taps, on the other hand, are XORed with the output bit before they are stored in the next position. The new output bit is the next input bit. The effect of this is that when the output bit is zero, all the bits in the register shift to the right unchanged, and the input bit becomes zero. When the output bit is one, the bits in the tap positions all flip (if they are 0, they become 1, and if they are 1, they become 0), and then the entire register is shifted to the right and the input bit becomes 1.

This is quick-and-dirty way to do LSFR. It can be done with clever xor but who got time to be clever.


def int2binstr(value):
    b = (bin(value)[2:]).zfill(16)
    return b

def lsfr(state):
    xor = [3,4,5]

    next_state = [int(x) for x in list(int2binstr(state))]

    tmp = list(next_state)

    for i in range(0,16):
        if i in xor:
            next_state[i] = tmp[i-1] ^ tmp[15]
        else:
            next_state[i] = tmp[i-1]

    next_state = int(''.join([bin(x)[2:] for x in next_state]),2)

    return next_state
    
state = 0x0001

s = state
for i in range(18):
    o, s = (s, lsfr(s))
    print(f'old={int2binstr(o)} new={int2binstr(s)}')

And the output patterns as follows (it’s not fully tested. So, hopefully, it’s right!)

old=0000000000000001 new=1001110000000000
old=1001110000000000 new=0100111000000000
old=0100111000000000 new=0010011100000000
old=0010011100000000 new=0001001110000000
old=0001001110000000 new=0000100111000000
old=0000100111000000 new=0000010011100000
old=0000010011100000 new=0000001001110000
old=0000001001110000 new=0000000100111000
old=0000000100111000 new=0000000010011100
old=0000000010011100 new=0000000001001110
old=0000000001001110 new=0000000000100111
old=0000000000100111 new=1001110000010011
old=1001110000010011 new=1101001000001001
old=1101001000001001 new=1111010100000100
old=1111010100000100 new=0111101010000010
old=0111101010000010 new=0011110101000001
old=0011110101000001 new=1000001010100000
old=1000001010100000 new=0100000101010000

Full disclosure, This is the just first half of the scrambler. The second half is the slower register driven by clocked by Byte clock (bit rate/8).