Browse Source

4v sim, fixed 6v sim, better testing

devel
stefan 2 weeks ago
parent
commit
31434cfd51
  1. 423
      src/kyupy/logic_sim.py
  2. 2
      tests/Makefile
  3. 134
      tests/all_kyupy_simprims.minimal.v
  4. 32
      tests/all_kyupy_simprims.v
  5. 76
      tests/test_logic_sim.py

423
src/kyupy/logic_sim.py

@ -11,7 +11,7 @@ import math
import numpy as np import numpy as np
from . import numba, logic, hr_bytes, sim, eng, cdiv, batchrange from . import numba, logic, hr_bytes, sim, eng, cdiv, batchrange
from .circuit import Circuit from .circuit import Circuit, Line
class LogicSim(sim.SimOps): class LogicSim(sim.SimOps):
@ -53,7 +53,7 @@ class LogicSim(sim.SimOps):
""" """
self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs, :self.mdim] self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs, :self.mdim]
def c_prop(self, sims=None, inject_cb=None, fault_line=-1, fault_mask=None, fault_model=2): def c_prop(self, sims=None, inject_cb=None, fault_line:Line|int=-1, fault_mask=None, fault_model=2):
"""Propagate the input values through the combinational circuit towards the outputs. """Propagate the input values through the combinational circuit towards the outputs.
Performs all logic operations in topological order. Performs all logic operations in topological order.
@ -78,7 +78,7 @@ class LogicSim(sim.SimOps):
t1 = self.c_locs[self.tmp2_idx] t1 = self.c_locs[self.tmp2_idx]
if self.m == 2: if self.m == 2:
if inject_cb is None: if inject_cb is None:
_prop_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model)) c_prop_2v_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model))
else: else:
for op, o0l, i0l, i1l, i2l, i3l in self.ops[:,:6]: for op, o0l, i0l, i1l, i2l, i3l in self.ops[:,:6]:
o0, i0, i1, i2, i3 = [self.c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)] o0, i0, i1, i2, i3 = [self.c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)]
@ -316,8 +316,93 @@ class LogicSim(sim.SimOps):
self.s_ppo_to_ppi() self.s_ppo_to_ppi()
class LogicSim2V(sim.SimOps):
"""A bit-parallel naïve combinational simulator for 2-valued logic.
:param circuit: The circuit to simulate.
:param sims: The number of parallel logic simulations to perform.
:param c_reuse: If True, intermediate signal values may get overwritten when not needed anymore to save memory.
:param strip_forks: If True, forks are not included in the simulation model to save memory and simulation time.
Caveat: faults on fanout branches will not be injected if forks are stripped.
"""
def __init__(self, circuit: Circuit, sims: int = 8, c_reuse: bool = False, strip_forks: bool = False):
super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks)
self.sims = sims
nbytes = cdiv(sims, 8)
self.c = np.zeros((self.c_len, 1, nbytes), dtype=np.uint8)
"""Logic values within the combinational portion of the circuit.
In bit-parallel (bp) storage format.
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)
"""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.
This field is a 2-dimensional array and expects values in the mv storage format.
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)
"""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.
This field is a 2-dimensional array and expects values in the mv storage format.
First index is the port position (defined by `self.circuit.s_nodes`), second index is the pattern index (0 ... `self.sims-1`).
"""
self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8)
def __repr__(self):
return f'{{name: "{self.circuit.name}", sims: {self.sims}, c_bytes: {eng(self.c.nbytes)}}}'
def s_to_c(self):
"""Assigns the values from ``self.s_assign`` to the inputs of the combinational portion.
"""
self.c[self.pippi_c_locs] = logic.mv_to_bp(self.s_assign[self.pippi_s_locs])[:,:1,:]
def c_prop(self, fault_line=-1, fault_model=2, fault_mask=None):
if fault_mask is None:
fault_mask = self._full_mask # default: full mask
else:
if len(fault_mask) < self.c.shape[-1]: # pad mask with 0's if necessary
fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8)
fault_mask2[:len(fault_mask)] = fault_mask
fault_mask = fault_mask2
c_prop_2v_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model))
def c_to_s(self):
"""Captures the results of the combinational portion into ``self.s_result``.
"""
self.s_result[self.poppo_s_locs] = logic.bp_to_mv(self.c[self.poppo_c_locs])[:,:self.sims] * logic.ONE
def c_ppo_to_ppi(self):
"""Copies the result data for all PPOs (flip-flops) to PPIs for a next simulation cycle.
"""
self.c[self.ppi_c_locs] = self.c[self.ppo_c_locs].copy() # copy prevents undefined behavior if (P)PIs are connected directly to (P)POs
def allocate(self):
"""Allocates a new pattern array with appropriate dimensions ``(self.s_len, self.sims)`` for one-pass simulation.
"""
return np.full((self.s_len, self.sims), logic.ZERO, dtype=np.uint8)
def simulate(self, patterns, cycles:int = 1, fault_line=-1, fault_model=2, fault_mask=None):
assert cycles >= 1
for bo, bs in batchrange(patterns.shape[-1], self.sims):
self.s_assign[self.pippi_s_locs, :bs] = patterns[self.pippi_s_locs, bo:bo+bs]
self.s_to_c()
for cycle in range(cycles):
self.c_prop(fault_line=fault_line, fault_model=fault_model, fault_mask=fault_mask)
if cycle < (cycles-1): self.c_ppo_to_ppi()
self.c_to_s()
patterns[self.poppo_s_locs, bo:bo+bs] = self.s_result[self.poppo_s_locs, :bs]
return patterns
@numba.njit @numba.njit
def _prop_cpu(ops, c_locs, c, fault_line, fault_mask, fault_model): def c_prop_2v_cpu(ops, c_locs, c, fault_line, fault_mask, fault_model):
for op, o0l, i0l, i1l, i2l, i3l in ops[:,:6]: for op, o0l, i0l, i1l, i2l, i3l in ops[:,:6]:
o0, i0, i1, i2, i3 = [c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)] o0, i0, i1, i2, i3 = [c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)]
if op == sim.BUF1: c[o0]=c[i0] if op == sim.BUF1: c[o0]=c[i0]
@ -363,21 +448,22 @@ def _prop_cpu(ops, c_locs, c, fault_line, fault_mask, fault_model):
c[o0] = c[o0] ^ fault_mask c[o0] = c[o0] ^ fault_mask
class LogicSim2V(sim.SimOps): class LogicSim4V(sim.SimOps):
"""A bit-parallel naïve combinational simulator for 2-valued logic. """A bit-parallel naïve combinational simulator for 4-valued logic.
Logic values: 0, 1, -, X
:param circuit: The circuit to simulate. :param circuit: The circuit to simulate.
:param sims: The number of parallel logic simulations to perform. :param sims: The number of parallel logic simulations to perform.
:param c_reuse: If True, intermediate signal values may get overwritten when not needed anymore to save memory. :param c_reuse: If True, intermediate signal values may get overwritten when not needed anymore to save memory.
:param strip_forks: If True, forks are not included in the simulation model to save memory and simulation time. :param strip_forks: If True, forks are not included in the simulation model to save memory and simulation time.
Caveat: faults on fanout branches will not be injected if forks are stripped.
""" """
def __init__(self, circuit: Circuit, sims: int = 8, c_reuse: bool = False, strip_forks: bool = False): def __init__(self, circuit: Circuit, sims: int = 8, c_reuse: bool = False, strip_forks: bool = False):
super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks) super().__init__(circuit, c_reuse=c_reuse, strip_forks=strip_forks)
self.sims = sims self.sims = sims
nbytes = cdiv(sims, 8) nbytes = cdiv(sims, 8)
self.c = np.zeros((self.c_len, 1, nbytes), dtype=np.uint8) self.c = np.zeros((self.c_len, 2, nbytes), dtype=np.uint8)
"""Logic values within the combinational portion of the circuit. """Logic values within the combinational portion of the circuit.
In bit-parallel (bp) storage format. In bit-parallel (bp) storage format.
@ -386,19 +472,19 @@ class LogicSim2V(sim.SimOps):
""" """
self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8) self.s_assign = np.zeros((self.s_len, self.sims), dtype=np.uint8)
"""Logic values assigned to the ports and flip-flops of the circuit. """Logic values assigned to the ports and flip-flops of the circuit.
The simulator reads (P)PI values from here. The simulator reads (P)PI values from here.
Values assigned to PO positions are ignored. Values assigned to PO positions are ignored.
This field is a 2-dimensional array and expects values in the mv storage format. This field is a 2-dimensional array and expects values in the mv storage format.
First index is the port position (defined by `self.circuit.s_nodes`), second index is the pattern index (0 ... `self.sims-1`). 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.zeros((self.s_len, self.sims), dtype=np.uint8)
"""Logic values at the ports and flip-flops of the circuit after simulation. """Logic values at the ports and flip-flops of the circuit after simulation.
The simulator writes (P)PO values here. The simulator writes (P)PO values here.
Values assigned to PI positions are ignored. Values assigned to PI positions are ignored.
This field is a 2-dimensional array and expects values in the mv storage format. This field is a 2-dimensional array and expects values in the mv storage format.
First index is the port position (defined by `self.circuit.s_nodes`), second index is the pattern index (0 ... `self.sims-1`). First index is the port position (defined by `self.circuit.s_nodes`), second
index is the pattern index (0 ... `self.sims-1`).
""" """
self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8) self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8)
@ -408,7 +494,7 @@ class LogicSim2V(sim.SimOps):
def s_to_c(self): def s_to_c(self):
"""Assigns the values from ``self.s_assign`` to the inputs of the combinational portion. """Assigns the values from ``self.s_assign`` to the inputs of the combinational portion.
""" """
self.c[self.pippi_c_locs] = logic.mv_to_bp(self.s_assign[self.pippi_s_locs])[:,:1,:] self.c[self.pippi_c_locs] = logic.mv_to_bp(self.s_assign[self.pippi_s_locs])[:,:2,:]
def c_prop(self, fault_line=-1, fault_model=2, fault_mask=None): def c_prop(self, fault_line=-1, fault_model=2, fault_mask=None):
if fault_mask is None: if fault_mask is None:
@ -418,12 +504,12 @@ class LogicSim2V(sim.SimOps):
fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8) fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8)
fault_mask2[:len(fault_mask)] = fault_mask fault_mask2[:len(fault_mask)] = fault_mask
fault_mask = fault_mask2 fault_mask = fault_mask2
_prop_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model)) c_prop_4v_cpu(self.ops, self.c_locs, self.c, int(fault_line), fault_mask, int(fault_model), self.tmp_idx, self.tmp2_idx)
def c_to_s(self): def c_to_s(self):
"""Captures the results of the combinational portion into ``self.s_result``. """Captures the results of the combinational portion into ``self.s_result``.
""" """
self.s_result[self.poppo_s_locs] = logic.bp_to_mv(self.c[self.poppo_c_locs])[:,:self.sims] * logic.ONE self.s_result[self.poppo_s_locs] = logic.bp_to_mv(self.c[self.poppo_c_locs])[:,:self.sims]
def c_ppo_to_ppi(self): def c_ppo_to_ppi(self):
"""Copies the result data for all PPOs (flip-flops) to PPIs for a next simulation cycle. """Copies the result data for all PPOs (flip-flops) to PPIs for a next simulation cycle.
@ -445,11 +531,197 @@ class LogicSim2V(sim.SimOps):
if cycle < (cycles-1): self.c_ppo_to_ppi() if cycle < (cycles-1): self.c_ppo_to_ppi()
self.c_to_s() self.c_to_s()
patterns[self.poppo_s_locs, bo:bo+bs] = self.s_result[self.poppo_s_locs, :bs] patterns[self.poppo_s_locs, bo:bo+bs] = self.s_result[self.poppo_s_locs, :bs]
return patterns
@numba.njit
def c_prop_4v_cpu(ops, c_locs, c, fault_line, fault_mask, fault_model, t0_idx, t1_idx):
t0 = c[c_locs[t0_idx]]
t1 = c[c_locs[t1_idx]]
for op, o0l, i0l, i1l, i2l, i3l in ops[:,:6]:
o0, i0, i1, i2, i3 = [c[c_locs[x]] for x in (o0l, i0l, i1l, i2l, i3l)]
if op == sim.BUF1 or op == sim.INV1:
# i0[0,1] -> o0[0,1]
# 0 0 0 -> 0 0 0
# - 0 1 -> X 1 0
# X 1 0 -> X 1 0
# 1 1 1 -> 1 1 1
o0[0] = i0[0] | i0[1]
o0[1] = i0[0] & i0[1]
elif op == sim.AND2 or op == sim.NAND2:
# i1[0,1] * i0[0,1] -> o0[0,1]
# 0 0 0 0 0 0 -> 0 0 0
# 0 0 0 - 0 1 -> 0 0 0
# 0 0 0 X 1 0 -> 0 0 0
# 0 0 0 1 1 1 -> 0 0 0
# - 0 1 0 0 0 -> 0 0 0
# - 0 1 - 0 1 -> X 1 0
# - 0 1 X 1 0 -> X 1 0
# - 0 1 1 1 1 -> X 1 0
# X 1 0 0 0 0 -> 0 0 0
# X 1 0 - 0 1 -> X 1 0
# X 1 0 X 1 0 -> X 1 0
# X 1 0 1 1 1 -> X 1 0
# 1 1 1 0 0 0 -> 0 0 0
# 1 1 1 - 0 1 -> X 1 0
# 1 1 1 X 1 0 -> X 1 0
# 1 1 1 1 1 1 -> 1 1 1
o0[0] = (i0[0]|i0[1]) & (i1[0]|i1[1])
o0[1] = i0[0] & i0[1] & i1[0] & i1[1]
elif op == sim.AND3 or op == sim.NAND3:
# i2[0,1] * i1[0,1] * i0[0,1] -> o0[0,1]
o0[0] = (i0[0]|i0[1]) & (i1[0]|i1[1]) & (i2[0]|i2[1])
o0[1] = i0[0] & i0[1] & i1[0] & i1[1] & i2[0] & i2[1]
elif op == sim.AND4 or op == sim.NAND4:
# i3[0,1] * i2[0,1] * i1[0,1] * i0[0,1] -> o0[0,1]
o0[0] = (i0[0]|i0[1]) & (i1[0]|i1[1]) & (i2[0]|i2[1]) & (i3[0]|i3[1])
o0[1] = i0[0] & i0[1] & i1[0] & i1[1] & i2[0] & i2[1] & i3[0] & i3[1]
elif op == sim.OR2 or op == sim.NOR2:
# i1[0,1] + i0[0,1] -> o0[0,1]
# 0 0 0 0 0 0 -> 0 0 0
# 0 0 0 - 0 1 -> X 1 0
# 0 0 0 X 1 0 -> X 1 0
# 0 0 0 1 1 1 -> 1 1 1
# - 0 1 0 0 0 -> X 1 0
# - 0 1 - 0 1 -> X 1 0
# - 0 1 X 1 0 -> X 1 0
# - 0 1 1 1 1 -> 1 1 1
# X 1 0 0 0 0 -> X 1 0
# X 1 0 - 0 1 -> X 1 0
# X 1 0 X 1 0 -> X 1 0
# X 1 0 1 1 1 -> 1 1 1
# 1 1 1 0 0 0 -> 1 1 1
# 1 1 1 - 0 1 -> 1 1 1
# 1 1 1 X 1 0 -> 1 1 1
# 1 1 1 1 1 1 -> 1 1 1
o0[0] = i0[0] | i0[1] | i1[0] | i1[1]
o0[1] = (i0[0]&i0[1]) | (i1[0]&i1[1])
elif op == sim.OR3 or op == sim.NOR3:
# i2[0,1] + i1[0,1] + i0[0,1] -> o0[0,1]
o0[0] = i0[0] | i0[1] | i1[0] | i1[1] | i2[0] | i2[1]
o0[1] = (i0[0]&i0[1]) | (i1[0]&i1[1]) | (i2[0]&i2[1])
elif op == sim.OR4 or op == sim.NOR4:
# i3[0,1] + i2[0,1] + i1[0,1] + i0[0,1] -> o0[0,1]
o0[0] = i0[0] | i0[1] | i1[0] | i1[1] | i2[0] | i2[1] | i3[0] | i3[1]
o0[1] = (i0[0]&i0[1]) | (i1[0]&i1[1]) | (i2[0]&i2[1]) | (i3[0]&i3[1])
elif op == sim.XOR2 or op == sim.XNOR2:
# i1[0,1] ^ i0[0,1] -> o0[0,1]
# 0 0 0 0 0 0 -> 0 0 0
# 0 0 0 - 0 1 -> X 1 0
# 0 0 0 X 1 0 -> X 1 0
# 0 0 0 1 1 1 -> 1 1 1
# - 0 1 0 0 0 -> X 1 0
# - 0 1 - 0 1 -> X 1 0
# - 0 1 X 1 0 -> X 1 0
# - 0 1 1 1 1 -> X 1 0
# X 1 0 0 0 0 -> X 1 0
# X 1 0 - 0 1 -> X 1 0
# X 1 0 X 1 0 -> X 1 0
# X 1 0 1 1 1 -> X 1 0
# 1 1 1 0 0 0 -> 1 1 1
# 1 1 1 - 0 1 -> X 1 0
# 1 1 1 X 1 0 -> X 1 0
# 1 1 1 1 1 1 -> 0 0 0
o0[0] = (i0[0]|i0[1]|i1[0]|i1[1]) & (~i0[0]|~i0[1]|~i1[0]|~i1[1])
o0[1] = (~i0[0]&~i0[1]&i1[0]&i1[1]) | (i0[0]&i0[1]&~i1[0]&~i1[1])
elif op == sim.XOR3 or op == sim.XNOR3:
# i2[0,1] ^ i1[0,1] ^ i0[0,1] -> o0[0,1]
t0[0] = (i0[0]|i0[1]|i1[0]|i1[1]) & (~i0[0]|~i0[1]|~i1[0]|~i1[1])
t0[1] = (~i0[0]&~i0[1]&i1[0]&i1[1]) | (i0[0]&i0[1]&~i1[0]&~i1[1])
o0[0] = (t0[0]|t0[1]|i2[0]|i2[1]) & (~t0[0]|~t0[1]|~i2[0]|~i2[1])
o0[1] = (~t0[0]&~t0[1]&i2[0]&i2[1]) | (t0[0]&t0[1]&~i2[0]&~i2[1])
elif op == sim.XOR4 or op == sim.XNOR4:
# i3[0,1] ^ i2[0,1] ^ i1[0,1] ^ i0[0,1] -> o0[0,1]
t0[0] = (i0[0]|i0[1]|i1[0]|i1[1]) & (~i0[0]|~i0[1]|~i1[0]|~i1[1])
t0[1] = (~i0[0]&~i0[1]&i1[0]&i1[1]) | (i0[0]&i0[1]&~i1[0]&~i1[1])
t1[0] = (t0[0]|t0[1]|i2[0]|i2[1]) & (~t0[0]|~t0[1]|~i2[0]|~i2[1])
t1[1] = (~t0[0]&~t0[1]&i2[0]&i2[1]) | (t0[0]&t0[1]&~i2[0]&~i2[1])
o0[0] = (t1[0]|t1[1]|i3[0]|i3[1]) & (~t1[0]|~t1[1]|~i3[0]|~i3[1])
o0[1] = (~t1[0]&~t1[1]&i3[0]&i3[1]) | (t1[0]&t1[1]&~i3[0]&~i3[1])
elif op == sim.AO21 or op == sim.AOI21:
# (i0[0,1] * i1[0,1]) + i2[0,1] -> o0[0,1]
t0[0] = (i0[0]|i0[1]) & (i1[0]|i1[1])
t0[1] = i0[0] & i0[1] & i1[0] & i1[1]
o0[0] = t0[0] | t0[1] | i2[0] | i2[1]
o0[1] = (t0[0]&t0[1]) | (i2[0]&i2[1])
elif op == sim.OA21 or op == sim.OAI21:
# (i0[0,1] + i1[0,1]) * i2[0,1] -> o0[0,1]
t0[0] = i0[0] | i0[1] | i1[0] | i1[1]
t0[1] = (i0[0]&i0[1]) | (i1[0]&i1[1])
o0[0] = (t0[0]|t0[1]) & (i2[0]|i2[1])
o0[1] = t0[0] & t0[1] & i2[0] & i2[1]
elif op == sim.AO22 or op == sim.AOI22:
# (i0[0,1] * i1[0,1]) + (i2[0,1] * i3[0,1]) -> o0[0,1]
t0[0] = (i0[0]|i0[1]) & (i1[0]|i1[1])
t0[1] = i0[0] & i0[1] & i1[0] & i1[1]
t1[0] = (i2[0]|i2[1]) & (i3[0]|i3[1])
t1[1] = i2[0] & i2[1] & i3[0] & i3[1]
o0[0] = t0[0] | t0[1] | t1[0] | t1[1]
o0[1] = (t0[0]&t0[1]) | (t1[0]&t1[1])
elif op == sim.OA22 or op == sim.OAI22:
# (i0[0,1] + i1[0,1]) * (i2[0,1] + i3[0,1]) -> o0[0,1]
t0[0] = i0[0] | i0[1] | i1[0] | i1[1]
t0[1] = (i0[0]&i0[1]) | (i1[0]&i1[1])
t1[0] = i2[0] | i2[1] | i3[0] | i3[1]
t1[1] = (i2[0]&i2[1]) | (i3[0]&i3[1])
o0[0] = (t0[0]|t0[1]) & (t1[0]|t1[1])
o0[1] = t0[0] & t0[1] & t1[0] & t1[1]
elif op == sim.AO211 or op == sim.AOI211:
# (i0[0,1] * i1[0,1]) + i2[0,1] + i3[0,1] -> o0[0,1]
t0[0] = (i0[0]|i0[1]) & (i1[0]|i1[1])
t0[1] = i0[0] & i0[1] & i1[0] & i1[1]
o0[0] = t0[0] | t0[1] | i2[0] | i2[1] | i3[0] | i3[1]
o0[1] = (t0[0]&t0[1]) | (i2[0]&i2[1]) | (i3[0]&i3[1])
elif op == sim.OA211 or op == sim.OAI211:
# (i0[0,1] + i1[0,1]) * i2[0,1] * i3[0,1] -> o0[0,1]
t0[0] = i0[0] | i0[1] | i1[0] | i1[1]
t0[1] = (i0[0]&i0[1]) | (i1[0]&i1[1])
o0[0] = (t0[0]|t0[1]) & (i2[0]|i2[1]) & (i3[0]|i3[1])
o0[1] = t0[0] & t0[1] & i2[0] & i2[1] & i3[0] & i3[1]
elif op == sim.MUX21:
# t1[0,1] = ~i2[0,1]
t1[0] = ~i2[0] | ~i2[1]
t1[1] = ~i2[0] & ~i2[1]
# t0 = i0 * t1
t0[0] = (i0[0]|i0[1]) & (t1[0]|t1[1])
t0[1] = i0[0] & i0[1] & t1[0] & t1[1]
# t1 = i1 * i2
t1[0] = (i1[0]|i1[1]) & (i2[0]|i2[1])
t1[1] = i1[0] & i1[1] & i2[0] & i2[1]
# o0 = t0 | t1
o0[0] = t0[0] | t0[1] | t1[0] | t1[1]
o0[1] = (t0[0]&t0[1]) | (t1[0]&t1[1])
else: print(f'unknown op {op}')
if (op == sim.INV1 or
op == sim.NAND2 or op == sim.NAND3 or op == sim.NAND4 or
op == sim.NOR2 or op == sim.NOR3 or op == sim.NOR4 or
op == sim.XNOR2 or op == sim.XNOR3 or op == sim.XNOR4 or
op == sim.AOI21 or op == sim.OAI21 or
op == sim.AOI22 or op == sim.OAI22 or
op == sim.AOI211 or op == sim.OAI211):
# o0[0,1] -> o0[0,1]
# 0 0 0 -> 1 1 1
# - 0 1 -> X 1 0
# X 1 0 -> X 1 0
# 1 1 1 -> 0 0 0
t0[0] = ~o0[0] | ~o0[1]
o0[1] = ~o0[0] & ~o0[1]
o0[0] = t0[0]
if fault_line >= 0 and o0l == fault_line:
if fault_model == 0:
o0[...] = o0 & ~fault_mask
elif fault_model == 1:
o0[...] = o0 | fault_mask
else:
t0[0] = ~(o0[0] ^ o0[1]) # mask for 0 and 1
o0[...] = o0 ^ (fault_mask & t0[0]) # leaves X and - untouched
class LogicSim6V(sim.SimOps): class LogicSim6V(sim.SimOps):
"""A bit-parallel naïve combinational simulator for 6-valued logic. """A bit-parallel naïve combinational simulator for 6-valued logic.
Logic values: 0, 1, R, F, N, P
:param circuit: The circuit to simulate. :param circuit: The circuit to simulate.
:param sims: The number of parallel logic simulations to perform. :param sims: The number of parallel logic simulations to perform.
:param c_reuse: If True, intermediate signal values may get overwritten when not needed anymore to save memory. :param c_reuse: If True, intermediate signal values may get overwritten when not needed anymore to save memory.
@ -471,6 +743,9 @@ class LogicSim6V(sim.SimOps):
Access this array to assign new values to the (P)PIs or read values from the (P)POs. Access this array to assign new values to the (P)PIs or read values from the (P)POs.
""" """
self.s_assign = self.s[0]
self.s_result = self.s[1]
self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8)
def __repr__(self): def __repr__(self):
return f'{{name: "{self.circuit.name}", sims: {self.sims}, c_bytes: {eng(self.c.nbytes)}}}' return f'{{name: "{self.circuit.name}", sims: {self.sims}, c_bytes: {eng(self.c.nbytes)}}}'
@ -480,17 +755,46 @@ class LogicSim6V(sim.SimOps):
""" """
self.c[self.pippi_c_locs] = logic.mv_to_bp(self.s[0, self.pippi_s_locs]) self.c[self.pippi_c_locs] = logic.mv_to_bp(self.s[0, self.pippi_s_locs])
def c_prop(self): def c_prop(self, fault_line=-1, fault_model=2, fault_mask=None):
c_prop_cpu(self.ops, self.c, self.c_locs, self.tmp_idx, self.tmp2_idx) if fault_mask is None:
fault_mask = self._full_mask # default: full mask
else:
if len(fault_mask) < self.c.shape[-1]: # pad mask with 0's if necessary
fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8)
fault_mask2[:len(fault_mask)] = fault_mask
fault_mask = fault_mask2
c_prop_6v_cpu(self.ops, self.c, self.c_locs, fault_line, fault_mask, fault_model, self.tmp_idx, self.tmp2_idx)
def c_to_s(self): def c_to_s(self):
"""Captures the results of the combinational portion into ``s[1]``. """Captures the results of the combinational portion into ``s[1]``.
""" """
self.s[1, self.poppo_s_locs] = logic.bp_to_mv(self.c[self.poppo_c_locs])[:,:self.sims] self.s[1, self.poppo_s_locs] = logic.bp_to_mv(self.c[self.poppo_c_locs])[:,:self.sims]
def c_ppo_to_ppi(self):
"""Copies the result data for all PPOs (flip-flops) to PPIs for a next simulation cycle.
"""
self.c[self.ppi_c_locs] = self.c[self.ppo_c_locs].copy() # copy prevents undefined behavior if (P)PIs are connected directly to (P)POs
def allocate(self):
"""Allocates a new pattern array with appropriate dimensions ``(self.s_len, self.sims)`` for one-pass simulation.
"""
return np.full((self.s_len, self.sims), logic.ZERO, dtype=np.uint8)
def simulate(self, patterns, cycles:int = 1, fault_line=-1, fault_model=2, fault_mask=None):
assert cycles >= 1
for bo, bs in batchrange(patterns.shape[-1], self.sims):
self.s_assign[self.pippi_s_locs, :bs] = patterns[self.pippi_s_locs, bo:bo+bs]
self.s_to_c()
for cycle in range(cycles):
self.c_prop(fault_line=fault_line, fault_model=fault_model, fault_mask=fault_mask)
if cycle < (cycles-1): self.c_ppo_to_ppi()
self.c_to_s()
patterns[self.poppo_s_locs, bo:bo+bs] = self.s_result[self.poppo_s_locs, :bs]
return patterns
@numba.njit @numba.njit
def c_prop_cpu(ops, c, c_locs, tmp_idx, tmp2_idx): def c_prop_6v_cpu(ops, c, c_locs, fault_line, fault_mask, fault_model, tmp_idx, tmp2_idx):
t0 = c[c_locs[tmp_idx]] t0 = c[c_locs[tmp_idx]]
t1 = c[c_locs[tmp2_idx]] t1 = c[c_locs[tmp2_idx]]
inv_op = np.array([255, 255, 0], dtype=np.uint8)[np.newaxis, :, np.newaxis] inv_op = np.array([255, 255, 0], dtype=np.uint8)[np.newaxis, :, np.newaxis]
@ -538,6 +842,84 @@ def c_prop_cpu(ops, c, c_locs, tmp_idx, tmp2_idx):
o0[0] = i0[0] ^ i1[0] o0[0] = i0[0] ^ i1[0]
o0[1] = i0[1] ^ i1[1] o0[1] = i0[1] ^ i1[1]
o0[2] = i0[2] | i1[2] o0[2] = i0[2] | i1[2]
elif op == sim.XOR3 or op == sim.XNOR3:
o0[0] = i0[0] ^ i1[0] ^ i2[0]
o0[1] = i0[1] ^ i1[1] ^ i2[1]
o0[2] = i0[2] | i1[2] | i2[2]
elif op == sim.XOR4 or op == sim.XNOR4:
o0[0] = i0[0] ^ i1[0] ^ i2[0] ^ i3[0]
o0[1] = i0[1] ^ i1[1] ^ i2[1] ^ i3[1]
o0[2] = i0[2] | i1[2] | i2[2] | i3[2]
elif op == sim.AO21 or op == sim.AOI21:
# (i0[0,1] * i1[0,1]) + i2[0,1] -> o0[0,1]
t0[0] = i0[0] & i1[0]
t0[1] = i0[1] & i1[1]
t0[2] = (i0[2]&(i1[0]|i1[1]|i1[2])|
i1[2]&(i0[0]|i0[1]|i0[2]))
o0[0] = t0[0] | i2[0]
o0[1] = t0[1] | i2[1]
o0[2] = (t0[2]&(~i2[0]|~i2[1]|i2[2])|
i2[2]&(~t0[0]|~t0[1]|t0[2]))
elif op == sim.OA21 or op == sim.OAI21:
# (i0[0,1] + i1[0,1]) * i2[0,1] -> o0[0,1]
t0[0] = i0[0] | i1[0]
t0[1] = i0[1] | i1[1]
t0[2] = (i0[2]&(~i1[0]|~i1[1]|i1[2])|
i1[2]&(~i0[0]|~i0[1]|i0[2]))
o0[0] = t0[0] & i2[0]
o0[1] = t0[1] & i2[1]
o0[2] = (t0[2]&(i2[0]|i2[1]|i2[2])|
i2[2]&(t0[0]|t0[1]|t0[2]))
elif op == sim.AO22 or op == sim.AOI22:
# (i0[0,1] * i1[0,1]) + (i2[0,1] * i3[0,1]) -> o0[0,1]
t0[0] = i0[0] & i1[0]
t0[1] = i0[1] & i1[1]
t0[2] = (i0[2]&(i1[0]|i1[1]|i1[2])|
i1[2]&(i0[0]|i0[1]|i0[2]))
t1[0] = i2[0] & i3[0]
t1[1] = i2[1] & i3[1]
t1[2] = (i2[2]&(i3[0]|i3[1]|i3[2])|
i3[2]&(i2[0]|i2[1]|i2[2]))
o0[0] = t0[0] | t1[0]
o0[1] = t0[1] | t1[1]
o0[2] = (t0[2]&(~t1[0]|~t1[1]|t1[2])|
t1[2]&(~t0[0]|~t0[1]|t0[2]))
elif op == sim.OA22 or op == sim.OAI22:
# (i0[0,1] + i1[0,1]) * (i2[0,1] + i3[0,1]) -> o0[0,1]
t0[0] = i0[0] | i1[0]
t0[1] = i0[1] | i1[1]
t0[2] = (i0[2]&(~i1[0]|~i1[1]|i1[2])|
i1[2]&(~i0[0]|~i0[1]|i0[2]))
t1[0] = i2[0] | i3[0]
t1[1] = i2[1] | i3[1]
t1[2] = (i2[2]&(~i3[0]|~i3[1]|i3[2])|
i3[2]&(~i2[0]|~i2[1]|i2[2]))
o0[0] = t0[0] & t1[0]
o0[1] = t0[1] & t1[1]
o0[2] = (t0[2]&(t1[0]|t1[1]|t1[2])|
t1[2]&(t0[0]|t0[1]|t0[2]))
elif op == sim.AO211 or op == sim.AOI211:
# (i0[0,1] * i1[0,1]) + i2[0,1] + i3[0,1] -> o0[0,1]
t0[0] = i0[0] & i1[0]
t0[1] = i0[1] & i1[1]
t0[2] = (i0[2]&(i1[0]|i1[1]|i1[2])|
i1[2]&(i0[0]|i0[1]|i0[2]))
o0[0] = t0[0] | i2[0] | i3[0]
o0[1] = t0[1] | i2[1] | i3[1]
o0[2] = (t0[2]&(~i2[0]|~i2[1]|i2[2])&(~i3[0]|~i3[1]|i3[2])|
i2[2]&(~t0[0]|~t0[1]|t0[2])&(~i3[0]|~i3[1]|i3[2])|
i3[2]&(~t0[0]|~t0[1]|t0[2])&(~i2[0]|~i2[1]|i2[2]))
elif op == sim.OA211 or op == sim.OAI211:
# (i0[0,1] + i1[0,1]) * i2[0,1] * i3[0,1] -> o0[0,1]
t0[0] = i0[0] | i1[0]
t0[1] = i0[1] | i1[1]
t0[2] = (i0[2]&(~i1[0]|~i1[1]|i1[2])|
i1[2]&(~i0[0]|~i0[1]|i0[2]))
o0[0] = t0[0] & i2[0] & i3[0]
o0[1] = t0[1] & i2[1] & i3[1]
o0[2] = (t0[2]&(i2[0]|i2[1]|i2[2])&(i3[0]|i3[1]|i3[2])|
i2[2]&(t0[0]|t0[1]|t0[2])&(i3[0]|i3[1]|i3[2])|
i3[2]&(t0[0]|t0[1]|t0[2])&(i2[0]|i2[1]|i2[2]))
elif op == sim.MUX21: elif op == sim.MUX21:
# t1 = ~i2 # t1 = ~i2
t1[...] = i2 ^ inv_op t1[...] = i2 ^ inv_op
@ -561,5 +943,8 @@ def c_prop_cpu(ops, c, c_locs, tmp_idx, tmp2_idx):
if (op == sim.INV1 or if (op == sim.INV1 or
op == sim.NAND2 or op == sim.NAND3 or op == sim.NAND4 or op == sim.NAND2 or op == sim.NAND3 or op == sim.NAND4 or
op == sim.NOR2 or op == sim.NOR3 or op == sim.NOR4 or op == sim.NOR2 or op == sim.NOR3 or op == sim.NOR4 or
op == sim.XNOR2): op == sim.XNOR2 or op == sim.XNOR3 or op == sim.XNOR4 or
op == sim.AOI21 or op == sim.OAI21 or
op == sim.AOI22 or op == sim.OAI22 or
op == sim.AOI211 or op == sim.OAI211):
o0[...] = o0 ^ inv_op o0[...] = o0 ^ inv_op

