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'
|
author = 'Stefan Holst'
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = '0.0.2'
|
release = '0.0.3'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ Simulators
|
|||||||
Logic Simulation - :mod:`kyupy.logic_sim`
|
Logic Simulation - :mod:`kyupy.logic_sim`
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: kyupy.logic_sim
|
||||||
|
|
||||||
.. autoclass:: kyupy.logic_sim.LogicSim
|
.. autoclass:: kyupy.logic_sim.LogicSim
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,25 @@ import gzip
|
|||||||
import numpy as np
|
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:
|
class Log:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.start = time.perf_counter()
|
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 math
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from . import logic
|
from . import logic, hr_bytes
|
||||||
|
|
||||||
|
|
||||||
class LogicSim:
|
class LogicSim:
|
||||||
"""A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic.
|
"""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]
|
assert m in [2, 4, 8]
|
||||||
self.m = m
|
self.m = m
|
||||||
mdim = math.ceil(math.log2(m))
|
mdim = math.ceil(math.log2(m))
|
||||||
@@ -40,8 +55,15 @@ class LogicSim:
|
|||||||
raise ValueError(f'Unknown node kind {n.kind}')
|
raise ValueError(f'Unknown node kind {n.kind}')
|
||||||
self.node_fct.append(fcts[0])
|
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):
|
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'):
|
if hasattr(stimuli, 'data'):
|
||||||
stimuli = stimuli.data
|
stimuli = stimuli.data
|
||||||
for stim, node in zip(stimuli, self.interface):
|
for stim, node in zip(stimuli, self.interface):
|
||||||
@@ -61,16 +83,37 @@ class LogicSim:
|
|||||||
self.state_epoch[line.reader.index] = self.epoch
|
self.state_epoch[line.reader.index] = self.epoch
|
||||||
|
|
||||||
def capture(self, responses):
|
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'):
|
if hasattr(responses, 'data'):
|
||||||
responses = responses.data
|
responses = responses.data
|
||||||
for resp, node in zip(responses, self.interface):
|
for resp, node in zip(responses, self.interface):
|
||||||
if len(node.ins) == 0: continue
|
if len(node.ins) == 0: continue
|
||||||
resp[...] = self.state[node.ins[0].index]
|
resp[...] = self.state[node.ins[0].index]
|
||||||
# print(responses)
|
|
||||||
|
|
||||||
def propagate(self, inject_cb=None):
|
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():
|
for node in self.circuit.topological_order():
|
||||||
if self.state_epoch[node.index] != self.epoch: continue
|
if self.state_epoch[node.index] != self.epoch: continue
|
||||||
inputs = [self.state[line.index] if line else self.zero for line in node.ins]
|
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.
|
"""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.
|
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).
|
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
|
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
|
import numpy as np
|
||||||
|
|
||||||
from . import numba
|
from . import numba, cuda, hr_bytes
|
||||||
from . import cuda
|
|
||||||
|
|
||||||
|
|
||||||
TMAX = np.float32(2 ** 127) # almost np.PINF for 32-bit floating point values
|
TMAX = np.float32(2 ** 127) # almost np.PINF for 32-bit floating point values
|
||||||
@@ -253,6 +252,12 @@ class WaveSim:
|
|||||||
m0 = ~m1
|
m0 = ~m1
|
||||||
self.mask = np.rollaxis(np.vstack((m0, m1)), 1)
|
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):
|
def get_line_delay(self, line, polarity):
|
||||||
return self.timing[line, 0, polarity]
|
return self.timing[line, 0, polarity]
|
||||||
|
|
||||||
@@ -283,10 +288,7 @@ class WaveSim:
|
|||||||
self.state[ppi_loc + toggle, p] = TMAX
|
self.state[ppi_loc + toggle, p] = TMAX
|
||||||
|
|
||||||
def propagate(self, sims=None, sd=0.0, seed=1):
|
def propagate(self, sims=None, sd=0.0, seed=1):
|
||||||
if sims is None:
|
sims = min(sims or self.sims, self.sims)
|
||||||
sims = self.sims
|
|
||||||
else:
|
|
||||||
sims = min(sims, self.sims)
|
|
||||||
for op_start, op_stop in zip(self.level_starts, self.level_stops):
|
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.overflows += level_eval(self.ops, op_start, op_stop, self.state, self.sat, 0, sims,
|
||||||
self.timing, sd, seed)
|
self.timing, sd, seed)
|
||||||
@@ -559,6 +561,13 @@ class WaveSimCuda(WaveSim):
|
|||||||
|
|
||||||
self._block_dim = (32, 16)
|
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):
|
def get_line_delay(self, line, polarity):
|
||||||
return self.d_timing[line, 0, polarity]
|
return self.d_timing[line, 0, polarity]
|
||||||
|
|
||||||
@@ -586,10 +595,7 @@ class WaveSimCuda(WaveSim):
|
|||||||
return gx, gy
|
return gx, gy
|
||||||
|
|
||||||
def propagate(self, sims=None, sd=0.0, seed=1):
|
def propagate(self, sims=None, sd=0.0, seed=1):
|
||||||
if sims is None:
|
sims = min(sims or self.sims, self.sims)
|
||||||
sims = self.sims
|
|
||||||
else:
|
|
||||||
sims = min(sims, self.sims)
|
|
||||||
for op_start, op_stop in zip(self.level_starts, self.level_stops):
|
for op_start, op_stop in zip(self.level_starts, self.level_stops):
|
||||||
grid_dim = self._grid_dim(sims, op_stop - op_start)
|
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),
|
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