From c5df9ff5d501f891e7af7fe3fdd943524e8cef31 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 14 Mar 2023 01:03:33 -0400 Subject: [PATCH] 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 --- scripts/__init__.py | 0 scripts/lfsr.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 scripts/__init__.py create mode 100755 scripts/lfsr.py diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/lfsr.py b/scripts/lfsr.py new file mode 100755 index 0000000..8ab497a --- /dev/null +++ b/scripts/lfsr.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: AGPL-3.0-Only +# Copyright (C) 2023 Sean Anderson + +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}")