Browse Source

docs, fix stil unassigned, fix io_locs for busses

devel
Stefan Holst 2 years ago
parent
commit
0968cb451e
  1. 2
      docs/Makefile
  2. 28
      src/kyupy/circuit.py
  3. 2
      src/kyupy/logic.py
  4. 9
      src/kyupy/logic_sim.py
  5. 14
      src/kyupy/stil.py
  6. 9
      src/kyupy/wave_sim.py

2
docs/Makefile

@ -1,3 +1,5 @@
# pip install sphinx sphinx-rtd-theme
#
# Minimal makefile for Sphinx documentation # Minimal makefile for Sphinx documentation
# #

28
src/kyupy/circuit.py

@ -291,6 +291,15 @@ class Circuit:
@property @property
def stats(self): def stats(self):
"""Generates a dictionary with the number of all different elements in the circuit.
The dictionary contains the number of all different kinds of nodes, the number
of lines, as well various sums like number of combinational gates, number of
primary I/Os, number of sequential elements, and so on.
The count of regular cells use their `Node.kind` as key, other statistics use
dunder-keys like: `__comb__`, `__io__`, `__seq__`, and so on.
"""
stats = defaultdict(int) stats = defaultdict(int)
stats['__node__'] = len(self.nodes) stats['__node__'] = len(self.nodes)
stats['__cell__'] = len(self.cells) stats['__cell__'] = len(self.cells)
@ -386,15 +395,31 @@ class Circuit:
yield stem, region yield stem, region
def io_locs(self, prefix): def io_locs(self, prefix):
"""Returns the indices of primary I/Os that start with given name prefix.
The returned values are used to index into the :py:attr:`io_nodes` array.
If only one I/O cell matches the given prefix, a single integer is returned.
If a bus matches the given prefix, a sorted list of indices is returned.
Busses are identified by integers in the cell names following the given prefix.
Lists for bus indices are sorted from LSB (e.g. `data[0]`) to MSB (e.g. `data[31]`).
If a prefix matches multiple different signals or busses, alphanumerically sorted
lists of lists are returned. Therefore, higher-dimensional busses
(e.g. `data0[0], data0[1], ...`, `data1[0], data1[1], ...`) are supported as well.
"""
return self._locs(prefix, list(self.io_nodes)) return self._locs(prefix, list(self.io_nodes))
def s_locs(self, prefix): def s_locs(self, prefix):
"""Returns the indices of I/Os and sequential elements that start with given name prefix.
The returned values are used to index into the :py:attr:`s_nodes` array.
It works the same as :py:attr:`io_locs`. See there for more details.
"""
return self._locs(prefix, self.s_nodes) return self._locs(prefix, self.s_nodes)
def _locs(self, prefix, nodes): def _locs(self, prefix, nodes):
d_top = dict() d_top = dict()
for i, n in enumerate(nodes): 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
for j in path[:-1]: for j in path[:-1]:
@ -402,6 +427,7 @@ class Circuit:
d = d[j] d = d[j]
d[path[-1]] = i d[path[-1]] = i
# sort recursively for multi-dimensional lists.
def sorted_values(d): return [sorted_values(v) for k, v in sorted(d.items())] if isinstance(d, dict) else d def sorted_values(d): return [sorted_values(v) for k, v in sorted(d.items())] if isinstance(d, dict) else d
l = sorted_values(d_top) l = sorted_values(d_top)
while isinstance(l, list) and len(l) == 1: l = l[0] while isinstance(l, list) and len(l) == 1: l = l[0]

2
src/kyupy/logic.py

@ -17,7 +17,7 @@ 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 = 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. * 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. 2-valued logic only considers bit0, but should store logic one as ``ONE=0b011`` for interoperability.
4-valued logic only considers bit0 and bit1. 4-valued logic only considers bit0 and bit1.
8-valued logic considers all 3 bits. 8-valued logic considers all 3 bits.

9
src/kyupy/logic_sim.py

