from __future__ import annotations
import random
from collections.abc import Callable
import pyrtl
def calculate_max_and_min_bitwidths(max_bitwidth=None, exact_bitwidth=None):
if max_bitwidth is not None:
min_bitwidth = 1
elif exact_bitwidth is not None:
min_bitwidth = max_bitwidth = exact_bitwidth
else:
msg = "A max or exact bitwidth must be specified"
raise pyrtl.PyrtlError(msg)
return min_bitwidth, max_bitwidth
def inverse_power_dist(bitwidth):
# Note that this is not uniformly distributed
return int(2 ** random.uniform(0, bitwidth) - 1)
def uniform_dist(bitwidth):
return random.randrange(2**bitwidth)
# deprecated name
generate_in_wire_and_values = an_input_and_vals
[docs]
def make_consts(
num_wires: int,
max_bitwidth: int | None = None,
exact_bitwidth: int | None = None,
random_dist: Callable[[int], int] = inverse_power_dist,
) -> tuple[list[pyrtl.Const], list[int]]:
"""Generate random :class:`.Const` values.
:param num_wires: Number of :class:`Consts<.Const>` to generate.
:param max_bitwidth: If specified, generate :class:`Consts<.Const>` with random
:attr:`~.WireVector.bitwidth` in the range ``[1, max_bitwidth)``.
:param exact_bitwidth: If specified, generate :class:`Consts<.Const>` with
:attr:`~.WireVector.bitwidth` ``exact_bitwidth``.
:param random_dist: Function to generate the random :class:`.Const` values.
:return: ``(consts, values)``, where ``consts`` is a :class:`list` of
:class:`Consts<.Const>`, and ``values`` is a list of each ``const``'s
value.
"""
min_bitwidth, max_bitwidth = calculate_max_and_min_bitwidths(
max_bitwidth, exact_bitwidth
)
bitwidths = [
random.randrange(min_bitwidth, max_bitwidth + 1) for i in range(num_wires)
]
wires = [pyrtl.Const(random_dist(b), b) for b in bitwidths]
vals = [w.val for w in wires]
return wires, vals
def sim_and_ret_out(
outwire: pyrtl.WireVector, inwires: list[pyrtl.WireVector], invals: list[list[int]]
) -> list[int]:
"""Run a simulation with ``invals`` for ``inwires`` and return ``outwire``'s values.
.. WARNING::
Use :meth:`.Simulation.step_multiple` instead::
sim = pyrtl.Simulation()
sim.step_multiple(provided_inputs=dict(zip(inwires, invals)))
output = sim.tracer.trace[outwire.name]
:param outwire: The wire to return the values of in each simulation cycle.
:param inwires: A list of :class:`.Input` wires to provide values for.
:param invals: A list of :class:`.Input` value lists.
:return: A list of ``outwire``'s values in each simulation cycle.
"""
# Pulling the value of outwire straight from the log
return sim_and_ret_outws(inwires, invals)[outwire.name]
def sim_and_ret_outws(
inwires: list[pyrtl.WireVector], invals: list[list[int]]
) -> dict[str, list[int]]:
"""Run a simulation with ``invals`` for ``inwires`` and return all wire values.
.. WARNING::
Use :meth:`.Simulation.step_multiple` instead::
sim = pyrtl.Simulation()
sim.step_multiple(provided_inputs=dict(zip(inwires, invals)))
outputs = sim.tracer.trace
:param inwires: A list of :class:`.Input` wires to provide values for.
:param invals: A list of :class:`.Input` value lists.
:return: A :class:`dict` mapping from a :class:`WireVector`'s name to a
:class:`list` of its values in each cycle.
"""
sim = pyrtl.Simulation()
sim.step_multiple(provided_inputs=dict(zip(inwires, invals, strict=True)))
return sim.tracer.trace
def sim_multicycle(in_dict, hold_dict, hold_cycles, sim=None):
if sim is None:
sim = pyrtl.Simulation()
sim.step(in_dict)
for _i in range(hold_cycles):
sim.step(hold_dict)
return sim.tracer.trace[-1]
def multi_sim_multicycle(in_dict, hold_dict, hold_cycles, sim=None):
if sim is None:
sim = pyrtl.Simulation()
cycles = len(next(iter(in_dict.values())))
for cycle in range(cycles):
current_dict = {wire: values[cycle] for wire, values in in_dict}
cur_result = sim_multicycle(current_dict, hold_dict, hold_cycles, sim)
if cycle == 0:
results = {wire: [result_val] for wire, result_val in cur_result}
else:
for wire, result_val in cur_result:
results[wire].append(result_val)
return results