diff --git a/src/kyupy/circuit.py b/src/kyupy/circuit.py index 23e30bf..29413f2 100644 --- a/src/kyupy/circuit.py +++ b/src/kyupy/circuit.py @@ -235,6 +235,10 @@ class Circuit: """A dictionary to access forks by name. """ + @property + def s_nodes(self): + return list(self.io_nodes) + [n for n in self.nodes if 'dff' in n.kind.lower()] + [n for n in self.nodes if 'latch' in n.kind.lower()] + def get_or_add_fork(self, name): return self.forks[name] if name in self.forks else Node(self, name) @@ -294,11 +298,23 @@ class Circuit: f'lines={len(self.lines)} ports={len(self.io_nodes)}>' @property - def cell_counts(self): - counts = defaultdict(int) + def stats(self): + stats = defaultdict(int) + stats['__node__'] = len(self.nodes) + stats['__cell__'] = len(self.cells) + stats['__fork__'] = len(self.forks) + stats['__port__'] = len(self.io_nodes) + stats['__line__'] = len(self.lines) for n in self.cells.values(): - counts[n.kind] += 1 - return counts + stats[n.kind] += 1 + if 'dff' in n.kind.lower(): + stats['__dff__'] += 1 + elif 'latch' in n.kind.lower(): + stats['__latch__'] += 1 + elif 'put' not in n.kind.lower(): # no input or output + stats['__comb__'] += 1 + stats['__seq__'] = stats['__dff__'] + stats['__latch__'] + return stats def topological_order(self): """Generator function to iterate over all nodes in topological order. @@ -380,9 +396,15 @@ class Circuit: region.append(n) yield stem, region - def io_loc(self, prefix): + def io_locs(self, prefix): + return self._locs(prefix, list(self.io_nodes)) + + def s_locs(self, prefix): + return self._locs(prefix, self.s_nodes) + + def _locs(self, prefix, nodes): d_top = dict() - for i, n in enumerate(list(self.io_nodes) + [n for n in self.nodes if 'dff' in n.kind.lower()]): + for i, n in enumerate(nodes): if m := re.match(fr'({prefix}.*?)((?:\d+[_\[\]])*$)', n.name): path = [m[1]] + [int(v) for v in re.split(r'[_\[\]]+', m[2]) if len(v) > 0] d = d_top diff --git a/src/kyupy/logic.py b/src/kyupy/logic.py index c6dc199..0d1ccdb 100644 --- a/src/kyupy/logic.py +++ b/src/kyupy/logic.py @@ -1,4 +1,4 @@ -"""This module contains definitions and data structures for 2-, 4-, and 8-valued logic operations. +"""This module contains definitions and utilities for 2-, 4-, and 8-valued logic operations. 8 logic values are defined as integer constants. @@ -6,18 +6,22 @@ * 4-valued logic adds: ``UNASSIGNED`` and ``UNKNOWN`` * 8-valued logic adds: ``RISE``, ``FALL``, ``PPULSE``, and ``NPULSE``. -The bits in these constants have the following meaning: +In general, the bits in these constants have the following meaning: - * bit 0: Final/settled binary value of a signal - * bit 1: Initial binary value of a signal - * bit 2: Activity or transitions are present on a signal + * bit0: Final/settled binary value of a signal + * bit1: Initial binary value of a signal + * bit2: Activity or transitions are present on a signal + +Except when bit0 differs from bit1, but bit2 (activity) is 0: + + * bit0 = 1, bit1 = 0, bit2 = 0 means ``UNKNOWN`` in 4-valued and 8-valued logic. + * bit0 = 0, bit1 = 1, bit2 = 0 means ``UNASSIGNED`` in 4-valued and 8-valued logic. + +2-valued logic only considers bit0, but should store logic one as ``ONE=0b011`` for interoerability. +4-valued logic only considers bit0 and bit1. +8-valued logic considers all 3 bits. -Special meaning is given to values where bits 0 and 1 differ, but bit 2 (activity) is 0. -These values are interpreted as ``UNKNOWN`` or ``UNASSIGNED`` in 4-valued and 8-valued logic. -In general, 2-valued logic only considers bit 0, 4-valued logic considers bits 0 and 1, and 8-valued logic -considers all 3 bits. -The only exception is constant ``ONE=0b11`` which has two bits set for all logics including 2-valued logic. """ import math @@ -279,8 +283,7 @@ def mv_transition(init, final, out=None): def bp_buf(out, inp): - unknown = inp[..., 0, :] ^ inp[..., 1, :] - unknown &= ~inp[..., 2, :] + unknown = (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :] out[..., 0, :] = inp[..., 0, :] | unknown out[..., 1, :] = inp[..., 1, :] & ~unknown out[..., 2, :] = inp[..., 2, :] & ~unknown diff --git a/src/kyupy/logic_sim.py b/src/kyupy/logic_sim.py index 6f44c15..dd9a45c 100644 --- a/src/kyupy/logic_sim.py +++ b/src/kyupy/logic_sim.py @@ -95,7 +95,7 @@ class LogicSim(SimOps): :type stimuli: :py:class:`~kyupy.logic.BPArray` :returns: The given stimuli object. """ - self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs] + self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs, :self.mdim] # for node, stim in zip(self.interface, stimuli.data if hasattr(stimuli, 'data') else stimuli): # if len(node.outs) == 0: continue # if node.index in self.latch_dict: @@ -125,7 +125,10 @@ class LogicSim(SimOps): (the currently assigned pattern) and the new state. :returns: The given responses object. """ - self.s[1, self.poppo_s_locs] = self.c[self.poppo_c_locs] + self.s[1, self.poppo_s_locs, :self.mdim] = self.c[self.poppo_c_locs] + if self.mdim == 1: + self.s[1, self.poppo_s_locs, 1:2] = self.c[self.poppo_c_locs] + # for node, resp in zip(self.interface, responses.data if hasattr(responses, 'data') else responses): # if len(node.ins) == 0: continue # if node.index in self.latch_dict: @@ -165,18 +168,35 @@ class LogicSim(SimOps): resumes with the manipulated values after the callback returns. :type inject_cb: ``f(Line, ndarray)`` """ - for op, o0, i0, i1, i2, i3 in self.ops: - o0, i0, i1, i2, i3 = [self.vat[x,0] for x in (o0, i0, i1, i2, i3)] - if op == SimPrim.BUF1: self.c[o0]=self.c[i0] - elif op == SimPrim.INV1: logic.bp_not(self.c[o0], self.c[i0]) - elif op == SimPrim.AND2: logic.bp_and(self.c[o0], self.c[i0], self.c[i1]) - elif op == SimPrim.NAND2: logic.bp_and(self.c[o0], self.c[i0], self.c[i1]); logic.bp_not(self.c[o0], self.c[o0]) - elif op == SimPrim.OR2: logic.bp_or(self.c[o0], self.c[i0], self.c[i1]) - elif op == SimPrim.NOR2: logic.bp_or(self.c[o0], self.c[i0], self.c[i1]); logic.bp_not(self.c[o0], self.c[o0]) - elif op == SimPrim.XOR2: logic.bp_xor(self.c[o0], self.c[i0], self.c[i1]) - elif op == SimPrim.XNOR2: logic.bp_xor(self.c[o0], self.c[i0], self.c[i1]); logic.bp_not(self.c[o0], self.c[o0]) - else: print(f'unknown SimPrim {op}') - if inject_cb is not None: inject_cb(o0, self.s[o0]) + if self.m == 2: + for op, o0, i0, i1, i2, i3 in self.ops: + o0, i0, i1, i2, i3 = [self.vat[x,0] for x in (o0, i0, i1, i2, i3)] + if op == SimPrim.BUF1: self.c[o0]=self.c[i0] + elif op == SimPrim.INV1: self.c[o0] = ~self.c[i0] + elif op == SimPrim.AND2: self.c[o0] = self.c[i0] & self.c[i1] + elif op == SimPrim.NAND2: self.c[o0] = ~(self.c[i0] & self.c[i1]) + elif op == SimPrim.OR2: self.c[o0] = self.c[i0] | self.c[i1] + elif op == SimPrim.NOR2: self.c[o0] = ~(self.c[i0] | self.c[i1]) + elif op == SimPrim.XOR2: self.c[o0] = self.c[i0] ^ self.c[i1] + elif op == SimPrim.XNOR2: self.c[o0] = ~(self.c[i0] ^ self.c[i1]) + else: print(f'unknown SimPrim {op}') + if inject_cb is not None: inject_cb(o0, self.s[o0]) + pass + elif self.m == 4: + pass + else: + for op, o0, i0, i1, i2, i3 in self.ops: + o0, i0, i1, i2, i3 = [self.vat[x,0] for x in (o0, i0, i1, i2, i3)] + if op == SimPrim.BUF1: self.c[o0]=self.c[i0] + elif op == SimPrim.INV1: logic.bp_not(self.c[o0], self.c[i0]) + elif op == SimPrim.AND2: logic.bp_and(self.c[o0], self.c[i0], self.c[i1]) + elif op == SimPrim.NAND2: logic.bp_and(self.c[o0], self.c[i0], self.c[i1]); logic.bp_not(self.c[o0], self.c[o0]) + elif op == SimPrim.OR2: logic.bp_or(self.c[o0], self.c[i0], self.c[i1]) + elif op == SimPrim.NOR2: logic.bp_or(self.c[o0], self.c[i0], self.c[i1]); logic.bp_not(self.c[o0], self.c[o0]) + elif op == SimPrim.XOR2: logic.bp_xor(self.c[o0], self.c[i0], self.c[i1]) + elif op == SimPrim.XNOR2: logic.bp_xor(self.c[o0], self.c[i0], self.c[i1]); logic.bp_not(self.c[o0], self.c[o0]) + else: print(f'unknown SimPrim {op}') + if inject_cb is not None: inject_cb(o0, self.s[o0]) # for node in self.circuit.topological_order(): # if self.state_epoch[node] != self.epoch: continue # inputs = [self.state[line] if line else self.zero for line in node.ins]