@ -46,17 +46,12 @@ class LogicSim(sim.SimOps):
""" """
self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs, :self.mdim] self.c[self.pippi_c_locs] = self.s[0, self.pippi_s_locs, :self.mdim]
def c_to_s(self): #, responses, ff_transitions=False): def c_to_s(self):
"""Capture the current values at the primary outputs and in the state-elements (flip-flops). """Capture the current values at the primary outputs and in the state-elements (flip-flops).
For primary outputs, the logic value is stored unmodified in the given target array. For primary outputs, the logic value is stored unmodified in the given target array.
For flip-flops, the logic value is either stored unmodified (`ff_transitions=False`) For flip-flops, the logic value is either stored unmodified (`ff_transitions=False`)
or constructed from the previous state and the new state (`ff_transitions=True`). or constructed from the previous state and the new state (`ff_transitions=True`).
:param responses: A bit-parallel storage target for the responses in a compatible shape.
:type responses: :py:class:`~kyupy.logic.BPArray`
:param ff_transitions: If true, calculate and store the transitions between the previous state
(the currently assigned pattern) and the new state.
:returns: The given responses object.
""" """
self.s[1, self.poppo_s_locs, :self.mdim] = 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: if self.mdim == 1:

14
src/kyupy/stil.py

@ -95,12 +95,15 @@ class StilFile:
tests = np.full((len(interface), len(self.patterns)), logic.UNASSIGNED) tests = np.full((len(interface), len(self.patterns)), logic.UNASSIGNED)
for i, p in enumerate(self.patterns): for i, p in enumerate(self.patterns):
for si_port in self.si_ports.keys(): for si_port in self.si_ports.keys():
pattern = logic.mv_xor(logic.mvarray(p.load[si_port]), scan_inversions[si_port]) pattern = logic.mvarray(p.load[si_port])
inversions = np.choose((pattern == logic.UNASSIGNED) | (pattern == logic.UNKNOWN),
[scan_inversions[si_port], logic.ZERO]).astype(np.uint8)
np.bitwise_xor(pattern, inversions, out=pattern)
tests[scan_maps[si_port], i] = pattern tests[scan_maps[si_port], i] = pattern
tests[pi_map, i] = logic.mvarray(p.capture['_pi']) tests[pi_map, i] = logic.mvarray(p.capture['_pi'])
return tests return tests
def tests_loc(self, circuit): def tests_loc(self, circuit, init_filter=None, launch_filter=None):
"""Assembles and returns a LoC scan test pattern set for given circuit. """Assembles and returns a LoC scan test pattern set for given circuit.
This function assumes a launch-on-capture (LoC) delay test. This function assumes a launch-on-capture (LoC) delay test.
@ -112,9 +115,13 @@ class StilFile:
for i, p in enumerate(self.patterns): for i, p in enumerate(self.patterns):
# init.set_values(i, '0' * len(interface)) # init.set_values(i, '0' * len(interface))
for si_port in self.si_ports.keys(): for si_port in self.si_ports.keys():
pattern = logic.mv_xor(logic.mvarray(p.load[si_port]), scan_inversions[si_port]) pattern = logic.mvarray(p.load[si_port])
inversions = np.choose((pattern == logic.UNASSIGNED) | (pattern == logic.UNKNOWN),
[scan_inversions[si_port], logic.ZERO]).astype(np.uint8)
np.bitwise_xor(pattern, inversions, out=pattern)
init[scan_maps[si_port], i] = pattern init[scan_maps[si_port], i] = pattern
init[pi_map, i] = logic.mvarray(p.launch['_pi'] if '_pi' in p.launch else p.capture['_pi']) init[pi_map, i] = logic.mvarray(p.launch['_pi'] if '_pi' in p.launch else p.capture['_pi'])
if init_filter: init = init_filter(init)
sim8v = LogicSim(circuit, init.shape[-1], m=8) sim8v = LogicSim(circuit, init.shape[-1], m=8)
sim8v.s[0] = logic.mv_to_bp(init) sim8v.s[0] = logic.mv_to_bp(init)
sim8v.s_to_c() sim8v.s_to_c()
@ -130,6 +137,7 @@ class StilFile:
if '_pi' in p.capture and 'P' in p.capture['_pi']: if '_pi' in p.capture and 'P' in p.capture['_pi']:
launch[pi_map, i] = logic.mvarray(p.capture['_pi']) launch[pi_map, i] = logic.mvarray(p.capture['_pi'])
launch[po_map, i] = logic.UNASSIGNED launch[po_map, i] = logic.UNASSIGNED
if launch_filter: launch = launch_filter(launch)
return logic.mv_transition(init, launch) return logic.mv_transition(init, launch)

