|
|
|
@ -13,6 +13,7 @@ import numpy as np
@@ -13,6 +13,7 @@ import numpy as np
|
|
|
|
|
from . import numba, logic, hr_bytes, sim, eng, cdiv |
|
|
|
|
from .circuit import Circuit |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LogicSim(sim.SimOps): |
|
|
|
|
"""A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic. |
|
|
|
|
|
|
|
|
@ -334,3 +335,101 @@ def _prop_cpu(ops, c_locs, c):
@@ -334,3 +335,101 @@ def _prop_cpu(ops, c_locs, c):
|
|
|
|
|
elif op == sim.OAI211: c[o0] = ~((c[i0] | c[i1]) & c[i2] & c[i3]) |
|
|
|
|
elif op == sim.MUX21: c[o0] = (c[i0] & ~c[i2]) | (c[i1] & c[i2]) |
|
|
|
|
else: print(f'unknown op {op}') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LogicSim6V(sim.SimOps): |
|
|
|
|
"""A bit-parallel naïve combinational simulator for 6-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. |
|
|
|
|
""" |
|
|
|
|
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, 3, nbytes), dtype=np.uint8) |
|
|
|
|
self.s = np.zeros((2, self.s_len, self.sims), dtype=np.uint8) |
|
|
|
|
"""Logic values of the sequential elements (flip-flops) and ports. |
|
|
|
|
|
|
|
|
|
It is a pair of arrays in mv storage format: |
|
|
|
|
|
|
|
|
|
* ``s[0]`` Assigned values. Simulator will read (P)PI value from here. |
|
|
|
|
* ``s[1]`` Result values. Simulator will write (P)PO values here. |
|
|
|
|
|
|
|
|
|
Access this array to assign new values to the (P)PIs or read values from the (P)POs. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
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 ``s[0]`` to the inputs of the combinational portion. |
|
|
|
|
""" |
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
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] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@numba.njit |
|
|
|
|
def c_prop_cpu(ops, c, c_locs): |
|
|
|
|
inv_op = np.array([255, 255, 0], dtype=np.uint8)[np.newaxis, :, np.newaxis] |
|
|
|
|
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: |
|
|
|
|
o0[...] = i0 |
|
|
|
|
elif op == sim.AND2 or op == sim.NAND2: |
|
|
|
|
o0[0] = i0[0] & i1[0] |
|
|
|
|
o0[1] = i0[1] & i1[1] |
|
|
|
|
o0[2] = (i0[2]&(i1[0]|i1[1]|i1[2])| |
|
|
|
|
i1[2]&(i0[0]|i0[1]|i0[2])) |
|
|
|
|
elif op == sim.AND3 or op == sim.NAND3: |
|
|
|
|
o0[0] = i0[0] & i1[0] & i2[0] |
|
|
|
|
o0[1] = i0[1] & i1[1] & i2[1] |
|
|
|
|
o0[2] = (i0[2]&(i1[0]|i1[1]|i1[2])&(i2[0]|i2[1]|i2[2])| |
|
|
|
|
i1[2]&(i0[0]|i0[1]|i0[2])&(i2[0]|i2[1]|i2[2])| |
|
|
|
|
i2[2]&(i0[0]|i0[1]|i0[2])&(i1[0]|i1[1]|i1[2])) |
|
|
|
|
elif op == sim.AND4 or op == sim.NAND4: |
|
|
|
|
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[0]|i1[1]|i1[2])&(i2[0]|i2[1]|i2[2])&(i3[0]|i3[1]|i3[2])| |
|
|
|
|
i1[2]&(i0[0]|i0[1]|i0[2])&(i2[0]|i2[1]|i2[2])&(i3[0]|i3[1]|i3[2])| |
|
|
|
|
i2[2]&(i0[0]|i0[1]|i0[2])&(i1[0]|i1[1]|i1[2])&(i3[0]|i3[1]|i3[2])| |
|
|
|
|
i3[2]&(i0[0]|i0[1]|i0[2])&(i1[0]|i1[1]|i1[2])&(i2[0]|i2[1]|i2[2])) |
|
|
|
|
elif op == sim.OR2 or op == sim.NOR2: |
|
|
|
|
o0[0] = i0[0] | i1[0] |
|
|
|
|
o0[1] = i0[1] | i1[1] |
|
|
|
|
o0[2] = (i0[2]&(~i1[0]|~i1[1]|i1[2])| |
|
|
|
|
i1[2]&(~i0[0]|~i0[1]|i0[2])) |
|
|
|
|
elif op == sim.OR3 or op == sim.NOR3: |
|
|
|
|
o0[0] = i0[0] | i1[0] | i2[0] |
|
|
|
|
o0[1] = i0[1] | i1[1] | i2[1] |
|
|
|
|
o0[2] = (i0[2]&(~i1[0]|~i1[1]|i1[2])&(~i2[0]|~i2[1]|i2[2])| |
|
|
|
|
i1[2]&(~i0[0]|~i0[1]|i0[2])&(~i2[0]|~i2[1]|i2[2])| |
|
|
|
|
i2[2]&(~i0[0]|~i0[1]|i0[2])&(~i1[0]|~i1[1]|i1[2])) |
|
|
|
|
elif op == sim.OR4 or op == sim.NOR4: |
|
|
|
|
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[0]|~i1[1]|i1[2])&(~i2[0]|~i2[1]|i2[2])&(~i3[0]|~i3[1]|i3[2])| |
|
|
|
|
i1[2]&(~i0[0]|~i0[1]|i0[2])&(~i2[0]|~i2[1]|i2[2])&(~i3[0]|~i3[1]|i3[2])| |
|
|
|
|
i2[2]&(~i0[0]|~i0[1]|i0[2])&(~i1[0]|~i1[1]|i1[2])&(~i3[0]|~i3[1]|i3[2])| |
|
|
|
|
i3[2]&(~i0[0]|~i0[1]|i0[2])&(~i1[0]|~i1[1]|i1[2])&(~i2[0]|~i2[1]|i2[2])) |
|
|
|
|
elif op == sim.XOR2 or op == sim.XNOR2: |
|
|
|
|
o0[0] = i0[0] ^ i1[0] |
|
|
|
|
o0[1] = i0[1] ^ i1[1] |
|
|
|
|
o0[2] = i0[2] | i1[2] |
|
|
|
|
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): |
|
|
|
|
o0[...] = o0 ^ inv_op |
|
|
|
|