|
|
|
|
@ -11,7 +11,7 @@ import math
@@ -11,7 +11,7 @@ import math
|
|
|
|
|
import numpy as np |
|
|
|
|
|
|
|
|
|
from . import numba, logic, hr_bytes, sim, eng, cdiv, batchrange |
|
|
|
|
from .circuit import Circuit |
|
|
|
|
from .circuit import Circuit, Line |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LogicSim(sim.SimOps): |
|
|
|
|
@ -53,7 +53,7 @@ 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] |
|
|
|
|
|
|
|
|
|
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. |
|
|
|
|
|
|
|
|
|
Performs all logic operations in topological order. |
|
|
|
|
@ -78,7 +78,7 @@ class LogicSim(sim.SimOps):
@@ -78,7 +78,7 @@ class LogicSim(sim.SimOps):
|
|
|
|
|
t1 = self.c_locs[self.tmp2_idx] |
|
|
|
|
if self.m == 2: |
|
|
|
|
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: |
|
|
|
|
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)] |
|
|
|
|
@ -316,8 +316,93 @@ class LogicSim(sim.SimOps):
@@ -316,8 +316,93 @@ class LogicSim(sim.SimOps):
|
|
|
|
|
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 |
|
|
|
|
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]: |
|
|
|
|
o0, i0, i1, i2, i3 = [c_locs[x] for x in (o0l, i0l, i1l, i2l, i3l)] |
|
|
|
|
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):
@@ -363,21 +448,22 @@ def _prop_cpu(ops, c_locs, c, fault_line, fault_mask, fault_model):
|
|
|
|
|
c[o0] = c[o0] ^ fault_mask |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LogicSim2V(sim.SimOps): |
|
|
|
|
"""A bit-parallel naïve combinational simulator for 2-valued logic. |
|
|
|
|
class LogicSim4V(sim.SimOps): |
|
|
|
|
"""A bit-parallel naïve combinational simulator for 4-valued logic. |
|
|
|
|
|
|
|
|
|
Logic values: 0, 1, -, X |
|
|
|
|
|
|
|
|
|
: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) |
|
|
|
|
self.c = np.zeros((self.c_len, 2, nbytes), dtype=np.uint8) |
|
|
|
|
"""Logic values within the combinational portion of the circuit. |
|
|
|
|
|
|
|
|
|
In bit-parallel (bp) storage format. |
|
|
|
|
@ -386,19 +472,19 @@ class LogicSim2V(sim.SimOps):
@@ -386,19 +472,19 @@ class LogicSim2V(sim.SimOps):
|
|
|
|
|
""" |
|
|
|
|
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`). |
|
|
|
|
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`). |
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
@ -408,7 +494,7 @@ class LogicSim2V(sim.SimOps):
@@ -408,7 +494,7 @@ class LogicSim2V(sim.SimOps):
|
|
|
|
|
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,:] |
|
|
|
|
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): |
|
|
|
|
if fault_mask is None: |
|
|
|
|
@ -418,12 +504,12 @@ class LogicSim2V(sim.SimOps):
@@ -418,12 +504,12 @@ class LogicSim2V(sim.SimOps):
|
|
|
|
|
fault_mask2 = np.full(self.c.shape[-1], 0, dtype=np.uint8) |
|
|
|
|
fault_mask2[:len(fault_mask)] = fault_mask |
|
|
|
|
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): |
|
|
|
|
"""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): |
|
|
|
|
"""Copies the result data for all PPOs (flip-flops) to PPIs for a next simulation cycle. |
|
|
|
|
@ -445,11 +531,197 @@ class LogicSim2V(sim.SimOps):
@@ -445,11 +531,197 @@ class LogicSim2V(sim.SimOps):
|
|
|
|
|
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 |
|
|
|
|
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): |
|
|
|
|
"""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 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. |
|
|
|
|
@ -471,6 +743,9 @@ class LogicSim6V(sim.SimOps):
@@ -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. |
|
|
|
|
""" |
|
|
|
|
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): |
|
|
|
|
return f'{{name: "{self.circuit.name}", sims: {self.sims}, c_bytes: {eng(self.c.nbytes)}}}' |
|
|
|
|
@ -480,17 +755,46 @@ class LogicSim6V(sim.SimOps):
@@ -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]) |
|
|
|
|
|
|
|
|
|
def c_prop(self): |
|
|
|
|
c_prop_cpu(self.ops, self.c, self.c_locs, self.tmp_idx, self.tmp2_idx) |
|
|
|
|
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_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): |
|
|
|
|
"""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] |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
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]] |
|
|
|
|
t1 = c[c_locs[tmp2_idx]] |
|
|
|
|
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):
@@ -538,6 +842,84 @@ def c_prop_cpu(ops, c, c_locs, tmp_idx, tmp2_idx):
|
|
|
|
|
o0[0] = i0[0] ^ i1[0] |
|
|
|
|
o0[1] = i0[1] ^ i1[1] |
|
|
|
|
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: |
|
|
|
|
# t1 = ~i2 |
|
|
|
|
t1[...] = i2 ^ inv_op |
|
|
|
|
@ -561,5 +943,8 @@ def c_prop_cpu(ops, c, c_locs, tmp_idx, tmp2_idx):
@@ -561,5 +943,8 @@ def c_prop_cpu(ops, c, c_locs, tmp_idx, tmp2_idx):
|
|
|
|
|
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): |
|
|
|
|
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 |
|
|
|
|
|