9
src/kyupy/wave_sim.py

@ -44,12 +44,13 @@ class WaveSim(sim.SimOps):
:param strip_forks: If enabled, the simulator will not evaluate fork nodes explicitly. This saves simulation time :param strip_forks: If enabled, the simulator will not evaluate fork nodes explicitly. This saves simulation time
by reducing the number of nodes to simulate, but (interconnect) delay annotations of lines read by fork nodes by reducing the number of nodes to simulate, but (interconnect) delay annotations of lines read by fork nodes
are ignored. are ignored.
:param keep_waveforms: If disabled, memory of intermediate signal waveforms will be re-used. This greatly reduces :param c_reuse: If enabled, memory of intermediate signal waveforms will be re-used. This greatly reduces
memory footprint, but intermediate signal waveforms become unaccessible after a propagation. memory footprint, but intermediate signal waveforms become unaccessible after a propagation.
""" """
def __init__(self, circuit, delays, sims=8, c_caps=16, c_reuse=False, strip_forks=False): def __init__(self, circuit, delays, sims=8, c_caps=16, c_reuse=False, strip_forks=False):
super().__init__(circuit, c_caps=c_caps, c_caps_min=4, c_reuse=c_reuse, strip_forks=strip_forks) super().__init__(circuit, c_caps=c_caps, c_caps_min=4, c_reuse=c_reuse, strip_forks=strip_forks)
self.sims = sims self.sims = sims
if delays.ndim == 3: delays = np.expand_dims(delays, axis=0)
self.delays = np.zeros((len(delays), self.c_locs_len, 2, 2), dtype=delays.dtype) self.delays = np.zeros((len(delays), self.c_locs_len, 2, 2), dtype=delays.dtype)
self.delays[:, :delays.shape[1]] = delays self.delays[:, :delays.shape[1]] = delays
@ -72,8 +73,8 @@ class WaveSim(sim.SimOps):
* ``s[8]`` (P)PO sampled capture value: decided by random sampling according to a given seed. * ``s[8]`` (P)PO sampled capture value: decided by random sampling according to a given seed.
* ``s[9]`` (P)PO sampled capture slack: (capture time - LST) - decided by random sampling according to a given seed. * ``s[9]`` (P)PO sampled capture slack: (capture time - LST) - decided by random sampling according to a given seed.
* ``s[10]`` Overflow indicator: If non-zero, some signals in the input cone of this output had more * ``s[10]`` Overflow indicator: If non-zero, some signals in the input cone of this output had more
transitions than specified in ``c_caps``. Some transitions have been discarded, the transitions than specified in ``c_caps``. Some transitions have been discarded, the
final values in the waveforms are still valid. final values in the waveforms are still valid.
""" """
self.simctl_int = np.zeros((2, sims), dtype=np.int32) self.simctl_int = np.zeros((2, sims), dtype=np.int32)
@ -128,7 +129,7 @@ class WaveSim(sim.SimOps):
self.s[3:, s_loc, vector] = wave_capture_cpu(self.c, c_loc, c_len, vector, time=time, sd=sd, seed=seed) self.s[3:, s_loc, vector] = wave_capture_cpu(self.c, c_loc, c_len, vector, time=time, sd=sd, seed=seed)
def s_ppo_to_ppi(self, time=0.0): def s_ppo_to_ppi(self, time=0.0):
"""Re-assigns the last sampled capture to the appropriate pseudo-primary inputs (PPI). """Re-assigns the last sampled capture to the appropriate pseudo-primary inputs (PPI).
Each PPI transition is constructed from its previous final value, the Each PPI transition is constructed from its previous final value, the
given time, and the sampled captured value of its PPO. Reads and modifies ``self.s``. given time, and the sampled captured value of its PPO. Reads and modifies ``self.s``.

Loading…
Cancel
Save