2
tests/Makefile

@ -0,0 +1,2 @@
all:
abc -F map_to_minimal.abc

134
tests/all_kyupy_simprims.minimal.v

@ -1,22 +1,22 @@
// Benchmark "all_kyupy_primitives" written by ABC on Sat Nov 1 23:49:37 2025 // Benchmark "all_kyupy_primitives" written by ABC on Thu Nov 6 13:13:21 2025
module all_kyupy_primitives ( module all_kyupy_primitives (
i0, i1, i2, i3, i0, i1, i2, i3,
\o[0] , \o[1] , \o[2] , \o[3] , \o[4] , \o[5] , \o[6] , \o[7] , \o[8] , \o[0] , \o[1] , \o[2] , \o[3] , \o[4] , \o[5] , \o[6] , \o[7] , \o[8] ,
\o[9] , \o[10] , \o[11] , \o[12] , \o[13] , \o[14] , \o[15] , \o[16] , \o[9] , \o[10] , \o[11] , \o[12] , \o[13] , \o[14] , \o[15] , \o[16] ,
\o[17] , \o[18] , \o[19] , \o[20] , \o[21] , \o[22] , \o[23] , \o[24] , \o[17] , \o[18] , \o[19] , \o[20] , \o[21] , \o[22] , \o[23] , \o[24] ,
\o[25] , \o[26] , \o[27] , \o[28] , \o[29] ); \o[25] , \o[26] , \o[27] , \o[28] , \o[29] , \o[30] , \o[31] , \o[32] );
input i0, i1, i2, i3; input i0, i1, i2, i3;
output \o[0] , \o[1] , \o[2] , \o[3] , \o[4] , \o[5] , \o[6] , \o[7] , output \o[0] , \o[1] , \o[2] , \o[3] , \o[4] , \o[5] , \o[6] , \o[7] ,
\o[8] , \o[9] , \o[10] , \o[11] , \o[12] , \o[13] , \o[14] , \o[15] , \o[8] , \o[9] , \o[10] , \o[11] , \o[12] , \o[13] , \o[14] , \o[15] ,
\o[16] , \o[17] , \o[18] , \o[19] , \o[20] , \o[21] , \o[22] , \o[23] , \o[16] , \o[17] , \o[18] , \o[19] , \o[20] , \o[21] , \o[22] , \o[23] ,
\o[24] , \o[25] , \o[26] , \o[27] , \o[28] , \o[29] ; \o[24] , \o[25] , \o[26] , \o[27] , \o[28] , \o[29] , \o[30] , \o[31] ,
wire new_n42, new_n45, new_n48, new_n51, new_n52, new_n53, new_n54, \o[32] ;
new_n55, new_n57, new_n58, new_n59, new_n60, new_n61, new_n62, new_n63, wire new_n45, new_n48, new_n51, new_n54, new_n55, new_n56, new_n57,
new_n64, new_n65, new_n66, new_n67, new_n69, new_n70, new_n71, new_n72, new_n60, new_n61, new_n62, new_n63, new_n64, new_n65, new_n66, new_n67,
new_n73, new_n74, new_n75, new_n76, new_n77, new_n78, new_n79, new_n83, new_n68, new_n69, new_n72, new_n73, new_n74, new_n75, new_n76, new_n77,
new_n84, new_n88, new_n89, new_n93, new_n96, new_n99, new_n100, new_n78, new_n79, new_n80, new_n81, new_n86, new_n87, new_n91, new_n92,
new_n101; new_n96, new_n99, new_n102, new_n103, new_n104;
INV1 g00(.i0(i1), .o(\o[1] )); INV1 g00(.i0(i1), .o(\o[1] ));
AND2 g01(.i0(i1), .i1(i0), .o(\o[2] )); AND2 g01(.i0(i1), .i1(i0), .o(\o[2] ));
AND2 g02(.i0(\o[2] ), .i1(i2), .o(\o[3] )); AND2 g02(.i0(\o[2] ), .i1(i2), .o(\o[3] ));
@ -24,67 +24,67 @@ module all_kyupy_primitives (
INV1 g04(.i0(\o[2] ), .o(\o[5] )); INV1 g04(.i0(\o[2] ), .o(\o[5] ));
INV1 g05(.i0(\o[3] ), .o(\o[6] )); INV1 g05(.i0(\o[3] ), .o(\o[6] ));
INV1 g06(.i0(\o[4] ), .o(\o[7] )); INV1 g06(.i0(\o[4] ), .o(\o[7] ));
INV1 g07(.i0(i0), .o(new_n42)); INV1 g07(.i0(i0), .o(new_n45));
AND2 g08(.i0(\o[1] ), .i1(new_n42), .o(\o[11] )); AND2 g08(.i0(\o[1] ), .i1(new_n45), .o(\o[11] ));
INV1 g09(.i0(\o[11] ), .o(\o[8] )); INV1 g09(.i0(\o[11] ), .o(\o[8] ));
INV1 g10(.i0(i2), .o(new_n45)); INV1 g10(.i0(i2), .o(new_n48));
AND2 g11(.i0(\o[11] ), .i1(new_n45), .o(\o[12] )); AND2 g11(.i0(\o[11] ), .i1(new_n48), .o(\o[12] ));
INV1 g12(.i0(\o[12] ), .o(\o[9] )); INV1 g12(.i0(\o[12] ), .o(\o[9] ));
INV1 g13(.i0(i3), .o(new_n48)); INV1 g13(.i0(i3), .o(new_n51));
AND2 g14(.i0(\o[12] ), .i1(new_n48), .o(\o[13] )); AND2 g14(.i0(\o[12] ), .i1(new_n51), .o(\o[13] ));
INV1 g15(.i0(\o[13] ), .o(\o[10] )); INV1 g15(.i0(\o[13] ), .o(\o[10] ));
AND2 g16(.i0(\o[1] ), .i1(i0), .o(new_n51)); AND2 g16(.i0(\o[1] ), .i1(i0), .o(new_n54));
INV1 g17(.i0(new_n51), .o(new_n52)); INV1 g17(.i0(new_n54), .o(new_n55));
AND2 g18(.i0(i1), .i1(new_n42), .o(new_n53)); AND2 g18(.i0(i1), .i1(new_n45), .o(new_n56));
INV1 g19(.i0(new_n53), .o(new_n54)); INV1 g19(.i0(new_n56), .o(new_n57));
AND2 g20(.i0(new_n54), .i1(new_n52), .o(new_n55)); AND2 g20(.i0(new_n57), .i1(new_n55), .o(\o[17] ));
INV1 g21(.i0(new_n55), .o(\o[14] )); INV1 g21(.i0(\o[17] ), .o(\o[14] ));
AND2 g22(.i0(i2), .i1(i1), .o(new_n57)); AND2 g22(.i0(i2), .i1(i1), .o(new_n60));
INV1 g23(.i0(new_n57), .o(new_n58)); INV1 g23(.i0(new_n60), .o(new_n61));
AND2 g24(.i0(new_n45), .i1(\o[1] ), .o(new_n59)); AND2 g24(.i0(new_n48), .i1(\o[1] ), .o(new_n62));
INV1 g25(.i0(new_n59), .o(new_n60)); INV1 g25(.i0(new_n62), .o(new_n63));
AND2 g26(.i0(new_n60), .i1(new_n58), .o(new_n61)); AND2 g26(.i0(new_n63), .i1(new_n61), .o(new_n64));
INV1 g27(.i0(new_n61), .o(new_n62)); INV1 g27(.i0(new_n64), .o(new_n65));
AND2 g28(.i0(new_n62), .i1(i0), .o(new_n63)); AND2 g28(.i0(new_n65), .i1(i0), .o(new_n66));
INV1 g29(.i0(new_n63), .o(new_n64)); INV1 g29(.i0(new_n66), .o(new_n67));
AND2 g30(.i0(new_n61), .i1(new_n42), .o(new_n65)); AND2 g30(.i0(new_n64), .i1(new_n45), .o(new_n68));
INV1 g31(.i0(new_n65), .o(new_n66)); INV1 g31(.i0(new_n68), .o(new_n69));
AND2 g32(.i0(new_n66), .i1(new_n64), .o(new_n67)); AND2 g32(.i0(new_n69), .i1(new_n67), .o(\o[18] ));
INV1 g33(.i0(new_n67), .o(\o[15] )); INV1 g33(.i0(\o[18] ), .o(\o[15] ));
AND2 g34(.i0(i3), .i1(new_n45), .o(new_n69)); AND2 g34(.i0(i3), .i1(new_n48), .o(new_n72));
INV1 g35(.i0(new_n69), .o(new_n70)); INV1 g35(.i0(new_n72), .o(new_n73));
AND2 g36(.i0(new_n48), .i1(i2), .o(new_n71)); AND2 g36(.i0(new_n51), .i1(i2), .o(new_n74));
INV1 g37(.i0(new_n71), .o(new_n72)); INV1 g37(.i0(new_n74), .o(new_n75));
AND2 g38(.i0(new_n72), .i1(new_n70), .o(new_n73)); AND2 g38(.i0(new_n75), .i1(new_n73), .o(new_n76));
INV1 g39(.i0(new_n73), .o(new_n74)); INV1 g39(.i0(new_n76), .o(new_n77));
AND2 g40(.i0(new_n74), .i1(new_n55), .o(new_n75)); AND2 g40(.i0(new_n77), .i1(\o[17] ), .o(new_n78));
INV1 g41(.i0(new_n75), .o(new_n76)); INV1 g41(.i0(new_n78), .o(new_n79));
AND2 g42(.i0(new_n73), .i1(\o[14] ), .o(new_n77)); AND2 g42(.i0(new_n76), .i1(\o[14] ), .o(new_n80));
INV1 g43(.i0(new_n77), .o(new_n78)); INV1 g43(.i0(new_n80), .o(new_n81));
AND2 g44(.i0(new_n78), .i1(new_n76), .o(new_n79)); AND2 g44(.i0(new_n81), .i1(new_n79), .o(\o[19] ));
INV1 g45(.i0(new_n79), .o(\o[16] )); INV1 g45(.i0(\o[19] ), .o(\o[16] ));
AND2 g46(.i0(\o[5] ), .i1(new_n45), .o(\o[21] )); AND2 g46(.i0(\o[5] ), .i1(new_n48), .o(\o[24] ));
INV1 g47(.i0(\o[21] ), .o(\o[17] )); INV1 g47(.i0(\o[24] ), .o(\o[20] ));
AND2 g48(.i0(i3), .i1(i2), .o(new_n83)); AND2 g48(.i0(i3), .i1(i2), .o(new_n86));
INV1 g49(.i0(new_n83), .o(new_n84)); INV1 g49(.i0(new_n86), .o(new_n87));
AND2 g50(.i0(new_n84), .i1(\o[5] ), .o(\o[22] )); AND2 g50(.i0(new_n87), .i1(\o[5] ), .o(\o[25] ));
INV1 g51(.i0(\o[22] ), .o(\o[18] )); INV1 g51(.i0(\o[25] ), .o(\o[21] ));
AND2 g52(.i0(\o[8] ), .i1(i2), .o(\o[19] )); AND2 g52(.i0(\o[8] ), .i1(i2), .o(\o[22] ));
AND2 g53(.i0(new_n48), .i1(new_n45), .o(new_n88)); AND2 g53(.i0(new_n51), .i1(new_n48), .o(new_n91));
INV1 g54(.i0(new_n88), .o(new_n89)); INV1 g54(.i0(new_n91), .o(new_n92));
AND2 g55(.i0(new_n89), .i1(\o[8] ), .o(\o[20] )); AND2 g55(.i0(new_n92), .i1(\o[8] ), .o(\o[23] ));
INV1 g56(.i0(\o[19] ), .o(\o[23] )); INV1 g56(.i0(\o[22] ), .o(\o[26] ));
INV1 g57(.i0(\o[20] ), .o(\o[24] )); INV1 g57(.i0(\o[23] ), .o(\o[27] ));
AND2 g58(.i0(\o[5] ), .i1(new_n48), .o(new_n93)); AND2 g58(.i0(\o[5] ), .i1(new_n51), .o(new_n96));
AND2 g59(.i0(new_n93), .i1(new_n45), .o(\o[27] )); AND2 g59(.i0(new_n96), .i1(new_n48), .o(\o[30] ));
INV1 g60(.i0(\o[27] ), .o(\o[25] )); INV1 g60(.i0(\o[30] ), .o(\o[28] ));
AND2 g61(.i0(\o[8] ), .i1(i3), .o(new_n96)); AND2 g61(.i0(\o[8] ), .i1(i3), .o(new_n99));
AND2 g62(.i0(new_n96), .i1(i2), .o(\o[26] )); AND2 g62(.i0(new_n99), .i1(i2), .o(\o[29] ));
INV1 g63(.i0(\o[26] ), .o(\o[28] )); INV1 g63(.i0(\o[29] ), .o(\o[31] ));
AND2 g64(.i0(new_n45), .i1(i0), .o(new_n99)); AND2 g64(.i0(new_n48), .i1(i0), .o(new_n102));
INV1 g65(.i0(new_n99), .o(new_n100)); INV1 g65(.i0(new_n102), .o(new_n103));
AND2 g66(.i0(new_n100), .i1(new_n58), .o(new_n101)); AND2 g66(.i0(new_n103), .i1(new_n61), .o(new_n104));
INV1 g67(.i0(new_n101), .o(\o[29] )); INV1 g67(.i0(new_n104), .o(\o[32] ));
BUF1 g68(.i0(i0), .o(\o[0] )); BUF1 g68(.i0(i0), .o(\o[0] ));
endmodule endmodule

32
tests/all_kyupy_simprims.v

@ -3,7 +3,7 @@ module all_kyupy_primitives (i0, i1, i2, i3, o);
input i1; input i1;
input i2; input i2;
input i3; input i3;
output [29:0] o; output [32:0] o;
BUF1 buf1_0 (.i0(i0), .o(o[0])); BUF1 buf1_0 (.i0(i0), .o(o[0]));
INV1 inv1_0 (.i0(i1), .o(o[1])); INV1 inv1_0 (.i0(i1), .o(o[1]));
@ -28,22 +28,26 @@ module all_kyupy_primitives (i0, i1, i2, i3, o);
XOR3 xor3_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[15])); XOR3 xor3_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[15]));
XOR4 xor4_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[16])); XOR4 xor4_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[16]));
AO21 ao21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[17])); XNOR2 xnor2_0 (.i0(i0), .i1(i1), .o(o[17]));
AO22 ao22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[18])); XNOR3 xnor3_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[18]));
OA21 oa21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[19])); XNOR4 xnor4_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[19]));
OA22 oa22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[20]));
AOI21 aoi21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[21])); AO21 ao21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[20]));
AOI22 aoi22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[22])); AO22 ao22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[21]));
OAI21 oai21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[23])); OA21 oa21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[22]));
OAI22 oai22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[24])); OA22 oa22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[23]));
AO211 ao211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[25])); AOI21 aoi21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[24]));
OA211 oa211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[26])); AOI22 aoi22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[25]));
OAI21 oai21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[26]));
OAI22 oai22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[27]));
AOI211 aoi211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[27])); AO211 ao211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[28]));
OAI211 oai211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[28])); OA211 oa211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[29]));
MUX21 mux21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[29])); AOI211 aoi211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[30]));
OAI211 oai211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[31]));
MUX21 mux21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[32]));
endmodule endmodule

