Browse Source

more docs and reprs

devel
Stefan Holst 4 years ago
parent
commit
18c17b5f76
  1. 2
      docs/conf.py
  2. 2
      docs/simulators.rst
  3. 19
      src/kyupy/__init__.py
  4. 55
      src/kyupy/logic_sim.py
  5. 28
      src/kyupy/wave_sim.py

2
docs/conf.py

@ -24,7 +24,7 @@ copyright = '2020, Stefan Holst' @@ -24,7 +24,7 @@ copyright = '2020, Stefan Holst'
author = 'Stefan Holst'
# The full version, including alpha/beta/rc tags
release = '0.0.2'
release = '0.0.3'
# -- General configuration ---------------------------------------------------

2
docs/simulators.rst

@ -4,6 +4,8 @@ Simulators @@ -4,6 +4,8 @@ Simulators
Logic Simulation - :mod:`kyupy.logic_sim`
-----------------------------------------
.. automodule:: kyupy.logic_sim
.. autoclass:: kyupy.logic_sim.LogicSim
:members:

19
src/kyupy/__init__.py

@ -10,6 +10,25 @@ import gzip @@ -10,6 +10,25 @@ import gzip
import numpy as np
def hr_sci(value):
multiplier = 0
while abs(value) >= 1000:
value /= 1000
multiplier += 1
while abs(value) < 1:
value *= 1000
multiplier -= 1
return f'{value:.3f}{" kMGTafpnµm"[multiplier]}'
def hr_bytes(nbytes):
multiplier = 0
while abs(nbytes) >= 1000:
nbytes /= 1024
multiplier += 1
return f'{nbytes:.1f}{["", "ki", "Mi", "Gi", "Ti", "Pi"][multiplier]}B'
class Log:
def __init__(self):
self.start = time.perf_counter()

55
src/kyupy/logic_sim.py

@ -1,14 +1,29 @@ @@ -1,14 +1,29 @@
"""A High-Throughput combinational logic simulator.
The class :py:class:`~kyupy.logic_sim.LogicSim` performs parallel simulations of the combinational part of a circuit.
The logic operations are performed bit-parallel on packed numpy arrays.
Simple sequential circuits can be simulated by repeated assignments and propagations.
However, this simulator ignores the clock network and simply assumes that all state-elements are clocked all the time.
"""
import math
import numpy as np
from . import logic
from . import logic, hr_bytes
class LogicSim:
"""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
"""
def __init__(self, circuit, sims=1, m=8):
def __init__(self, circuit, sims=8, m=8):
assert m in [2, 4, 8]
self.m = m
mdim = math.ceil(math.log2(m))
@ -40,8 +55,15 @@ class LogicSim: @@ -40,8 +55,15 @@ class LogicSim:
raise ValueError(f'Unknown node kind {n.kind}')
self.node_fct.append(fcts[0])
def __repr__(self):
return f'<LogicSim {self.circuit.name} sims={self.sims} m={self.m} state_mem={hr_bytes(self.state.nbytes)}>'
def assign(self, stimuli):
"""Assign stimuli to the primary inputs and state-elements (flip-flops)."""
"""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`
"""
if hasattr(stimuli, 'data'):
stimuli = stimuli.data
for stim, node in zip(stimuli, self.interface):
@ -61,16 +83,37 @@ class LogicSim: @@ -61,16 +83,37 @@ class LogicSim:
self.state_epoch[line.reader.index] = self.epoch
def capture(self, responses):
"""Capture the current values at the primary outputs and in the state-elements (flip-flops)."""
"""Capture the current values at the primary outputs and in the state-elements (flip-flops).
:param responses: A bit-parallel storage target for the responses in a compatible shape.
:type responses: :py:class:`~kyupy.logic.BPArray`
"""
if hasattr(responses, 'data'):
responses = responses.data
for resp, node in zip(responses, self.interface):
if len(node.ins) == 0: continue
resp[...] = self.state[node.ins[0].index]
# print(responses)
def propagate(self, inject_cb=None):
"""Propagate the input values towards the outputs (Perform all logic operations in topological order)."""
"""Propagate the input values towards the outputs (Perform all logic operations in topological order).
If the circuit is sequential (it contains flip-flops), one call simulates one clock cycle.
Multiple clock cycles are simulated by a assign-propagate-capture loop:
.. code-block:: python
# initial state in state_bp
for cycle in range(10): # simulate 10 clock cycles
sim.assign(state_bp)
sim.propagate()
sim.capture(state_bp)
:param inject_cb: A callback function for manipulating intermediate signal values.
This function is called with a line index and its new logic values (in bit-parallel format) after
evaluation of a node. The callback may manipulate the given values in-place, the simulation
resumes with the manipulated values after the callback returns.
:type inject_cb: ``f(int, ndarray)``
"""
for node in self.circuit.topological_order():
if self.state_epoch[node.index] != self.epoch: continue
inputs = [self.state[line.index] if line else self.zero for line in node.ins]

