diff --git a/src/kyupy/logic_sim.py b/src/kyupy/logic_sim.py index 0aca25c..8f35d2c 100644 --- a/src/kyupy/logic_sim.py +++ b/src/kyupy/logic_sim.py @@ -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): 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 diff --git a/tests/test_logic_sim.py b/tests/test_logic_sim.py index 8086c3e..27b8990 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 +from kyupy.logic_sim import LogicSim, LogicSim6V from kyupy import bench, logic, sim from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp @@ -94,6 +94,30 @@ def test_4v(): '--0XX', '--X1X', '--XXX', '--XXX')) +def test_6v(): + c = bench.parse('input(x, y) output(a, o, n, xo, no) a=AND2(x,y) o=OR2(x,y) n=INV1(x) xo=XOR2(x,y) no=NOR2(x,y)') + s = LogicSim6V(c, 36) + assert s.s_len == 7 + mva = mvarray( + '0000101', '0101110', '0R0R1RF', '0F0F1FR', '0P0P1PN', '0N0N1NP', + '1001010', '1111000', '1RR10F0', '1FF10R0', '1PP10N0', '1NN10P0', + 'R00RFRF', 'R1R1FF0', 'RRRRFPF', 'RFPNFNP', 'RPPRFRF', 'RNRNFFP', + 'F00FRFR', 'F1F1RR0', 'FRPNRNP', 'FFFFRPR', 'FPPFRFR', 'FNFNRRP', + 'P00PNPN', 'P1P1NN0', 'PRPRNRF', 'PFPFNFR', 'PPPPNPN', 'PNPNNNP', + 'N00NPNP', 'N1N1PP0', 'NRRNPFP', 'NFFNPRP', 'NPPNPNP', 'NNNNPPP') + tests = np.copy(mva) + tests[2:] = logic.ZERO + s.s[0] = tests + s.s_to_c() + s.c_prop() + s.c_to_s() + resp = s.s[1].copy() + + exp_resp = np.copy(mva) + exp_resp[:2] = logic.ZERO + np.testing.assert_allclose(resp, exp_resp) + + def test_8v(): c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)') s = LogicSim(c, 64, m=8)