diff --git a/src/kyupy/__init__.py b/src/kyupy/__init__.py index 2ed27f8..89ee51a 100644 --- a/src/kyupy/__init__.py +++ b/src/kyupy/__init__.py @@ -81,6 +81,7 @@ def hr_time(seconds): def batchrange(nitems, maxsize): + assert nitems >= maxsize for offset in range(0, nitems, maxsize): yield offset, min(nitems-offset, maxsize) diff --git a/src/kyupy/logic_sim.py b/src/kyupy/logic_sim.py index dc8c771..92e2488 100644 --- a/src/kyupy/logic_sim.py +++ b/src/kyupy/logic_sim.py @@ -11,18 +11,18 @@ import math import numpy as np from . import numba, logic, hr_bytes, sim +from .circuit import Circuit class LogicSim(sim.SimOps): """A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic. :param circuit: The circuit to simulate. - :type circuit: :py:class:`~kyupy.circuit.Circuit` :param sims: The number of parallel logic simulations to perform. - :type sims: int :param m: The arity of the logic, must be 2, 4, or 8. - :type m: int + :param c_reuse: If True, intermediate signal values may get overwritten when not needed anymore. + :param strip_forks: If True, forks are not included in the simulation model. """ - def __init__(self, circuit, sims=8, m=8, c_reuse=False, strip_forks=False): + def __init__(self, circuit: Circuit, sims: int = 8, m: int = 8, c_reuse: bool = False, strip_forks: bool = False): assert m in [2, 4, 8] super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks) self.m = m @@ -32,26 +32,25 @@ class LogicSim(sim.SimOps): self.c = np.zeros((self.c_len, self.mdim, nbytes), dtype=np.uint8) self.s = np.zeros((2, self.s_len, 3, nbytes), dtype=np.uint8) + """Logic values of the sequential elements (flip-flops) and ports. + + The elements are as follows: + + * ``s[0]`` Assigned values. Simulator will read (P)PI value from here. + * ``s[1]`` Result values. Simulator will write (P)PO values here. + """ self.s[:,:,1,:] = 255 # unassigned def __repr__(self): - return f'' + return f'{{name: "{self.circuit.name}", sims: {self.sims}, m: {self.m}, c_bytes: {self.c.nbytes}}}' def s_to_c(self): - """Assign stimuli to the primary inputs and state-elements (flip-flops). - - :param stimuli: The input data to assign. Must be in bit-parallel storage format and in a compatible shape. - :type stimuli: :py:class:`~kyupy.logic.BPArray` - :returns: The given stimuli object. + """Copies the values from ``s[0]`` the inputs of the combinational portion. """ self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs, :self.mdim] def c_to_s(self): - """Capture the current values at the primary outputs and in the state-elements (flip-flops). - - For primary outputs, the logic value is stored unmodified in the given target array. - For flip-flops, the logic value is either stored unmodified (`ff_transitions=False`) - or constructed from the previous state and the new state (`ff_transitions=True`). + """Copies the results of the combinational portion to ``s[1]``. """ self.s[1, self.poppo_s_locs, :self.mdim] = self.c[self.poppo_c_locs] if self.mdim == 1: @@ -103,22 +102,27 @@ class LogicSim(sim.SimOps): if inject_cb is not None: inject_cb(o0, self.s[o0]) def s_ppo_to_ppi(self): + """Constructs a new assignment based on the current data in ``s``. + + Use this function for simulating consecutive clock cycles. + + For 2-valued or 4-valued simulations, all valued from PPOs (in ``s[1]``) and copied to the PPIs (in ``s[0]). + For 8-valued simulations, PPI transitions are constructed from the initial values of the assignment and the + final values of the results. + """ # TODO: handle latches correctly - if self.m == 2: - self.s[0, self.ppio_s_locs, 0] = self.s[1, self.ppio_s_locs, 0] + if self.mdim < 3: + self.s[0, self.ppio_s_locs] = self.s[1, self.ppio_s_locs] else: self.s[0, self.ppio_s_locs, 1] = self.s[0, self.ppio_s_locs, 0] # initial value is previously assigned final value self.s[0, self.ppio_s_locs, 0] = self.s[1, self.ppio_s_locs, 0] # final value is newly captured final value self.s[0, self.ppio_s_locs, 2] = self.s[0, self.ppio_s_locs, 0] ^ self.s[0, self.ppio_s_locs, 1] # TODO: not correct for X, - - def cycle(self, cycles=1, inject_cb=None): + def cycle(self, cycles: int = 1, inject_cb=None): """Assigns the given state, propagates it and captures the new state. - :param state: A bit-parallel array in a compatible shape holding the current circuit state. - The contained data is assigned to the PI and PPI and overwritten by data at the PO and PPO after - propagation. - :type state: :py:class:`~kyupy.logic.BPArray` - :param inject_cb: A callback function for manipulating intermediate signal values. See :py:func:`propagate`. + :param cycles: The number of cycles to simulate. + :param inject_cb: A callback function for manipulating intermediate signal values. See :py:func:`c_prop`. :returns: The given state object. """ for _ in range(cycles): diff --git a/src/kyupy/wave_sim.py b/src/kyupy/wave_sim.py index 1e554c4..c34540b 100644 --- a/src/kyupy/wave_sim.py +++ b/src/kyupy/wave_sim.py @@ -62,6 +62,7 @@ class WaveSim(sim.SimOps): The remaining values are written by ``c_to_s()``. The elements are as follows: + * ``s[0]`` (P)PI initial value * ``s[1]`` (P)PI transition time * ``s[2]`` (P)PI final value @@ -75,6 +76,7 @@ class WaveSim(sim.SimOps): * ``s[10]`` Overflow indicator: If non-zero, some signals in the input cone of this output had more transitions than specified in ``c_caps``. Some transitions have been discarded, the final values in the waveforms are still valid. + """ self.simctl_int = np.zeros((2, sims), dtype=np.int32)