Add script to calculate LFSR values

Add a script to calculate the initial value to load into an LFSR based
on how many cycles it should run for. The calculation is based on [1].
It takes advantage of the commutative and properties of exclusive or to
recursively break down the number of steps. This lets us achieve an
O(log(n)) runtime by caching our results. There's an alternative version
of this algoritm which stores bits by value (0x100) instead of position
(8). It's probably easier to do things that way in C or verilog, but
this was more elegant in python. When calculating the number of cycles,
we subtract one to get the number of cycles instead of the number of
steps.

[1] https://www.eevblog.com/forum/projects/algorithm-for-calculating-previous-states-of-lfsr-counters/msg2524395/#msg2524395

Signed-off-by: Sean Anderson <seanga2@gmail.com>
This commit is contained in:
Sean Anderson 2023-03-14 01:03:33 -04:00
parent cd5a4b28a0
commit c5df9ff5d5
2 changed files with 51 additions and 0 deletions

0
scripts/__init__.py Normal file
View File

51
scripts/lfsr.py Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: AGPL-3.0-Only
# Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
import functools
import sys
def logbits(x):
b = 0
while x:
if x & 1:
yield b
b += 1
x >>= 1
@functools.cache
def logstep_bit(taps, bit, logsteps):
if not logsteps:
return (bit < taps[0]) << (bit + 1) | (bit in taps)
return logstep_state(taps, logstep_bit(taps, bit, logsteps - 1), logsteps - 1)
def logstep_state(taps, state, logsteps):
lfsr = 0
for bit in logbits(state):
lfsr ^= logstep_bit(taps, bit, logsteps)
return lfsr
def step_state(taps, state, steps):
for logsteps in logbits(steps):
state = logstep_state(taps, state, logsteps)
return state
if __name__ == '__main__':
if len(sys.argv) < 2:
print(f"""Usage: {sys.argv[0]} TAPS CYCLES...
Calculate the starting state to use with the LFSR defined by TAPS (as an
integer) which will go through CYCLES states before finishing.""", file=sys.stderr)
sys.exit(1)
taps = tuple(reversed(tuple(logbits(int(sys.argv[1], base=0)))))
max_cycles = (1 << taps[0] + 1) - 1
for cycles in sys.argv[2:]:
cycles = int(cycles, base=0)
if cycles > max_cycles:
print(f"Maximum cycles is {max_cycles:#x}, got {cycles:#x}",
file=sys.stderr)
elif cycles <= 0:
print(f"Minimum cycles is 1, got {cycles}", file=sys.stderr)
else:
state = step_state(taps, max_cycles, max_cycles - cycles + 1)
print(f"{taps[0] + 1}'h{state:0{(taps[0] + 4) // 4}x}")