Browse Source

docs, fix stil unassigned, fix io_locs for busses

devel
Stefan Holst 1 year 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 @@ @@ -1,3 +1,5 @@
# pip install sphinx sphinx-rtd-theme
#
# Minimal makefile for Sphinx documentation
#

28
src/kyupy/circuit.py

@ -291,6 +291,15 @@ class Circuit: @@ -291,6 +291,15 @@ class Circuit:
@property
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['__node__'] = len(self.nodes)
stats['__cell__'] = len(self.cells)
@ -386,15 +395,31 @@ class Circuit: @@ -386,15 +395,31 @@ class Circuit:
yield stem, region
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))
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)
def _locs(self, prefix, nodes):
d_top = dict()
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]
d = d_top
for j in path[:-1]:
@ -402,6 +427,7 @@ class Circuit: @@ -402,6 +427,7 @@ class Circuit:
d = d[j]
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
l = sorted_values(d_top)
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: @@ -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 = 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.
8-valued logic considers all 3 bits.

9
src/kyupy/logic_sim.py

@ -46,17 +46,12 @@ class LogicSim(sim.SimOps): @@ -46,17 +46,12 @@ class LogicSim(sim.SimOps):
"""
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).
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`)
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]
if self.mdim == 1:

14
src/kyupy/stil.py

@ -95,12 +95,15 @@ class StilFile: @@ -95,12 +95,15 @@ class StilFile:
tests = np.full((len(interface), len(self.patterns)), logic.UNASSIGNED)
for i, p in enumerate(self.patterns):
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[pi_map, i] = logic.mvarray(p.capture['_pi'])
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.
This function assumes a launch-on-capture (LoC) delay test.
@ -112,9 +115,13 @@ class StilFile: @@ -112,9 +115,13 @@ class StilFile:
for i, p in enumerate(self.patterns):
# init.set_values(i, '0' * len(interface))
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[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.s[0] = logic.mv_to_bp(init)
sim8v.s_to_c()
@ -130,6 +137,7 @@ class StilFile: @@ -130,6 +137,7 @@ class StilFile:
if '_pi' in p.capture and 'P' in p.capture['_pi']:
launch[pi_map, i] = logic.mvarray(p.capture['_pi'])
launch[po_map, i] = logic.UNASSIGNED
if launch_filter: launch = launch_filter(launch)
return logic.mv_transition(init, launch)

9
src/kyupy/wave_sim.py

@ -44,12 +44,13 @@ class WaveSim(sim.SimOps): @@ -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
by reducing the number of nodes to simulate, but (interconnect) delay annotations of lines read by fork nodes
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.
"""
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)
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[:, :delays.shape[1]] = delays
@ -72,8 +73,8 @@ class WaveSim(sim.SimOps): @@ -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[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
transitions than specified in ``c_caps``. Some transitions have been discarded, the
final values in the waveforms are still valid.
transitions than specified in ``c_caps``. Some transitions have been discarded, the
final values in the waveforms are still valid.
"""
self.simctl_int = np.zeros((2, sims), dtype=np.int32)
@ -128,7 +129,7 @@ class WaveSim(sim.SimOps): @@ -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)
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
given time, and the sampled captured value of its PPO. Reads and modifies ``self.s``.

Loading…
Cancel
Save