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. 48
      src/kyupy/logic_sim.py

34
src/kyupy/circuit.py

@ -235,6 +235,10 @@ class Circuit: @@ -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: @@ -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: @@ -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

27
src/kyupy/logic.py

@ -1,4 +1,4 @@ @@ -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 @@ @@ -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): @@ -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

48
src/kyupy/logic_sim.py

@ -95,7 +95,7 @@ class LogicSim(SimOps): @@ -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): @@ -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): @@ -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]

Loading…
Cancel
Save