76
tests/test_logic_sim.py

@ -1,6 +1,6 @@
import numpy as np import numpy as np
from kyupy.logic_sim import LogicSim, LogicSim2V, LogicSim6V from kyupy.logic_sim import LogicSim, LogicSim2V, LogicSim4V, LogicSim6V
from kyupy import bench, logic, sim, verilog from kyupy import bench, logic, sim, verilog
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp
@ -9,7 +9,45 @@ def assert_equal_shape_and_contents(actual, desired):
assert actual.shape == desired.shape assert actual.shape == desired.shape
np.testing.assert_allclose(actual, desired) np.testing.assert_allclose(actual, desired)
def test_simprims(mydir): def test_LogicSim2V_minimal():
c = bench.parse('input(i0, i1) output(o0, o1, o2) o0=AND2(i0,i1) o1=INV1(i0) o2=BUF1(i0)')
sim = LogicSim2V(c)
expect = mvarray('00010',
'01010',
'10001',
'11101')
actual = sim.simulate(expect.copy())
np.testing.assert_array_equal(
expect[c.io_locs('o')],
actual[c.io_locs('o')])
def test_LogicSim4V_minimal():
c = bench.parse('input(i0, i1) output(o0, o1, o2) o0=AND2(i0,i1) o1=INV1(i0) o2=BUF1(i0)')
sim = LogicSim4V(c, 16)
expect = mvarray('00010', '01010', '0-010', '0X010',
'10001', '11101', '1-X01', '1XX01',
'-00XX', '-1XXX', '--XXX', '-XXXX',
'X00XX', 'X1XXX', 'X-XXX', 'XXXXX',)
actual = sim.simulate(expect.copy())
np.testing.assert_array_equal(
expect[c.io_locs('o')],
actual[c.io_locs('o')])
def test_LogicSim6V_minimal():
c = bench.parse('input(i0, i1) output(o0, o1, o2) o0=AND2(i0,i1) o1=INV1(i0) o2=BUF1(i0)')
sim = LogicSim6V(c, 36)
expect = mvarray('00010', '01010', '0R010', '0F010', '0P010', '0N010',
'10001', '11101', '1RR01', '1FF01', '1PP01', '1NN01',
'R00FR', 'R1RFR', 'RRRFR', 'RFPFR', 'RPPFR', 'RNRFR',
'F00RF', 'F1FRF', 'FRPRF', 'FFFRF', 'FPPRF', 'FNFRF',
'P00NP', 'P1PNP', 'PRPNP', 'PFPNP', 'PPPNP', 'PNPNP',
'N00PN', 'N1NPN', 'NRRPN', 'NFFPN', 'NPPPN', 'NNNPN')
actual = sim.simulate(expect.copy())
np.testing.assert_array_equal(
expect[c.io_locs('o')],
actual[c.io_locs('o')])
def test_LogicSim2V_simprims(mydir):
c1 = verilog.load(mydir / 'all_kyupy_simprims.v') c1 = verilog.load(mydir / 'all_kyupy_simprims.v')
c2 = verilog.load(mydir / 'all_kyupy_simprims.minimal.v') c2 = verilog.load(mydir / 'all_kyupy_simprims.minimal.v')
pi_count = sum([len(n.ins)==0 for n in c1.io_nodes]) pi_count = sum([len(n.ins)==0 for n in c1.io_nodes])
@ -24,6 +62,40 @@ def test_simprims(mydir):
tests1[c1.io_locs('o')], tests1[c1.io_locs('o')],
tests2[c2.io_locs('o')]) tests2[c2.io_locs('o')])
def test_LogicSim4V_simprims(mydir):
c1 = verilog.load(mydir / 'all_kyupy_simprims.v')
c2 = verilog.load(mydir / 'all_kyupy_simprims.minimal.v')
pi_count = sum([len(n.ins)==0 for n in c1.io_nodes])
sim1 = LogicSim4V(c1, sims=4**pi_count)
sim2 = LogicSim4V(c2, sims=4**pi_count)
tests1 = sim1.allocate()
tests1[sim1.pippi_s_locs] = np.fromfunction(lambda x, y: (y//(4**x))%4, (pi_count, sim1.sims), dtype=int) # exhaustive test
tests2 = tests1.copy()
sim1.simulate(tests1)
sim2.simulate(tests2)
np.testing.assert_array_equal(
tests1[c1.io_locs('o')],
tests2[c2.io_locs('o')])
def test_LogicSim6V_simprims(mydir):
c1 = verilog.load(mydir / 'all_kyupy_simprims.v')
c2 = verilog.load(mydir / 'all_kyupy_simprims.minimal.v')
pi_count = sum([len(n.ins)==0 for n in c1.io_nodes])
sim1 = LogicSim6V(c1, sims=6**pi_count)
sim2 = LogicSim6V(c2, sims=6**pi_count)
tests1 = sim1.allocate()
tests1[sim1.pippi_s_locs] = logic.mvarray('01RFPN')[np.fromfunction(lambda x, y: (y//(6**x))%6, (pi_count, sim1.sims), dtype=int)] # exhaustive test
tests2 = tests1.copy()
sim1.simulate(tests1)
sim2.simulate(tests2)
for loc1, loc2 in zip(c1.io_locs('o'), c2.io_locs('o')):
n = c1.s_nodes[loc1]
if (tests1[loc1] != tests2[loc2]).any():
print(f'Mismatch at output {n}')
np.testing.assert_array_equal(
tests1[c1.io_locs('o')],
tests2[c2.io_locs('o')])
def test_2v(): def test_2v():
c = bench.parse(f''' c = bench.parse(f'''
input(i3, i2, i1, i0) input(i3, i2, i1, i0)

Loading…
Cancel
Save