From 31434cfd514f04a751ea9954dfabbbf81ca5e4c9 Mon Sep 17 00:00:00 2001 From: stefan Date: Sun, 9 Nov 2025 08:09:46 +0900 Subject: [PATCH] 4v sim, fixed 6v sim, better testing --- src/kyupy/logic_sim.py | 423 +++++++++++++++++++++++++++-- tests/Makefile | 2 + tests/all_kyupy_simprims.minimal.v | 134 ++++----- tests/all_kyupy_simprims.v | 32 ++- tests/test_logic_sim.py | 76 +++++- 5 files changed, 565 insertions(+), 102 deletions(-) create mode 100644 tests/Makefile diff --git a/src/kyupy/logic_sim.py b/src/kyupy/logic_sim.py index e235a2f..3957d14 100644 --- a/src/kyupy/logic_sim.py +++ b/src/kyupy/logic_sim.py @@ -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): """ 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): 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): 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): 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): """ 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): 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): 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): 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): 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): """ 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): 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): 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 diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..472d104 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,2 @@ +all: + abc -F map_to_minimal.abc \ No newline at end of file diff --git a/tests/all_kyupy_simprims.minimal.v b/tests/all_kyupy_simprims.minimal.v index 1f0f986..17ebbd9 100644 --- a/tests/all_kyupy_simprims.minimal.v +++ b/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 ( i0, i1, i2, i3, \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[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; 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[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] ; - wire new_n42, new_n45, new_n48, new_n51, new_n52, new_n53, new_n54, - new_n55, new_n57, new_n58, new_n59, new_n60, new_n61, new_n62, new_n63, - new_n64, new_n65, new_n66, new_n67, new_n69, new_n70, new_n71, new_n72, - new_n73, new_n74, new_n75, new_n76, new_n77, new_n78, new_n79, new_n83, - new_n84, new_n88, new_n89, new_n93, new_n96, new_n99, new_n100, - new_n101; + \o[24] , \o[25] , \o[26] , \o[27] , \o[28] , \o[29] , \o[30] , \o[31] , + \o[32] ; + wire new_n45, new_n48, new_n51, new_n54, new_n55, new_n56, new_n57, + new_n60, new_n61, new_n62, new_n63, new_n64, new_n65, new_n66, new_n67, + new_n68, new_n69, new_n72, new_n73, new_n74, new_n75, new_n76, new_n77, + new_n78, new_n79, new_n80, new_n81, new_n86, new_n87, new_n91, new_n92, + new_n96, new_n99, new_n102, new_n103, new_n104; INV1 g00(.i0(i1), .o(\o[1] )); AND2 g01(.i0(i1), .i1(i0), .o(\o[2] )); 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 g05(.i0(\o[3] ), .o(\o[6] )); INV1 g06(.i0(\o[4] ), .o(\o[7] )); - INV1 g07(.i0(i0), .o(new_n42)); - AND2 g08(.i0(\o[1] ), .i1(new_n42), .o(\o[11] )); + INV1 g07(.i0(i0), .o(new_n45)); + AND2 g08(.i0(\o[1] ), .i1(new_n45), .o(\o[11] )); INV1 g09(.i0(\o[11] ), .o(\o[8] )); - INV1 g10(.i0(i2), .o(new_n45)); - AND2 g11(.i0(\o[11] ), .i1(new_n45), .o(\o[12] )); + INV1 g10(.i0(i2), .o(new_n48)); + AND2 g11(.i0(\o[11] ), .i1(new_n48), .o(\o[12] )); INV1 g12(.i0(\o[12] ), .o(\o[9] )); - INV1 g13(.i0(i3), .o(new_n48)); - AND2 g14(.i0(\o[12] ), .i1(new_n48), .o(\o[13] )); + INV1 g13(.i0(i3), .o(new_n51)); + AND2 g14(.i0(\o[12] ), .i1(new_n51), .o(\o[13] )); INV1 g15(.i0(\o[13] ), .o(\o[10] )); - AND2 g16(.i0(\o[1] ), .i1(i0), .o(new_n51)); - INV1 g17(.i0(new_n51), .o(new_n52)); - AND2 g18(.i0(i1), .i1(new_n42), .o(new_n53)); - INV1 g19(.i0(new_n53), .o(new_n54)); - AND2 g20(.i0(new_n54), .i1(new_n52), .o(new_n55)); - INV1 g21(.i0(new_n55), .o(\o[14] )); - AND2 g22(.i0(i2), .i1(i1), .o(new_n57)); - INV1 g23(.i0(new_n57), .o(new_n58)); - AND2 g24(.i0(new_n45), .i1(\o[1] ), .o(new_n59)); - INV1 g25(.i0(new_n59), .o(new_n60)); - AND2 g26(.i0(new_n60), .i1(new_n58), .o(new_n61)); - INV1 g27(.i0(new_n61), .o(new_n62)); - AND2 g28(.i0(new_n62), .i1(i0), .o(new_n63)); - INV1 g29(.i0(new_n63), .o(new_n64)); - AND2 g30(.i0(new_n61), .i1(new_n42), .o(new_n65)); - INV1 g31(.i0(new_n65), .o(new_n66)); - AND2 g32(.i0(new_n66), .i1(new_n64), .o(new_n67)); - INV1 g33(.i0(new_n67), .o(\o[15] )); - AND2 g34(.i0(i3), .i1(new_n45), .o(new_n69)); - INV1 g35(.i0(new_n69), .o(new_n70)); - AND2 g36(.i0(new_n48), .i1(i2), .o(new_n71)); - INV1 g37(.i0(new_n71), .o(new_n72)); - AND2 g38(.i0(new_n72), .i1(new_n70), .o(new_n73)); - INV1 g39(.i0(new_n73), .o(new_n74)); - AND2 g40(.i0(new_n74), .i1(new_n55), .o(new_n75)); - INV1 g41(.i0(new_n75), .o(new_n76)); - AND2 g42(.i0(new_n73), .i1(\o[14] ), .o(new_n77)); - INV1 g43(.i0(new_n77), .o(new_n78)); - AND2 g44(.i0(new_n78), .i1(new_n76), .o(new_n79)); - INV1 g45(.i0(new_n79), .o(\o[16] )); - AND2 g46(.i0(\o[5] ), .i1(new_n45), .o(\o[21] )); - INV1 g47(.i0(\o[21] ), .o(\o[17] )); - AND2 g48(.i0(i3), .i1(i2), .o(new_n83)); - INV1 g49(.i0(new_n83), .o(new_n84)); - AND2 g50(.i0(new_n84), .i1(\o[5] ), .o(\o[22] )); - INV1 g51(.i0(\o[22] ), .o(\o[18] )); - AND2 g52(.i0(\o[8] ), .i1(i2), .o(\o[19] )); - AND2 g53(.i0(new_n48), .i1(new_n45), .o(new_n88)); - INV1 g54(.i0(new_n88), .o(new_n89)); - AND2 g55(.i0(new_n89), .i1(\o[8] ), .o(\o[20] )); - INV1 g56(.i0(\o[19] ), .o(\o[23] )); - INV1 g57(.i0(\o[20] ), .o(\o[24] )); - AND2 g58(.i0(\o[5] ), .i1(new_n48), .o(new_n93)); - AND2 g59(.i0(new_n93), .i1(new_n45), .o(\o[27] )); - INV1 g60(.i0(\o[27] ), .o(\o[25] )); - AND2 g61(.i0(\o[8] ), .i1(i3), .o(new_n96)); - AND2 g62(.i0(new_n96), .i1(i2), .o(\o[26] )); - INV1 g63(.i0(\o[26] ), .o(\o[28] )); - AND2 g64(.i0(new_n45), .i1(i0), .o(new_n99)); - INV1 g65(.i0(new_n99), .o(new_n100)); - AND2 g66(.i0(new_n100), .i1(new_n58), .o(new_n101)); - INV1 g67(.i0(new_n101), .o(\o[29] )); + AND2 g16(.i0(\o[1] ), .i1(i0), .o(new_n54)); + INV1 g17(.i0(new_n54), .o(new_n55)); + AND2 g18(.i0(i1), .i1(new_n45), .o(new_n56)); + INV1 g19(.i0(new_n56), .o(new_n57)); + AND2 g20(.i0(new_n57), .i1(new_n55), .o(\o[17] )); + INV1 g21(.i0(\o[17] ), .o(\o[14] )); + AND2 g22(.i0(i2), .i1(i1), .o(new_n60)); + INV1 g23(.i0(new_n60), .o(new_n61)); + AND2 g24(.i0(new_n48), .i1(\o[1] ), .o(new_n62)); + INV1 g25(.i0(new_n62), .o(new_n63)); + AND2 g26(.i0(new_n63), .i1(new_n61), .o(new_n64)); + INV1 g27(.i0(new_n64), .o(new_n65)); + AND2 g28(.i0(new_n65), .i1(i0), .o(new_n66)); + INV1 g29(.i0(new_n66), .o(new_n67)); + AND2 g30(.i0(new_n64), .i1(new_n45), .o(new_n68)); + INV1 g31(.i0(new_n68), .o(new_n69)); + AND2 g32(.i0(new_n69), .i1(new_n67), .o(\o[18] )); + INV1 g33(.i0(\o[18] ), .o(\o[15] )); + AND2 g34(.i0(i3), .i1(new_n48), .o(new_n72)); + INV1 g35(.i0(new_n72), .o(new_n73)); + AND2 g36(.i0(new_n51), .i1(i2), .o(new_n74)); + INV1 g37(.i0(new_n74), .o(new_n75)); + AND2 g38(.i0(new_n75), .i1(new_n73), .o(new_n76)); + INV1 g39(.i0(new_n76), .o(new_n77)); + AND2 g40(.i0(new_n77), .i1(\o[17] ), .o(new_n78)); + INV1 g41(.i0(new_n78), .o(new_n79)); + AND2 g42(.i0(new_n76), .i1(\o[14] ), .o(new_n80)); + INV1 g43(.i0(new_n80), .o(new_n81)); + AND2 g44(.i0(new_n81), .i1(new_n79), .o(\o[19] )); + INV1 g45(.i0(\o[19] ), .o(\o[16] )); + AND2 g46(.i0(\o[5] ), .i1(new_n48), .o(\o[24] )); + INV1 g47(.i0(\o[24] ), .o(\o[20] )); + AND2 g48(.i0(i3), .i1(i2), .o(new_n86)); + INV1 g49(.i0(new_n86), .o(new_n87)); + AND2 g50(.i0(new_n87), .i1(\o[5] ), .o(\o[25] )); + INV1 g51(.i0(\o[25] ), .o(\o[21] )); + AND2 g52(.i0(\o[8] ), .i1(i2), .o(\o[22] )); + AND2 g53(.i0(new_n51), .i1(new_n48), .o(new_n91)); + INV1 g54(.i0(new_n91), .o(new_n92)); + AND2 g55(.i0(new_n92), .i1(\o[8] ), .o(\o[23] )); + INV1 g56(.i0(\o[22] ), .o(\o[26] )); + INV1 g57(.i0(\o[23] ), .o(\o[27] )); + AND2 g58(.i0(\o[5] ), .i1(new_n51), .o(new_n96)); + AND2 g59(.i0(new_n96), .i1(new_n48), .o(\o[30] )); + INV1 g60(.i0(\o[30] ), .o(\o[28] )); + AND2 g61(.i0(\o[8] ), .i1(i3), .o(new_n99)); + AND2 g62(.i0(new_n99), .i1(i2), .o(\o[29] )); + INV1 g63(.i0(\o[29] ), .o(\o[31] )); + AND2 g64(.i0(new_n48), .i1(i0), .o(new_n102)); + INV1 g65(.i0(new_n102), .o(new_n103)); + AND2 g66(.i0(new_n103), .i1(new_n61), .o(new_n104)); + INV1 g67(.i0(new_n104), .o(\o[32] )); BUF1 g68(.i0(i0), .o(\o[0] )); endmodule diff --git a/tests/all_kyupy_simprims.v b/tests/all_kyupy_simprims.v index d3ce2f4..3279512 100644 --- a/tests/all_kyupy_simprims.v +++ b/tests/all_kyupy_simprims.v @@ -3,7 +3,7 @@ module all_kyupy_primitives (i0, i1, i2, i3, o); input i1; input i2; input i3; - output [29:0] o; + output [32:0] o; BUF1 buf1_0 (.i0(i0), .o(o[0])); 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])); 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])); - AO22 ao22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[18])); - OA21 oa21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[19])); - OA22 oa22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[20])); + XNOR2 xnor2_0 (.i0(i0), .i1(i1), .o(o[17])); + XNOR3 xnor3_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[18])); + XNOR4 xnor4_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[19])); - AOI21 aoi21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[21])); - AOI22 aoi22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[22])); - OAI21 oai21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[23])); - OAI22 oai22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[24])); + AO21 ao21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[20])); + AO22 ao22_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[21])); + OA21 oa21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[22])); + 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])); - OA211 oa211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[26])); + AOI21 aoi21_0 (.i0(i0), .i1(i1), .i2(i2), .o(o[24])); + 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])); - OAI211 oai211_0 (.i0(i0), .i1(i1), .i2(i2), .i3(i3), .o(o[28])); + AO211 ao211_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 \ No newline at end of file diff --git a/tests/test_logic_sim.py b/tests/test_logic_sim.py index 2345de5..c4b7cc3 100644 --- a/tests/test_logic_sim.py +++ b/tests/test_logic_sim.py @@ -1,6 +1,6 @@ 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.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 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') c2 = verilog.load(mydir / 'all_kyupy_simprims.minimal.v') 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')], 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(): c = bench.parse(f''' input(i3, i2, i1, i0)