Browse Source

better circuit statsu, 2v logic sim

devel
stefan 2 years ago
parent
commit
89f317b463
  1. 34
      src/kyupy/circuit.py
  2. 27
      src/kyupy/logic.py
  3. 24
      src/kyupy/logic_sim.py

34
src/kyupy/circuit.py

@ -235,6 +235,10 @@ class Circuit:
"""A dictionary to access forks by name. """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): def get_or_add_fork(self, name):
return self.forks[name] if name in self.forks else Node(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)}>' f'lines={len(self.lines)} ports={len(self.io_nodes)}>'
@property @property
def cell_counts(self): def stats(self):
counts = defaultdict(int) 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(): for n in self.cells.values():
counts[n.kind] += 1 stats[n.kind] += 1
return counts 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): def topological_order(self):
"""Generator function to iterate over all nodes in topological order. """Generator function to iterate over all nodes in topological order.
@ -380,9 +396,15 @@ class Circuit:
region.append(n) region.append(n)
yield stem, region 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() 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): 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] path = [m[1]] + [int(v) for v in re.split(r'[_\[\]]+', m[2]) if len(v) > 0]
d = d_top d = d_top

27
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. 8 logic values are defined as integer constants.
@ -6,18 +6,22 @@
* 4-valued logic adds: ``UNASSIGNED`` and ``UNKNOWN`` * 4-valued logic adds: ``UNASSIGNED`` and ``UNKNOWN``
* 8-valued logic adds: ``RISE``, ``FALL``, ``PPULSE``, and ``NPULSE``. * 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 * bit0: Final/settled binary value of a signal
* bit 1: Initial binary value of a signal * bit1: Initial binary value of a signal
* bit 2: Activity or transitions are present on 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 import math
@ -279,8 +283,7 @@ def mv_transition(init, final, out=None):
def bp_buf(out, inp): def bp_buf(out, inp):
unknown = inp[..., 0, :] ^ inp[..., 1, :] unknown = (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :]
unknown &= ~inp[..., 2, :]
out[..., 0, :] = inp[..., 0, :] | unknown out[..., 0, :] = inp[..., 0, :] | unknown
out[..., 1, :] = inp[..., 1, :] & ~unknown out[..., 1, :] = inp[..., 1, :] & ~unknown
out[..., 2, :] = inp[..., 2, :] & ~unknown out[..., 2, :] = inp[..., 2, :] & ~unknown

24
src/kyupy/logic_sim.py

@ -95,7 +95,7 @@ class LogicSim(SimOps):
:type stimuli: :py:class:`~kyupy.logic.BPArray` :type stimuli: :py:class:`~kyupy.logic.BPArray`
:returns: The given stimuli object. :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): # for node, stim in zip(self.interface, stimuli.data if hasattr(stimuli, 'data') else stimuli):
# if len(node.outs) == 0: continue # if len(node.outs) == 0: continue
# if node.index in self.latch_dict: # if node.index in self.latch_dict:
@ -125,7 +125,10 @@ class LogicSim(SimOps):
(the currently assigned pattern) and the new state. (the currently assigned pattern) and the new state.
:returns: The given responses object. :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): # for node, resp in zip(self.interface, responses.data if hasattr(responses, 'data') else responses):
# if len(node.ins) == 0: continue # if len(node.ins) == 0: continue
# if node.index in self.latch_dict: # if node.index in self.latch_dict:
@ -165,6 +168,23 @@ class LogicSim(SimOps):
resumes with the manipulated values after the callback returns. resumes with the manipulated values after the callback returns.
:type inject_cb: ``f(Line, ndarray)`` :type inject_cb: ``f(Line, ndarray)``
""" """
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: 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)] 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] if op == SimPrim.BUF1: self.c[o0]=self.c[i0]

Loading…
Cancel
Save