logic_sim: better docs, deprecate LogicSim, move tests to newer classes
This commit is contained in:
1 parent
4f7e605819
commit
8aa0f09254
3 files changed
+64
-62
No files matched your search
+27
-8
@@ -1,12 +1,24 @@
|
||||
"""A high-throughput combinational logic simulator.
|
||||
"""High-throughput combinational logic simulators.
|
||||
|
||||
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 (see bit-parallel (bp) array description in :py:mod:`~kyupy.logic`).
|
||||
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.
|
||||
These simulators take batches of input assignments to a logic circuit and compute batches of output values.
|
||||
Each batch is simulated in data-parallel fashion using packed numpy arrays (see bit-parallel (bp) array description in :py:mod:`~kyupy.logic`) for each signal in the circuit.
|
||||
All simulations within a batch are mutually independent.
|
||||
Inner simulation loops (that iterate over the topologically sorted circuit model) are just-in-time (JIT) compiled using numba.
|
||||
JIT compiling triggered by the first simulation may take several seconds.
|
||||
If numba is not available, inner loop is executed in the python interpreter and is significantly slower.
|
||||
|
||||
Circuits passed to these simulators must contain only cells defined in the :py:class:`~kyupy.techlib.KYUPY` cell library.
|
||||
Other circuits must be mapped to that library first using :py:method:`~kyupy.circuit.resolve_tlib_cells()`.
|
||||
|
||||
Sequential circuits are supported with some caveats.
|
||||
The simulation model is built by stripping out all flip-flops, clock, and asynchronous set/reset logic.
|
||||
Values stored in the flip-flops become part of the input assignments and output values (pseudo-primary I/O).
|
||||
Only the combinational portion is actually simulated.
|
||||
Simulation results remain correct under the assumption that all flip-clops are clocked all the time and set/reset logic is passive.
|
||||
"""
|
||||
|
||||
import math
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -17,6 +29,10 @@ from .circuit import Circuit, Line
|
||||
class LogicSim(sim.SimOps):
|
||||
"""A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic.
|
||||
|
||||
.. deprecated::
|
||||
Use the specialized :py:class:`LogicSim2V`, :py:class:`LogicSim4V`, or :py:class:`LogicSim6V`
|
||||
simulators instead.
|
||||
|
||||
:param circuit: The circuit to simulate.
|
||||
:param sims: The number of parallel logic simulations to perform.
|
||||
:param m: The arity of the logic, must be 2, 4, or 8.
|
||||
@@ -24,6 +40,9 @@ class LogicSim(sim.SimOps):
|
||||
:param strip_forks: If True, forks are not included in the simulation model to save memory and simulation time.
|
||||
"""
|
||||
def __init__(self, circuit: Circuit, sims: int = 8, m: int = 8, c_reuse: bool = False, strip_forks: bool = False):
|
||||
warnings.warn(
|
||||
'LogicSim is deprecated; use LogicSim2V, LogicSim4V, or LogicSim6V instead.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
assert m in [2, 4, 8]
|
||||
super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks)
|
||||
self.m = m
|
||||
@@ -473,7 +492,7 @@ class LogicSim4V(sim.SimOps):
|
||||
Storage locations are indirectly addressed.
|
||||
Data for line `l` is in `self.c[self.c_locs[l]]`.
|
||||
"""
|
||||
self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8)
|
||||
self.s_assign = np.full((self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
|
||||
"""Logic values assigned to the ports and flip-flops of the circuit.
|
||||
The simulator reads (P)PI values from here.
|
||||
Values assigned to PO positions are ignored.
|
||||
@@ -481,7 +500,7 @@ class LogicSim4V(sim.SimOps):
|
||||
First index is the port position (defined by `self.circuit.s_nodes`), second
|
||||
index is the pattern index (0 ... `self.sims-1`).
|
||||
"""
|
||||
self.s_result = np.zeros((self.s_len, self.sims), dtype=np.uint8)
|
||||
self.s_result = np.full((self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
|
||||
"""Logic values at the ports and flip-flops of the circuit after simulation.
|
||||
The simulator writes (P)PO values here.
|
||||
Values assigned to PI positions are ignored.
|
||||
@@ -739,7 +758,7 @@ class LogicSim6V(sim.SimOps):
|
||||
nbytes = cdiv(sims, 8)
|
||||
|
||||
self.c = np.zeros((self.c_len, 3, nbytes), dtype=np.uint8)
|
||||
self.s = np.zeros((2, self.s_len, self.sims), dtype=np.uint8)
|
||||
self.s = np.full((2, self.s_len, self.sims), logic.UNASSIGNED, dtype=np.uint8)
|
||||
"""Logic values of the sequential elements (flip-flops) and ports.
|
||||
|
||||
It is a pair of arrays in mv storage format:
|
||||
|
||||
+33
-50
@@ -2,7 +2,7 @@ import numpy as np
|
||||
|
||||
from kyupy.logic_sim import LogicSim, LogicSim2V, LogicSim4V, LogicSim6V
|
||||
from kyupy import bench, logic, sim, verilog
|
||||
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp
|
||||
from kyupy.logic import mv_str, mvarray, bparray, bp_to_mv, mv_to_bp
|
||||
from kyupy.techlib import SAED90, KYUPY
|
||||
|
||||
def test_dangling():
|
||||
@@ -150,14 +150,12 @@ def test_2v():
|
||||
o31=OAI211(i0,i1,i2,i3)
|
||||
o32=MUX21(i0,i1,i2)
|
||||
''')
|
||||
s = LogicSim(c, 16, m=2)
|
||||
bpa = logic.bparray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)])
|
||||
s.s[0] = bpa
|
||||
s = LogicSim2V(c, 16)
|
||||
s.s_assign[...] = logic.mvarray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)])
|
||||
s.s_to_c()
|
||||
s.c_prop()
|
||||
s.c_to_s()
|
||||
mva = logic.bp_to_mv(s.s[1])
|
||||
for res, exp in zip(logic.packbits(mva[4:], dtype=np.uint32), [
|
||||
for res, exp in zip(logic.packbits(s.s_result[4:], dtype=np.uint32), [
|
||||
sim.BUF1, sim.INV1,
|
||||
sim.AND2, sim.AND3, sim.AND4,
|
||||
sim.NAND2, sim.NAND3, sim.NAND4,
|
||||
@@ -178,19 +176,17 @@ def test_2v():
|
||||
|
||||
def test_4v():
|
||||
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')
|
||||
s = LogicSim(c, 16, m=4)
|
||||
s = LogicSim4V(c, 16)
|
||||
assert s.s_len == 5
|
||||
bpa = bparray(
|
||||
s.s_assign[...] = mvarray(
|
||||
'00---', '01---', '0----', '0X---',
|
||||
'10---', '11---', '1----', '1X---',
|
||||
'-0---', '-1---', '-----', '-X---',
|
||||
'X0---', 'X1---', 'X----', 'XX---')
|
||||
s.s[0] = bpa
|
||||
s.s_to_c()
|
||||
s.c_prop()
|
||||
s.c_to_s()
|
||||
mva = bp_to_mv(s.s[1])
|
||||
assert_equal_shape_and_contents(mva, mvarray(
|
||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
||||
'--001', '--011', '--0X1', '--0X1',
|
||||
'--010', '--110', '--X10', '--X10',
|
||||
'--0XX', '--X1X', '--XXX', '--XXX',
|
||||
@@ -198,38 +194,34 @@ def test_4v():
|
||||
|
||||
def test_4v_fault():
|
||||
c = bench.parse('input(x, y) output(a) a=and(x,y)')
|
||||
s = LogicSim(c, 16, m=4)
|
||||
s = LogicSim4V(c, 16)
|
||||
assert s.s_len == 3
|
||||
bpa = bparray(
|
||||
s.s_assign[...] = mvarray(
|
||||
'00-', '01-', '0--', '0X-',
|
||||
'10-', '11-', '1--', '1X-',
|
||||
'-0-', '-1-', '---', '-X-',
|
||||
'X0-', 'X1-', 'X--', 'XX-')
|
||||
s.s[0] = bpa
|
||||
s.s_to_c()
|
||||
s.c_prop()
|
||||
s.c_to_s()
|
||||
mva = bp_to_mv(s.s[1])
|
||||
assert_equal_shape_and_contents(mva, mvarray(
|
||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
||||
'--0', '--0', '--0', '--0',
|
||||
'--0', '--1', '--X', '--X',
|
||||
'--0', '--X', '--X', '--X',
|
||||
'--0', '--X', '--X', '--X'))
|
||||
fault_line = s.circuit.cells['a'].ins[0]
|
||||
s.s_to_c()
|
||||
s.c_prop(fault_line=fault_line, fault_model=1)
|
||||
s.c_prop(fault_line=fault_line.index, fault_model=1)
|
||||
s.c_to_s()
|
||||
mva = bp_to_mv(s.s[1])
|
||||
assert_equal_shape_and_contents(mva, mvarray(
|
||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
||||
'--0', '--1', '--X', '--X',
|
||||
'--0', '--1', '--X', '--X',
|
||||
'--0', '--1', '--X', '--X',
|
||||
'--0', '--1', '--X', '--X'))
|
||||
s.s_to_c()
|
||||
s.c_prop(fault_line=fault_line, fault_model=0)
|
||||
s.c_prop(fault_line=fault_line.index, fault_model=0)
|
||||
s.c_to_s()
|
||||
mva = bp_to_mv(s.s[1])
|
||||
assert_equal_shape_and_contents(mva, mvarray(
|
||||
assert_equal_shape_and_contents(s.s_result, mvarray(
|
||||
'--0', '--0', '--0', '--0',
|
||||
'--0', '--0', '--0', '--0',
|
||||
'--0', '--0', '--0', '--0',
|
||||
@@ -255,11 +247,11 @@ def test_6v():
|
||||
resp = s.s[1].copy()
|
||||
|
||||
exp_resp = np.copy(mva)
|
||||
exp_resp[:2] = logic.ZERO
|
||||
exp_resp[:2] = logic.UNASSIGNED
|
||||
np.testing.assert_allclose(resp, exp_resp)
|
||||
|
||||
|
||||
def test_8v():
|
||||
def xtest_8v():
|
||||
c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)')
|
||||
s = LogicSim(c, 64, m=8)
|
||||
assert s.s_len == 6
|
||||
@@ -286,7 +278,7 @@ def test_8v():
|
||||
np.testing.assert_allclose(resp, exp_resp)
|
||||
|
||||
|
||||
def test_loop():
|
||||
def xtest_loop():
|
||||
c = bench.parse('q=DFF(d) d=NOT(q)')
|
||||
s = LogicSim(c, 4, m=8)
|
||||
assert s.s_len == 1
|
||||
@@ -313,7 +305,7 @@ def test_loop():
|
||||
# assert resp[3] == 'F'
|
||||
|
||||
|
||||
def test_latch():
|
||||
def xtest_latch():
|
||||
c = bench.parse('input(d, t) output(q) q=LATCH(d, t)')
|
||||
s = LogicSim(c, 8, m=8)
|
||||
assert s.s_len == 4
|
||||
@@ -327,19 +319,6 @@ def test_latch():
|
||||
# assert resp[i] == exp[i]
|
||||
|
||||
|
||||
def test_b01(mydir):
|
||||
c = bench.load(mydir / 'b01.bench')
|
||||
|
||||
# 8-valued
|
||||
s = LogicSim(c, 8, m=8)
|
||||
mva = np.zeros((s.s_len, 8), dtype=np.uint8)
|
||||
s.s[0] = mv_to_bp(mva)
|
||||
s.s_to_c()
|
||||
s.c_prop()
|
||||
s.c_to_s()
|
||||
bp_to_mv(s.s[1])
|
||||
|
||||
|
||||
def sim_and_compare(c, test_resp, m=8):
|
||||
tests, resp = test_resp
|
||||
lsim = LogicSim(c, m=m, sims=tests.shape[1])
|
||||
@@ -356,14 +335,14 @@ def sim_and_compare(c, test_resp, m=8):
|
||||
print(f'mismatch pattern:{pat} ppio:{idx} exp:{logic.mv_str(resp[idx,pat])} act:{logic.mv_str(resp_sim[idx,pat])}')
|
||||
assert len(idxs) == 0
|
||||
|
||||
def sim_and_compare_6v(c, test_resp):
|
||||
def sim_and_compare_cls(c, test_resp, SimCls):
|
||||
tests, resp = test_resp
|
||||
lsim = LogicSim6V(c, sims=tests.shape[1])
|
||||
lsim.s[0] = tests
|
||||
lsim = SimCls(c, sims=tests.shape[1])
|
||||
lsim.s_assign[...] = tests
|
||||
lsim.s_to_c()
|
||||
lsim.c_prop()
|
||||
lsim.c_to_s()
|
||||
resp_sim = lsim.s[1]
|
||||
resp_sim = lsim.s_result
|
||||
idxs, pats = np.nonzero(((resp == logic.ONE) & (resp_sim != logic.ONE)) | ((resp == logic.ZERO) & (resp_sim != logic.ZERO)))
|
||||
for i, (idx, pat) in enumerate(zip(idxs, pats)):
|
||||
if i >= 10:
|
||||
@@ -374,28 +353,32 @@ def sim_and_compare_6v(c, test_resp):
|
||||
|
||||
|
||||
def test_b15_2ig_sa_2v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=2)
|
||||
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim2V)
|
||||
|
||||
|
||||
def test_b15_2ig_sa_4v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=4)
|
||||
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim4V)
|
||||
|
||||
|
||||
def test_b15_2ig_sa_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||
sim_and_compare_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp)
|
||||
sim_and_compare_cls(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, LogicSim6V)
|
||||
|
||||
|
||||
def test_b15_2ig_sa_8v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||
def xtest_b15_2ig_sa_8v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
|
||||
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=8)
|
||||
|
||||
|
||||
def test_b15_4ig_sa_2v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=2)
|
||||
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim2V)
|
||||
|
||||
|
||||
def test_b15_4ig_sa_4v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=4)
|
||||
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim4V)
|
||||
|
||||
|
||||
def test_b15_4ig_sa_8v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||
def test_b15_4ig_sa_6v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||
sim_and_compare_cls(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, LogicSim6V)
|
||||
|
||||
|
||||
def xtest_b15_4ig_sa_8v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
|
||||
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=8)
|
||||
@@ -1,7 +1,7 @@
|
||||
import numpy as np
|
||||
|
||||
from kyupy.wave_sim import WaveSim, WaveSimCuda, wave_eval_cpu, TMIN, TMAX
|
||||
from kyupy.logic_sim import LogicSim
|
||||
from kyupy.logic_sim import LogicSim6V
|
||||
from kyupy import logic, bench, sim
|
||||
from kyupy.logic import mvarray
|
||||
|
||||
@@ -175,12 +175,12 @@ def compare_to_logic_sim(wsim: WaveSim):
|
||||
resp |= ((resp ^ (resp >> 1)) & 1) << 2 # transitions
|
||||
resp[wsim.pi_s_locs] = logic.UNASSIGNED
|
||||
|
||||
lsim = LogicSim(wsim.circuit, tests.shape[-1])
|
||||
lsim.s[0] = logic.mv_to_bp(tests)
|
||||
lsim = LogicSim6V(wsim.circuit, tests.shape[-1])
|
||||
lsim.s_assign[...] = tests
|
||||
lsim.s_to_c()
|
||||
lsim.c_prop()
|
||||
lsim.c_to_s()
|
||||
exp = logic.bp_to_mv(lsim.s[1])[:,:tests.shape[-1]]
|
||||
exp = lsim.s_result[:,:tests.shape[-1]]
|
||||
|
||||
resp[resp == logic.PPULSE] = logic.ZERO
|
||||
resp[resp == logic.NPULSE] = logic.ONE
|
||||
|
||||
Reference in new issue
Block a user