more docs and reprs
This commit is contained in:
1 parent
0bad95e94e
commit
18c17b5f76
5 files changed
+88
-18
No files matched your search
+1
-1
@@ -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 ---------------------------------------------------
|
||||
|
||||
@@ -4,6 +4,8 @@ Simulators
|
||||
Logic Simulation - :mod:`kyupy.logic_sim`
|
||||
-----------------------------------------
|
||||
|
||||
.. automodule:: kyupy.logic_sim
|
||||
|
||||
.. autoclass:: kyupy.logic_sim.LogicSim
|
||||
:members:
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
+49
-6
@@ -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:
|
||||
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:
|
||||
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]
|
||||
|
||||
+17
-11
@@ -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
|
||||
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
|
||||
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):
|
||||
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),
|
||||
|
||||
Reference in new issue
Block a user