28
src/kyupy/wave_sim.py

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
"""High-Throughput combinational logic timing simulators.
These simulators work similarly to :py:class:`kyupy.logic_sim.LogicSim`.
These simulators work similarly to :py:class:`~kyupy.logic_sim.LogicSim`.
They propagate values through the combinational circuit from (pseudo) primary inputs to (pseudo) primary outputs.
Instead of propagating logic values, these simulators propagate signal histories (waveforms).
They are designed to run many simulations in parallel and while their latencies are quite high, they achieve
@ -17,8 +17,7 @@ from bisect import bisect, insort_left @@ -17,8 +17,7 @@ from bisect import bisect, insort_left
import numpy as np
from . import numba
from . import cuda
from . import numba, cuda, hr_bytes
TMAX = np.float32(2 ** 127) # almost np.PINF for 32-bit floating point values
@ -253,6 +252,12 @@ class WaveSim: @@ -253,6 +252,12 @@ class WaveSim:
m0 = ~m1
self.mask = np.rollaxis(np.vstack((m0, m1)), 1)
def __repr__(self):
total_mem = self.state.nbytes + self.sat.nbytes + self.ops.nbytes + self.cdata.nbytes
return f'<WaveSim {self.circuit.name} sims={self.sims} ops={len(self.ops)} ' + \
f'levels={len(self.level_starts)} state_mem={hr_bytes(self.state.nbytes)} ' + \
f'total_mem={hr_bytes(total_mem)}>'
def get_line_delay(self, line, polarity):
return self.timing[line, 0, polarity]
@ -283,10 +288,7 @@ class WaveSim: @@ -283,10 +288,7 @@ class WaveSim:
self.state[ppi_loc + toggle, p] = TMAX
def propagate(self, sims=None, sd=0.0, seed=1):
if sims is None:
sims = self.sims
else:
sims = min(sims, self.sims)
sims = min(sims or self.sims, self.sims)
for op_start, op_stop in zip(self.level_starts, self.level_stops):
self.overflows += level_eval(self.ops, op_start, op_stop, self.state, self.sat, 0, sims,
self.timing, sd, seed)
@ -559,6 +561,13 @@ class WaveSimCuda(WaveSim): @@ -559,6 +561,13 @@ class WaveSimCuda(WaveSim):
self._block_dim = (32, 16)
def __repr__(self):
total_mem = self.state.nbytes + self.sat.nbytes + self.ops.nbytes + self.timing.nbytes + \
self.tdata.nbytes + self.cdata.nbytes
return f'<WaveSimCuda {self.circuit.name} sims={self.sims} ops={len(self.ops)} ' + \
f'levels={len(self.level_starts)} state_mem={hr_bytes(self.state.nbytes)} ' + \
f'total_mem={hr_bytes(total_mem)}>'
def get_line_delay(self, line, polarity):
return self.d_timing[line, 0, polarity]
@ -586,10 +595,7 @@ class WaveSimCuda(WaveSim): @@ -586,10 +595,7 @@ class WaveSimCuda(WaveSim):
return gx, gy
def propagate(self, sims=None, sd=0.0, seed=1):
if sims is None:
sims = self.sims
else:
sims = min(sims, self.sims)
sims = min(sims or self.sims, self.sims)
for op_start, op_stop in zip(self.level_starts, self.level_stops):
grid_dim = self._grid_dim(sims, op_stop - op_start)
wave_kernel[grid_dim, self._block_dim](self.d_ops, op_start, op_stop, self.d_state, self.sat, int(0),

Loading…
Cancel
Save