[topgen] Use strong_random class to generate topgen constants
Replace calls to random class functions with strong_random
functions to generate netlist constants and permutations.
This class uses entropy from file entropy_buffer.txt
Signed-off-by: Vladimir Rozic <vrozic@lowrisc.org>
diff --git a/util/topgen.py b/util/topgen.py
index 0729351..74830e5 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -6,6 +6,7 @@
"""
import argparse
import logging as log
+import os
import random
import shutil
import sys
@@ -28,10 +29,11 @@
from reggen.ip_block import IpBlock
from reggen.countermeasure import CounterMeasure
from reggen.lib import check_list
+from topgen import entropy_buffer_generator as ebg
from topgen import get_hjsonobj_xbars
from topgen import intermodule as im
from topgen import lib as lib
-from topgen import merge_top, search_ips, validate_top
+from topgen import merge_top, search_ips, strong_random, validate_top
from topgen.c_test import TopGenCTest
from topgen.clocks import Clocks
from topgen.gen_dv import gen_dv
@@ -57,6 +59,16 @@
TOPGEN_TEMPLATE_PATH = Path(__file__).parent / "topgen/templates"
+# Size and path to the entropy buffer.
+# This buffer is generated using Mersenne Twister PRNG seeded with rnd_cnst_seed
+# and deleted in the end."
+# This buffer will not be created If a different one is provided by args.entropy_buffer.
+# Module strong_random fetches entropy from the buffer to generate random bit-vectors
+# and permutations.
+BUFFER_SIZE = 20000
+
+PATH_TO_BUFFER = "util/topgen/entropy_buffer.txt"
+
def ipgen_render(template_name: str, topname: str, params: Dict,
out_path: Path):
@@ -899,20 +911,6 @@
log.info("Detected crossbars: %s" %
(", ".join([x["name"] for x in xbar_objs])))
- # If specified, override the seed for random netlist constant computation.
- if args.rnd_cnst_seed:
- log.warning("Commandline override of rnd_cnst_seed with {}.".format(
- args.rnd_cnst_seed))
- topcfg["rnd_cnst_seed"] = args.rnd_cnst_seed
- # Otherwise, we either take it from the top_{topname}.hjson if present, or
- # randomly generate a new seed if not.
- else:
- random.seed()
- new_seed = random.getrandbits(64)
- if topcfg.setdefault("rnd_cnst_seed", new_seed) == new_seed:
- log.warning(
- "No rnd_cnst_seed specified, setting to {}.".format(new_seed))
-
topcfg, error = validate_top(topcfg, ip_objs, xbar_objs)
if error != 0:
raise SystemExit("Error occured while validating top.hjson")
@@ -1038,6 +1036,9 @@
type=int,
metavar="<seed>",
help="Custom seed for RNG to compute netlist constants.")
+ parser.add_argument(
+ "--entropy_buffer",
+ help="A file with entropy.")
# Miscellaneous: only return the list of blocks and exit.
parser.add_argument("--get_blocks",
default=False,
@@ -1086,6 +1087,32 @@
except ValueError:
raise SystemExit(sys.exc_info()[1])
+ # Initialize RNG for compile-time netlist constants.
+ if args.entropy_buffer:
+ if args.rnd_cnst_seed:
+ log.error("'entropy_buffer' option cannot be used with 'rnd_cnst_seed option'")
+ # error out
+ raise SystemExit(sys.exc_info()[1])
+ else:
+ # generate entropy from a buffer
+ strong_random.load(SRCTREE_TOP / args.entropy_buffer)
+ else:
+ # If specified, override the seed for random netlist constant computation.
+ if args.rnd_cnst_seed:
+ log.warning("Commandline override of rnd_cnst_seed with {}.".format(
+ args.rnd_cnst_seed))
+ topcfg["rnd_cnst_seed"] = args.rnd_cnst_seed
+ # Otherwise, we either take it from the top_{topname}.hjson if present, or
+ # randomly generate a new seed if not.
+ else:
+ random.seed()
+ new_seed = random.getrandbits(64)
+ if topcfg.setdefault("rnd_cnst_seed", new_seed) == new_seed:
+ log.warning(
+ "No rnd_cnst_seed specified, setting to {}.".format(new_seed))
+ ebg.gen_buffer(BUFFER_SIZE, SRCTREE_TOP / PATH_TO_BUFFER, False, topcfg["rnd_cnst_seed"])
+ strong_random.load(SRCTREE_TOP / PATH_TO_BUFFER)
+
# TODO, long term, the levels of dependency should be automatically determined instead
# of hardcoded. The following are a few examples:
# Example 1: pinmux depends on amending all modules before calculating the correct number of
@@ -1134,6 +1161,11 @@
topname = topcfg["name"]
+ if not args.entropy_buffer:
+ # Delete entropy buffer since it is no longer needed.
+ # This buffer can always be re-generated from the seed using entropy_buffer_generator
+ os.remove(SRCTREE_TOP / PATH_TO_BUFFER)
+
# Create the chip-level RAL only
if args.top_ral:
# See above: we only need `completeconfig` and `name_to_block`, not all
@@ -1165,7 +1197,15 @@
genhjson_path = genhjson_dir / ("top_%s.gen.hjson" % completecfg["name"])
# Header for HJSON
- gencmd = """//
+ if args.entropy_buffer:
+ gencmd = """//
+// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\
+// -o hw/top_{topname}/ \\
+// --hjson-only \\
+// --entropy-buffer {path}
+""".format(topname=topname, path = args.entropy_buffer)
+ else:
+ gencmd = """//
// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\
// -o hw/top_{topname}/ \\
// --hjson-only \\
@@ -1187,11 +1227,18 @@
fout.write(template_contents)
# Header for SV files
- gencmd = warnhdr + """//
+ if args.entropy_buffer:
+ gencmd = warnhdr + """//
+// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\
+// -o hw/top_{topname}/ \\
+// --entropy-buffer {path}
+""".format(topname=topname, path = args.entropy_buffer)
+ else:
+ gencmd = warnhdr + """//
// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\
// -o hw/top_{topname}/ \\
// --rnd_cnst_seed {seed}
-""".format(topname=topname, seed=topcfg["rnd_cnst_seed"])
+""".format(topname=topname, seed=completecfg["rnd_cnst_seed"])
# SystemVerilog Top:
# "toplevel.sv.tpl" -> "rtl/autogen/top_{topname}.sv"
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index 695a8ff..547b20a 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -3,14 +3,13 @@
# SPDX-License-Identifier: Apache-2.0
import logging as log
-import random
import re
from collections import OrderedDict
from copy import deepcopy
from math import ceil, log2
from typing import Dict, List, Union, Tuple
-from topgen import c, lib
+from topgen import c, lib, strong_random
from .clocks import Clocks
from .resets import Resets
from reggen.ip_block import IpBlock
@@ -20,7 +19,7 @@
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 = hex(strong_random.getrandbits(width))
return literal_str
@@ -30,7 +29,7 @@
num_elements = int(numel)
width = int(ceil(log2(num_elements)))
idx = [x for x in range(num_elements)]
- random.shuffle(idx)
+ strong_random.shuffle(idx)
literal_str = ""
for k in idx:
literal_str += format(k, '0' + str(width) + 'b')
@@ -48,8 +47,6 @@
more details of what gets added.
'''
- # Initialize RNG for compile-time netlist constants.
- random.seed(int(top['rnd_cnst_seed']))
for instance in top['module']:
block = name_to_block[instance['type']]
diff --git a/util/topgen/validate.py b/util/topgen/validate.py
index 0a764ec..e9a741c 100644
--- a/util/topgen/validate.py
+++ b/util/topgen/validate.py
@@ -45,7 +45,6 @@
'memory': ['l', 'list of memories. At least one memory '
'is needed to run the software'],
'xbar': ['l', 'List of the xbar used in the top'],
- 'rnd_cnst_seed': ['int', "Seed for random netlist constant computation"],
'pinout': ['g', 'Pinout configuration'],
'targets': ['l', ' Target configurations'],
'pinmux': ['g', 'pinmux configuration'],
@@ -66,7 +65,8 @@
'interrupt_module': ['l', 'list of the modules that connects to rv_plic'],
'num_cores': ['pn', "number of computing units"],
'power': ['g', 'power domains supported by the design'],
- 'port': ['g', 'assign special attributes to specific ports']
+ 'port': ['g', 'assign special attributes to specific ports'],
+ 'rnd_cnst_seed': ['int', "Seed for random netlist constant computation"]
}
top_added = {}