blob: 6bf56e698e4f18da507d2a93cb0fd261cbdd54c3 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""This script generates random seeds and state permutations for LFSRs
and outputs them them as a packed SV logic vectors suitable for use with
prim_lfsr.sv.
"""
import argparse
import random
import textwrap
import logging as log
from math import ceil, log2
SV_INSTRUCTIONS = """
------------------------------------------------
| COPY PASTE THE CODE TEMPLATE BELOW INTO YOUR |
| RTL CODE, INLUDING THE COMMENT IN ORDER TO |
| EASE AUDITABILITY AND REPRODUCIBILITY. |
------------------------------------------------
"""
def _as_snake_case_prefix(name):
""" Convert PascalCase name into snake_case name"""
outname = ""
for c in name:
if c.isupper() and len(outname) > 0:
outname += '_'
outname += c.lower()
return outname + ('_' if name else '')
def _get_random_data_hex_literal(width):
""" Fetch 'width' random bits and return them as hex literal"""
width = int(width)
literal_str = hex(random.getrandbits(width))
literal_str = str(width) + "'h" + literal_str[2:]
return literal_str
def _blockify(s, size, limit):
""" Make sure the output does not exceed a certain size per line"""
str_idx = 2
remain = size % (limit * 4)
numbits = remain if remain else limit * 4
s_list = []
remain = size
while remain > 0:
s_incr = int(numbits / 4)
s_list.append("{}'h{}".format(numbits, s[str_idx: str_idx + s_incr]))
str_idx += s_incr
remain -= numbits
numbits = limit * 4
return(",\n ".join(s_list))
def _get_random_perm_hex_literal(numel):
""" Compute a random permutation of 'numel' elements and
return as packed hex literal"""
num_elements = int(numel)
width = int(ceil(log2(num_elements)))
idx = [x for x in range(num_elements)]
random.shuffle(idx)
literal_str = ""
for k in idx:
literal_str += format(k, '0' + str(width) + 'b')
# convert to hex for space efficiency
literal_str = hex(int(literal_str, 2))
return _blockify(literal_str, width * numel, 64)
def _wrapped_docstring():
'''Return a text-wrapped version of the module docstring'''
paras = []
para = []
for line in __doc__.strip().split('\n'):
line = line.strip()
if not line:
if para:
paras.append('\n'.join(para))
para = []
else:
para.append(line)
if para:
paras.append('\n'.join(para))
return '\n\n'.join(textwrap.fill(p) for p in paras)
def main():
log.basicConfig(level=log.INFO,
format="%(asctime)s - %(message)s",
datefmt="%Y-%m-%d %H:%M")
parser = argparse.ArgumentParser(
prog="gen-lfsre-perm",
description=_wrapped_docstring(),
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-w',
'--width',
type=int,
default=32,
metavar='<#bitwidth>',
help='LFSR width.')
parser.add_argument('-s',
'--seed',
type=int,
metavar='<seed>',
help='Custom seed for RNG.')
parser.add_argument('-p',
'--prefix',
type=str,
metavar='name',
default="",
help='Optional prefix to add to '
'types and parameters. '
'Make sure this is PascalCase.')
args = parser.parse_args()
if args.width <= 0:
log.error("LFSR width must be nonzero")
exit(1)
if args.seed is None:
random.seed()
args.seed = random.getrandbits(32)
random.seed(args.seed)
print(SV_INSTRUCTIONS)
type_prefix = _as_snake_case_prefix(args.prefix)
outstr = '''
// These LFSR parameters have been generated with
// $ hw/ip/prim/util/gen-lfsr-seed.py --width {} --seed {} --prefix "{}"
parameter int {}LfsrWidth = {};
typedef logic [{}LfsrWidth-1:0] {}lfsr_seed_t;
typedef logic [{}LfsrWidth-1:0][$clog2({}LfsrWidth)-1:0] {}lfsr_perm_t;
parameter {}lfsr_seed_t RndCnst{}LfsrSeedDefault = {};
parameter {}lfsr_perm_t RndCnst{}LfsrPermDefault = {{
{}
}};
'''.format(args.width, args.seed, args.prefix,
args.prefix, args.width,
args.prefix, type_prefix,
args.prefix, args.prefix, type_prefix,
type_prefix, args.prefix, _get_random_data_hex_literal(args.width),
type_prefix, args.prefix, _get_random_perm_hex_literal(args.width))
print(outstr)
if __name__ == "__main__":
main()