Browse Source
- MVArray for multi-valued logic - BPArray for bit-parallel storage layout - Started documenting with Sphinx - Migrated simulators to new BPArraymain v0.0.2
Stefan Holst
4 years ago
32 changed files with 1847 additions and 1476 deletions
@ -1,56 +0,0 @@ |
|||||||
KyuPy - Processing VLSI Circuits With Ease |
|
||||||
========================================== |
|
||||||
|
|
||||||
KyuPy is a python package for high-performance processing and analysis of |
|
||||||
non-hierarchical VLSI designs. Its purpose is to provide a rapid prototyping |
|
||||||
platform to aid and accelerate research in the fields of VLSI test, diagnosis |
|
||||||
and reliability. KyuPy is freely available under the MIT license. |
|
||||||
|
|
||||||
|
|
||||||
Main Features |
|
||||||
------------- |
|
||||||
|
|
||||||
* Partial [lark](https://github.com/lark-parser/lark) parsers for common files used with synthesized designs: |
|
||||||
bench, gate-level verilog, standard delay format (SDF), standard test interface language (STIL) |
|
||||||
* Bit-parallel gate-level 2-, 4-, and 8-valued logic simulation |
|
||||||
* GPU-accelerated high-throughput gate-level timing simulation |
|
||||||
* High-performance through the use of [numpy](https://numpy.org) and [numba](https://numba.pydata.org) |
|
||||||
|
|
||||||
|
|
||||||
Getting Started |
|
||||||
--------------- |
|
||||||
|
|
||||||
KyuPy requires Python 3.6 or newer. |
|
||||||
Install the latest release by running: |
|
||||||
```commandline |
|
||||||
pip3 install --user kyupy |
|
||||||
``` |
|
||||||
For best performance, ensure you have [numba](https://pypi.org/project/numba) installed: |
|
||||||
```commandline |
|
||||||
pip3 install --user numba |
|
||||||
``` |
|
||||||
GPU/CUDA support may [require some additional setup](https://numba.pydata.org/numba-doc/latest/cuda/index.html). |
|
||||||
If CUDA or numba is not available, KyuPy will automatically fall back to slow, pure python execution. |
|
||||||
|
|
||||||
The Jupyter Notebook [UsageExamples.ipynb](https://github.com/s-holst/kyupy/blob/main/UsageExamples.ipynb) on GitHub |
|
||||||
contains some useful examples to get familiar with the API. |
|
||||||
|
|
||||||
|
|
||||||
Development |
|
||||||
----------- |
|
||||||
|
|
||||||
To contribute to KyuPy or simply explore the source code, clone the KyuPy [repository](https://github.com/s-holst/kyupy) on GitHub. |
|
||||||
Within your local checkout, run: |
|
||||||
```commandline |
|
||||||
pip3 install --user -e . |
|
||||||
``` |
|
||||||
to make the kyupy package available in your python environment. |
|
||||||
The source code comes with tests that can be run with: |
|
||||||
``` |
|
||||||
pytest |
|
||||||
``` |
|
||||||
|
|
||||||
KyuPy depends on the following packages: |
|
||||||
* [lark-parser](https://pypi.org/project/lark-parser) |
|
||||||
* [numpy](https://pypi.org/project/numpy) |
|
||||||
* [numba](https://pypi.org/project/numba) (optional, required only for GPU/CUDA support) |
|
@ -0,0 +1,28 @@ |
|||||||
|
KyuPy - Pythonic Processing of VLSI Circuits |
||||||
|
============================================ |
||||||
|
|
||||||
|
KyuPy is a Python package for processing and analysis of non-hierarchical gate-level VLSI designs. |
||||||
|
It contains fundamental building blocks for research software in the fields of VLSI test, diagnosis and reliability: |
||||||
|
|
||||||
|
* Efficient data structures for gate-level circuits and related design data. |
||||||
|
* Partial `lark <https://github.com/lark-parser/lark>`_ parsers for common design files like |
||||||
|
bench, gate-level verilog, standard delay format (SDF), standard test interface language (STIL). |
||||||
|
* Bit-parallel gate-level 2-, 4-, and 8-valued logic simulation. |
||||||
|
* GPU-accelerated high-throughput gate-level timing simulation. |
||||||
|
* High-performance through the use of `numpy <https://numpy.org>`_ and `numba <https://numba.pydata.org>`_. |
||||||
|
|
||||||
|
|
||||||
|
Getting Started |
||||||
|
--------------- |
||||||
|
|
||||||
|
KyuPy is available in `PyPI <https://pypi.org/project/kyupy>`_. |
||||||
|
It requires Python 3.6 or newer, `lark-parser <https://pypi.org/project/lark-parser>`_, and `numpy`_. |
||||||
|
Although optional, `numba`_ should be installed for best performance. |
||||||
|
GPU/CUDA support in numba may `require some additional setup <https://numba.pydata.org/numba-doc/latest/cuda/index.html>`_. |
||||||
|
If numba is not available, KyuPy will automatically fall back to slow, pure Python execution. |
||||||
|
|
||||||
|
The Jupyter Notebook `Demo.ipynb <https://github.com/s-holst/kyupy/blob/main/Demo.ipynb>`_ contains some useful examples to get familiar with the API. |
||||||
|
|
||||||
|
To work with the latest pre-release source code, clone the `KyuPy GitHub repository <https://github.com/s-holst/kyupy>`_. |
||||||
|
Run ``pip3 install --user -e .`` within your local checkout to make the package available in your Python environment. |
||||||
|
The source code comes with tests that can be run with ``pytest``. |
@ -0,0 +1,20 @@ |
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?= |
||||||
|
SPHINXBUILD ?= sphinx-build |
||||||
|
SOURCEDIR = . |
||||||
|
BUILDDIR = _build |
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help: |
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) |
||||||
|
|
||||||
|
.PHONY: help Makefile |
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile |
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) |
@ -0,0 +1,64 @@ |
|||||||
|
# Configuration file for the Sphinx documentation builder. |
||||||
|
# |
||||||
|
# This file only contains a selection of the most common options. For a full |
||||||
|
# list see the documentation: |
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html |
||||||
|
|
||||||
|
# -- Path setup -------------------------------------------------------------- |
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory, |
||||||
|
# add these directories to sys.path here. If the directory is relative to the |
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here. |
||||||
|
# |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import sphinx_rtd_theme |
||||||
|
sys.path.insert(0, os.path.abspath('../src')) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# -- Project information ----------------------------------------------------- |
||||||
|
|
||||||
|
project = 'KyuPy' |
||||||
|
copyright = '2020, Stefan Holst' |
||||||
|
author = 'Stefan Holst' |
||||||
|
|
||||||
|
# The full version, including alpha/beta/rc tags |
||||||
|
release = '0.0.2' |
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration --------------------------------------------------- |
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be |
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |
||||||
|
# ones. |
||||||
|
extensions = [ |
||||||
|
'sphinx.ext.autodoc', |
||||||
|
'sphinx_rtd_theme', |
||||||
|
] |
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory. |
||||||
|
templates_path = ['_templates'] |
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and |
||||||
|
# directories to ignore when looking for source files. |
||||||
|
# This pattern also affects html_static_path and html_extra_path. |
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] |
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ------------------------------------------------- |
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for |
||||||
|
# a list of builtin themes. |
||||||
|
# |
||||||
|
# html_theme = 'alabaster' |
||||||
|
html_theme = 'sphinx_rtd_theme' |
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here, |
||||||
|
# relative to this directory. They are copied after the builtin static files, |
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css". |
||||||
|
html_static_path = ['_static'] |
||||||
|
|
||||||
|
autodoc_default_options = { |
||||||
|
'member-order': 'bysource', |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
Data Structures |
||||||
|
=============== |
||||||
|
|
||||||
|
KyuPy provides two types of core data structures, one for gate-level circuits, and a few others for representing and storing logic data and signal values. |
||||||
|
The data structures are designed to work together nicely with numpy arrays. |
||||||
|
For example, all the nodes and connections in the circuit graph have consecutive integer indices that can be used to access ndarrays with associated data. |
||||||
|
Circuit graphs also define an ordering of inputs, outputs and other nodes to easily process test vector data and alike. |
||||||
|
|
||||||
|
Circuit Graph - :mod:`kyupy.circuit` |
||||||
|
------------------------------------ |
||||||
|
|
||||||
|
.. automodule:: kyupy.circuit |
||||||
|
|
||||||
|
.. autoclass:: kyupy.circuit.Node |
||||||
|
:members: |
||||||
|
|
||||||
|
.. autoclass:: kyupy.circuit.Line |
||||||
|
:members: |
||||||
|
|
||||||
|
.. autoclass:: kyupy.circuit.Circuit |
||||||
|
:members: |
||||||
|
|
||||||
|
Multi-Valued Logic - :mod:`kyupy.logic` |
||||||
|
--------------------------------------- |
||||||
|
|
||||||
|
.. automodule:: kyupy.logic |
||||||
|
:members: |
||||||
|
|
||||||
|
|
@ -0,0 +1,12 @@ |
|||||||
|
.. include:: ../README.rst |
||||||
|
|
||||||
|
API Reference |
||||||
|
------------- |
||||||
|
|
||||||
|
.. toctree:: |
||||||
|
:maxdepth: 2 |
||||||
|
|
||||||
|
datastructures |
||||||
|
parsers |
||||||
|
simulators |
||||||
|
|
@ -0,0 +1,42 @@ |
|||||||
|
Parsers |
||||||
|
======= |
||||||
|
|
||||||
|
KyuPy contains simple (and often incomplete) parsers for common file formats. |
||||||
|
These parsers are tailored to the most common use-cases to keep the grammars and the code-base as simple as possible. |
||||||
|
|
||||||
|
Each of the modules export a function ``parse()`` for parsing a string directly and a function |
||||||
|
``load()`` for loading a file. Files with a '.gz' extension are uncompressed on-the-fly. |
||||||
|
|
||||||
|
|
||||||
|
Verilog - :mod:`kyupy.verilog` |
||||||
|
------------------------------ |
||||||
|
|
||||||
|
.. automodule:: kyupy.verilog |
||||||
|
:members: parse, load |
||||||
|
|
||||||
|
|
||||||
|
Bench Format - :mod:`kyupy.bench` |
||||||
|
--------------------------------- |
||||||
|
|
||||||
|
.. automodule:: kyupy.bench |
||||||
|
:members: parse, load |
||||||
|
|
||||||
|
|
||||||
|
Standard Test Interface Language - :mod:`kyupy.stil` |
||||||
|
---------------------------------------------------- |
||||||
|
|
||||||
|
.. automodule:: kyupy.stil |
||||||
|
:members: parse, load |
||||||
|
|
||||||
|
.. autoclass:: kyupy.stil.StilFile |
||||||
|
:members: |
||||||
|
|
||||||
|
|
||||||
|
Standard Delay Format - :mod:`kyupy.sdf` |
||||||
|
---------------------------------------- |
||||||
|
|
||||||
|
.. automodule:: kyupy.sdf |
||||||
|
:members: parse, load |
||||||
|
|
||||||
|
.. autoclass:: kyupy.sdf.DelayFile |
||||||
|
:members: |
@ -0,0 +1,20 @@ |
|||||||
|
Simulators |
||||||
|
========== |
||||||
|
|
||||||
|
Logic Simulation - :mod:`kyupy.logic_sim` |
||||||
|
----------------------------------------- |
||||||
|
|
||||||
|
.. autoclass:: kyupy.logic_sim.LogicSim |
||||||
|
:members: |
||||||
|
|
||||||
|
|
||||||
|
Timing Simulation - :mod:`kyupy.wave_sim` |
||||||
|
----------------------------------------- |
||||||
|
|
||||||
|
.. automodule:: kyupy.wave_sim |
||||||
|
|
||||||
|
.. autoclass:: kyupy.wave_sim.WaveSim |
||||||
|
:members: |
||||||
|
|
||||||
|
.. autoclass:: kyupy.wave_sim.WaveSimCuda |
||||||
|
:members: |
@ -1,23 +0,0 @@ |
|||||||
import numpy as np |
|
||||||
import importlib.util |
|
||||||
if importlib.util.find_spec('numba') is not None: |
|
||||||
import numba |
|
||||||
else: |
|
||||||
from . import numba |
|
||||||
print('Numba unavailable. Falling back to pure python') |
|
||||||
|
|
||||||
|
|
||||||
_pop_count_lut = np.asarray([bin(x).count('1') for x in range(256)]) |
|
||||||
|
|
||||||
|
|
||||||
def popcount(a): |
|
||||||
return np.sum(_pop_count_lut[a]) |
|
||||||
|
|
||||||
|
|
||||||
_bit_in_lut = np.array([2 ** x for x in range(7, -1, -1)], dtype='uint8') |
|
||||||
|
|
||||||
|
|
||||||
@numba.njit |
|
||||||
def bit_in(a, pos): |
|
||||||
return a[pos >> 3] & _bit_in_lut[pos & 7] |
|
||||||
|
|
@ -0,0 +1,402 @@ |
|||||||
|
"""This module contains definitions and data structures for 2-, 4-, and 8-valued logic operations. |
||||||
|
|
||||||
|
8 logic values are defined as integer constants. |
||||||
|
|
||||||
|
* For 2-valued logic: ``ZERO`` and ``ONE`` |
||||||
|
* 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: |
||||||
|
|
||||||
|
* 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 |
||||||
|
|
||||||
|
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 |
||||||
|
from collections.abc import Iterable |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
|
||||||
|
from . import numba |
||||||
|
|
||||||
|
|
||||||
|
ZERO = 0b000 |
||||||
|
"""Integer constant ``0b000`` for logic-0. ``'0'``, ``0``, ``False``, ``'L'``, and ``'l'`` are interpreted as ``ZERO``. |
||||||
|
""" |
||||||
|
UNKNOWN = 0b001 |
||||||
|
"""Integer constant ``0b001`` for unknown or conflict. ``'X'``, or any other value is interpreted as ``UNKNOWN``. |
||||||
|
""" |
||||||
|
UNASSIGNED = 0b010 |
||||||
|
"""Integer constant ``0b010`` for unassigned or high-impedance. ``'-'``, ``None``, ``'Z'``, and ``'z'`` are |
||||||
|
interpreted as ``UNASSIGNED``. |
||||||
|
""" |
||||||
|
ONE = 0b011 |
||||||
|
"""Integer constant ``0b011`` for logic-1. ``'1'``, ``1``, ``True``, ``'H'``, and ``'h'`` are interpreted as ``ONE``. |
||||||
|
""" |
||||||
|
PPULSE = 0b100 |
||||||
|
"""Integer constant ``0b100`` for positive pulse, meaning initial and final values are 0, but there is some activity |
||||||
|
on a signal. ``'P'``, ``'p'``, and ``'^'`` are interpreted as ``PPULSE``. |
||||||
|
""" |
||||||
|
RISE = 0b101 |
||||||
|
"""Integer constant ``0b110`` for a rising transition. ``'R'``, ``'r'``, and ``'/'`` are interpreted as ``RISE``. |
||||||
|
""" |
||||||
|
FALL = 0b110 |
||||||
|
"""Integer constant ``0b101`` for a falling transition. ``'F'``, ``'f'``, and ``'\\'`` are interpreted as ``FALL``. |
||||||
|
""" |
||||||
|
NPULSE = 0b111 |
||||||
|
"""Integer constant ``0b111`` for negative pulse, meaning initial and final values are 1, but there is some activity |
||||||
|
on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``. |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
def interpret(value): |
||||||
|
if isinstance(value, Iterable) and not (isinstance(value, str) and len(value) == 1): |
||||||
|
return list(map(interpret, value)) |
||||||
|
if value in [0, '0', False, 'L', 'l']: |
||||||
|
return ZERO |
||||||
|
if value in [1, '1', True, 'H', 'h']: |
||||||
|
return ONE |
||||||
|
if value in [None, '-', 'Z', 'z']: |
||||||
|
return UNASSIGNED |
||||||
|
if value in ['R', 'r', '/']: |
||||||
|
return RISE |
||||||
|
if value in ['F', 'f', '\\']: |
||||||
|
return FALL |
||||||
|
if value in ['P', 'p', '^']: |
||||||
|
return PPULSE |
||||||
|
if value in ['N', 'n', 'v']: |
||||||
|
return NPULSE |
||||||
|
return UNKNOWN |
||||||
|
|
||||||
|
|
||||||
|
_bit_in_lut = np.array([2 ** x for x in range(7, -1, -1)], dtype='uint8') |
||||||
|
|
||||||
|
|
||||||
|
@numba.njit |
||||||
|
def bit_in(a, pos): |
||||||
|
return a[pos >> 3] & _bit_in_lut[pos & 7] |
||||||
|
|
||||||
|
|
||||||
|
def mv_cast(*args, m=8): |
||||||
|
return [a if isinstance(a, MVArray) else MVArray(a, m=m) for a in args] |
||||||
|
|
||||||
|
|
||||||
|
def mv_getm(*args): |
||||||
|
return max([a.m for a in args if isinstance(a, MVArray)] + [0]) or 8 |
||||||
|
|
||||||
|
|
||||||
|
def _mv_not(m, out, inp): |
||||||
|
np.bitwise_xor(inp, 0b11, out=out) # this also exchanges UNASSIGNED <-> UNKNOWN |
||||||
|
if m > 2: |
||||||
|
np.putmask(out, (inp == UNKNOWN), UNKNOWN) # restore UNKNOWN |
||||||
|
|
||||||
|
|
||||||
|
def mv_not(x1, out=None): |
||||||
|
m = mv_getm(x1) |
||||||
|
x1 = mv_cast(x1, m=m)[0] |
||||||
|
out = out or MVArray(x1.data.shape, m=m) |
||||||
|
_mv_not(m, out.data, x1.data) |
||||||
|
return out |
||||||
|
|
||||||
|
|
||||||
|
def _mv_or(m, out, *ins): |
||||||
|
if m > 2: |
||||||
|
any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED) |
||||||
|
for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED) |
||||||
|
any_one = (ins[0] == ONE) |
||||||
|
for inp in ins[1:]: any_one |= (inp == ONE) |
||||||
|
|
||||||
|
out[...] = ZERO |
||||||
|
np.putmask(out, any_one, ONE) |
||||||
|
for inp in ins: |
||||||
|
np.bitwise_or(out, inp, out=out, where=~any_one) |
||||||
|
np.putmask(out, (any_unknown & ~any_one), UNKNOWN) |
||||||
|
else: |
||||||
|
out[...] = ZERO |
||||||
|
for inp in ins: np.bitwise_or(out, inp, out=out) |
||||||
|
|
||||||
|
|
||||||
|
def mv_or(x1, x2, out=None): |
||||||
|
m = mv_getm(x1, x2) |
||||||
|
x1, x2 = mv_cast(x1, x2, m=m) |
||||||
|
out = out or MVArray(np.broadcast(x1.data, x2.data).shape, m=m) |
||||||
|
_mv_or(m, out.data, x1.data, x2.data) |
||||||
|
return out |
||||||
|
|
||||||
|
|
||||||
|
def _mv_and(m, out, *ins): |
||||||
|
if m > 2: |
||||||
|
any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED) |
||||||
|
for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED) |
||||||
|
any_zero = (ins[0] == ZERO) |
||||||
|
for inp in ins[1:]: any_zero |= (inp == ZERO) |
||||||
|
|
||||||
|
out[...] = ONE |
||||||
|
np.putmask(out, any_zero, ZERO) |
||||||
|
for inp in ins: |
||||||
|
np.bitwise_and(out, inp | 0b100, out=out, where=~any_zero) |
||||||
|
if m > 4: np.bitwise_or(out, inp & 0b100, out=out, where=~any_zero) |
||||||
|
np.putmask(out, (any_unknown & ~any_zero), UNKNOWN) |
||||||
|
else: |
||||||
|
out[...] = ONE |
||||||
|
for inp in ins: np.bitwise_and(out, inp, out=out) |
||||||
|
|
||||||
|
|
||||||
|
def mv_and(x1, x2, out=None): |
||||||
|
m = mv_getm(x1, x2) |
||||||
|
x1, x2 = mv_cast(x1, x2, m=m) |
||||||
|
out = out or MVArray(np.broadcast(x1.data, x2.data).shape, m=m) |
||||||
|
_mv_and(m, out.data, x1.data, x2.data) |
||||||
|
return out |
||||||
|
|
||||||
|
|
||||||
|
def _mv_xor(m, out, *ins): |
||||||
|
if m > 2: |
||||||
|
any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED) |
||||||
|
for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED) |
||||||
|
|
||||||
|
out[...] = ZERO |
||||||
|
for inp in ins: |
||||||
|
np.bitwise_xor(out, inp & 0b011, out=out) |
||||||
|
if m > 4: np.bitwise_or(out, inp & 0b100, out=out) |
||||||
|
np.putmask(out, any_unknown, UNKNOWN) |
||||||
|
else: |
||||||
|
out[...] = ZERO |
||||||
|
for inp in ins: np.bitwise_xor(out, inp, out=out) |
||||||
|
|
||||||
|
|
||||||
|
def mv_xor(x1, x2, out=None): |
||||||
|
m = mv_getm(x1, x2) |
||||||
|
x1, x2 = mv_cast(x1, x2, m=m) |
||||||
|
out = out or MVArray(np.broadcast(x1.data, x2.data).shape, m=m) |
||||||
|
_mv_xor(m, out.data, x1.data, x2.data) |
||||||
|
return out |
||||||
|
|
||||||
|
|
||||||
|
def mv_transition(init, final, out=None): |
||||||
|
m = mv_getm(init, final) |
||||||
|
init, final = mv_cast(init, final, m=m) |
||||||
|
init = init.data |
||||||
|
final = final.data |
||||||
|
out = out or MVArray(np.broadcast(init, final).shape, m=8) |
||||||
|
out.data[...] = (init & 0b010) | (final & 0b001) |
||||||
|
out.data[...] |= ((out.data << 1) ^ (out.data << 2)) & 0b100 |
||||||
|
unknown = (init == UNKNOWN) | (init == UNASSIGNED) | (final == UNKNOWN) | (final == UNASSIGNED) |
||||||
|
unassigned = (init == UNASSIGNED) & (final == UNASSIGNED) |
||||||
|
np.putmask(out.data, unknown, UNKNOWN) |
||||||
|
np.putmask(out.data, unassigned, UNASSIGNED) |
||||||
|
return out |
||||||
|
|
||||||
|
|
||||||
|
class MVArray: |
||||||
|
"""An n-dimensional array of m-valued logic values. |
||||||
|
|
||||||
|
This class wraps a numpy.ndarray of type uint8 and adds support for encoding and |
||||||
|
interpreting 2-valued, 4-valued, and 8-valued logic values. |
||||||
|
Each logic value is stored as an uint8, value manipulations are cheaper than in BPArray. |
||||||
|
|
||||||
|
An MVArray always has 2 axes: |
||||||
|
|
||||||
|
* Axis 0 is PI/PO/FF position, the length of this axis is called "width". |
||||||
|
* Axis 1 is vector/pattern, the length of this axis is called "length". |
||||||
|
|
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, a, m=None): |
||||||
|
self.m = m or 8 |
||||||
|
assert self.m in [2, 4, 8] |
||||||
|
|
||||||
|
# Try our best to interpret given a. |
||||||
|
if isinstance(a, MVArray): |
||||||
|
self.data = a.data.copy() |
||||||
|
self.m = m or a.m |
||||||
|
elif hasattr(a, 'data'): # assume it is a BPArray. Can't use isinstance() because BPArray isn't declared yet. |
||||||
|
self.data = np.zeros((a.width, a.length), dtype=np.uint8) |
||||||
|
self.m = m or a.m |
||||||
|
for i in range(a.data.shape[-2]): |
||||||
|
self.data[...] <<= 1 |
||||||
|
self.data[...] |= np.unpackbits(a.data[..., -i-1, :], axis=1)[:, :a.length] |
||||||
|
if a.data.shape[-2] == 1: |
||||||
|
self.data *= 3 |
||||||
|
elif isinstance(a, int): |
||||||
|
self.data = np.full((a, 1), UNASSIGNED, dtype=np.uint8) |
||||||
|
elif isinstance(a, tuple): |
||||||
|
self.data = np.full(a, UNASSIGNED, dtype=np.uint8) |
||||||
|
else: |
||||||
|
if isinstance(a, str): a = [a] |
||||||
|
self.data = np.asarray(interpret(a), dtype=np.uint8) |
||||||
|
self.data = self.data[:, np.newaxis] if self.data.ndim == 1 else np.moveaxis(self.data, -2, -1) |
||||||
|
|
||||||
|
# Cast data to m-valued logic. |
||||||
|
if self.m == 2: |
||||||
|
self.data[...] = ((self.data & 0b001) & ((self.data >> 1) & 0b001) | (self.data == RISE)) * ONE |
||||||
|
elif self.m == 4: |
||||||
|
self.data[...] = (self.data & 0b011) & ((self.data != FALL) * ONE) | ((self.data == RISE) * ONE) |
||||||
|
elif self.m == 8: |
||||||
|
self.data[...] = self.data & 0b111 |
||||||
|
|
||||||
|
self.length = self.data.shape[-1] |
||||||
|
self.width = self.data.shape[-2] |
||||||
|
|
||||||
|
def __repr__(self): |
||||||
|
return f'<MVArray length={self.length} width={self.width} m={self.m} nbytes={self.data.nbytes}>' |
||||||
|
|
||||||
|
def __str__(self): |
||||||
|
return str([self[idx] for idx in range(self.length)]) |
||||||
|
|
||||||
|
def __getitem__(self, vector_idx): |
||||||
|
chars = ["0", "X", "-", "1", "P", "R", "F", "N"] |
||||||
|
return ''.join(chars[v] for v in self.data[:, vector_idx]) |
||||||
|
|
||||||
|
def __len__(self): |
||||||
|
return self.length |
||||||
|
|
||||||
|
|
||||||
|
def bp_buf(out, inp): |
||||||
|
md = out.shape[-2] |
||||||
|
assert md == inp.shape[-2] |
||||||
|
if md > 1: |
||||||
|
unknown = inp[..., 0, :] ^ inp[..., 1, :] |
||||||
|
if md > 2: unknown &= ~inp[..., 2, :] |
||||||
|
out[..., 0, :] = inp[..., 0, :] | unknown |
||||||
|
out[..., 1, :] = inp[..., 1, :] & ~unknown |
||||||
|
if md > 2: out[..., 2, :] = inp[..., 2, :] & ~unknown |
||||||
|
else: |
||||||
|
out[..., 0, :] = inp[..., 0, :] |
||||||
|
|
||||||
|
|
||||||
|
def bp_not(out, inp): |
||||||
|
md = out.shape[-2] |
||||||
|
assert md == inp.shape[-2] |
||||||
|
if md > 1: |
||||||
|
unknown = inp[..., 0, :] ^ inp[..., 1, :] |
||||||
|
if md > 2: unknown &= ~inp[..., 2, :] |
||||||
|
out[..., 0, :] = ~inp[..., 0, :] | unknown |
||||||
|
out[..., 1, :] = ~inp[..., 1, :] & ~unknown |
||||||
|
if md > 2: out[..., 2, :] = inp[..., 2, :] & ~unknown |
||||||
|
else: |
||||||
|
out[..., 0, :] = ~inp[..., 0, :] |
||||||
|
|
||||||
|
|
||||||
|
def bp_or(out, *ins): |
||||||
|
md = out.shape[-2] |
||||||
|
for inp in ins: assert md == inp.shape[-2] |
||||||
|
out[...] = 0 |
||||||
|
if md == 1: |
||||||
|
for inp in ins: out[..., 0, :] |= inp[..., 0, :] |
||||||
|
elif md == 2: |
||||||
|
any_unknown = ins[0][..., 0, :] ^ ins[0][..., 1, :] |
||||||
|
for inp in ins[1:]: any_unknown |= inp[..., 0, :] ^ inp[..., 1, :] |
||||||
|
any_one = ins[0][..., 0, :] & ins[0][..., 1, :] |
||||||
|
for inp in ins[1:]: any_one |= inp[..., 0, :] & inp[..., 1, :] |
||||||
|
for inp in ins: |
||||||
|
out[..., 0, :] |= inp[..., 0, :] | any_unknown |
||||||
|
out[..., 1, :] |= inp[..., 1, :] & (~any_unknown | any_one) |
||||||
|
else: |
||||||
|
any_unknown = (ins[0][..., 0, :] ^ ins[0][..., 1, :]) & ~ins[0][..., 2, :] |
||||||
|
for inp in ins[1:]: any_unknown |= (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :] |
||||||
|
any_one = ins[0][..., 0, :] & ins[0][..., 1, :] & ~ins[0][..., 2, :] |
||||||
|
for inp in ins[1:]: any_one |= inp[..., 0, :] & inp[..., 1, :] & ~inp[..., 2, :] |
||||||
|
for inp in ins: |
||||||
|
out[..., 0, :] |= inp[..., 0, :] | any_unknown |
||||||
|
out[..., 1, :] |= inp[..., 1, :] & (~any_unknown | any_one) |
||||||
|
out[..., 2, :] |= inp[..., 2, :] & (~any_unknown | any_one) & ~any_one |
||||||
|
|
||||||
|
|
||||||
|
def bp_and(out, *ins): |
||||||
|
md = out.shape[-2] |
||||||
|
for inp in ins: assert md == inp.shape[-2] |
||||||
|
out[...] = 0xff |
||||||
|
if md == 1: |
||||||
|
for inp in ins: out[..., 0, :] &= inp[..., 0, :] |
||||||
|
elif md == 2: |
||||||
|
any_unknown = ins[0][..., 0, :] ^ ins[0][..., 1, :] |
||||||
|
for inp in ins[1:]: any_unknown |= inp[..., 0, :] ^ inp[..., 1, :] |
||||||
|
any_zero = ~ins[0][..., 0, :] & ~ins[0][..., 1, :] |
||||||
|
for inp in ins[1:]: any_zero |= ~inp[..., 0, :] & ~inp[..., 1, :] |
||||||
|
for inp in ins: |
||||||
|
out[..., 0, :] &= inp[..., 0, :] | (any_unknown & ~any_zero) |
||||||
|
out[..., 1, :] &= inp[..., 1, :] & ~any_unknown |
||||||
|
else: |
||||||
|
any_unknown = (ins[0][..., 0, :] ^ ins[0][..., 1, :]) & ~ins[0][..., 2, :] |
||||||
|
for inp in ins[1:]: any_unknown |= (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :] |
||||||
|
any_zero = ~ins[0][..., 0, :] & ~ins[0][..., 1, :] & ~ins[0][..., 2, :] |
||||||
|
for inp in ins[1:]: any_zero |= ~inp[..., 0, :] & ~inp[..., 1, :] & ~inp[..., 2, :] |
||||||
|
out[..., 2, :] = 0 |
||||||
|
for inp in ins: |
||||||
|
out[..., 0, :] &= inp[..., 0, :] | (any_unknown & ~any_zero) |
||||||
|
out[..., 1, :] &= inp[..., 1, :] & ~any_unknown |
||||||
|
out[..., 2, :] |= inp[..., 2, :] & (~any_unknown | any_zero) & ~any_zero |
||||||
|
|
||||||
|
|
||||||
|
def bp_xor(out, *ins): |
||||||
|
md = out.shape[-2] |
||||||
|
for inp in ins: assert md == inp.shape[-2] |
||||||
|
out[...] = 0 |
||||||
|
if md == 1: |
||||||
|
for inp in ins: out[..., 0, :] ^= inp[..., 0, :] |
||||||
|
elif md == 2: |
||||||
|
any_unknown = ins[0][..., 0, :] ^ ins[0][..., 1, :] |
||||||
|
for inp in ins[1:]: any_unknown |= inp[..., 0, :] ^ inp[..., 1, :] |
||||||
|
for inp in ins: out[...] ^= inp |
||||||
|
out[..., 0, :] |= any_unknown |
||||||
|
out[..., 1, :] &= ~any_unknown |
||||||
|
else: |
||||||
|
any_unknown = (ins[0][..., 0, :] ^ ins[0][..., 1, :]) & ~ins[0][..., 2, :] |
||||||
|
for inp in ins[1:]: any_unknown |= (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :] |
||||||
|
for inp in ins: |
||||||
|
out[..., 0, :] ^= inp[..., 0, :] |
||||||
|
out[..., 1, :] ^= inp[..., 1, :] |
||||||
|
out[..., 2, :] |= inp[..., 2, :] |
||||||
|
out[..., 0, :] |= any_unknown |
||||||
|
out[..., 1, :] &= ~any_unknown |
||||||
|
out[..., 2, :] &= ~any_unknown |
||||||
|
|
||||||
|
|
||||||
|
class BPArray: |
||||||
|
"""An n-dimensional array of m-valued logic values that uses bit-parallel storage. |
||||||
|
|
||||||
|
The primary use of this format is in aiding efficient bit-parallel logic simulation. |
||||||
|
The secondary benefit over MVArray is its memory efficiency. |
||||||
|
Accessing individual values is more expensive than with :py:class:`MVArray`. |
||||||
|
It is advised to first construct a MVArray, pack it into a :py:class:`BPArray` for simulation and unpack the results |
||||||
|
back into a :py:class:`MVArray` for value access. |
||||||
|
|
||||||
|
The values along the last axis (vectors/patterns) are packed into uint8 words. |
||||||
|
The second-last axis has length ceil(log2(m)) for storing all bits. |
||||||
|
All other axes stay the same as in MVArray. |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self, a, m=None): |
||||||
|
if not isinstance(a, MVArray) and not isinstance(a, BPArray): |
||||||
|
a = MVArray(a, m) |
||||||
|
self.m = a.m |
||||||
|
if isinstance(a, MVArray): |
||||||
|
if m is not None and m != a.m: |
||||||
|
a = MVArray(a, m) # cast data |
||||||
|
self.m = a.m |
||||||
|
assert self.m in [2, 4, 8] |
||||||
|
nwords = math.ceil(math.log2(self.m)) |
||||||
|
nbytes = (a.data.shape[-1] - 1) // 8 + 1 |
||||||
|
self.data = np.zeros(a.data.shape[:-1] + (nwords, nbytes), dtype=np.uint8) |
||||||
|
for i in range(self.data.shape[-2]): |
||||||
|
self.data[..., i, :] = np.packbits((a.data >> i) & 1, axis=-1) |
||||||
|
else: # we have a BPArray |
||||||
|
self.data = a.data.copy() # TODO: support conversion to different m |
||||||
|
self.m = a.m |
||||||
|
self.length = a.length |
||||||
|
self.width = a.width |
||||||
|
|
||||||
|
def __repr__(self): |
||||||
|
return f'<BPArray length={self.length} width={self.width} m={self.m} bytes={self.data.nbytes}>' |
||||||
|
|
||||||
|
def __len__(self): |
||||||
|
return self.length |
@ -1,299 +0,0 @@ |
|||||||
import numpy as np |
|
||||||
from .bittools import popcount, bit_in |
|
||||||
|
|
||||||
|
|
||||||
class PackedVectors: |
|
||||||
def __init__(self, nvectors=8, width=1, vdim=1, from_cache=None): |
|
||||||
if from_cache is not None: |
|
||||||
self.bits = np.array(from_cache) |
|
||||||
self.width, self.vdim, nbytes = self.bits.shape |
|
||||||
else: |
|
||||||
self.bits = np.zeros((width, vdim, (nvectors - 1) // 8 + 1), dtype='uint8') |
|
||||||
self.vdim = vdim |
|
||||||
self.width = width |
|
||||||
self.nvectors = nvectors |
|
||||||
m1 = np.array([2 ** x for x in range(7, -1, -1)], dtype='uint8') |
|
||||||
m0 = ~m1 |
|
||||||
self.mask = np.rollaxis(np.vstack((m0, m1)), 1) |
|
||||||
|
|
||||||
@classmethod |
|
||||||
def from_pair(cls, init, final): |
|
||||||
assert init.nvectors == final.nvectors |
|
||||||
assert len(init.bits) == len(final.bits) |
|
||||||
init_v = init.bits[:, 0] |
|
||||||
if init.vdim == 3: |
|
||||||
init_c = (init.bits[:, 0] ^ init.bits[:, 1]) | init.bits[:, 2] |
|
||||||
elif init.vdim == 2: |
|
||||||
init_c = init.bits[:, 1] |
|
||||||
else: |
|
||||||
init_c = ~np.zeros_like(init.bits[:, 0]) |
|
||||||
final_v = final.bits[:, 0] |
|
||||||
if final.vdim == 3: |
|
||||||
final_c = (final.bits[:, 0] ^ final.bits[:, 1]) | final.bits[:, 2] |
|
||||||
final_v = ~final.bits[:, 1] |
|
||||||
elif final.vdim == 2: |
|
||||||
final_c = final.bits[:, 1] |
|
||||||
else: |
|
||||||
final_c = ~np.zeros_like(final.bits[:, 0]) |
|
||||||
c = init_c & final_c |
|
||||||
a0 = init_v & c |
|
||||||
a1 = ~final_v & c |
|
||||||
a2 = (init_v ^ final_v) & c |
|
||||||
p = PackedVectors(init.nvectors, len(init.bits), 3) |
|
||||||
p.bits[:, 0] = a0 |
|
||||||
p.bits[:, 1] = a1 |
|
||||||
p.bits[:, 2] = a2 |
|
||||||
return p |
|
||||||
|
|
||||||
def transition_vectors(self): |
|
||||||
a = PackedVectors(self.nvectors-1, self.width, 3) |
|
||||||
for pos in range(self.width): |
|
||||||
for vidx in range(self.nvectors-1): |
|
||||||
tr = self.get_value(vidx, pos) + self.get_value(vidx+1, pos) |
|
||||||
if tr == '00': |
|
||||||
a.set_value(vidx, pos, '0') |
|
||||||
elif tr == '11': |
|
||||||
a.set_value(vidx, pos, '1') |
|
||||||
elif tr == '01': |
|
||||||
a.set_value(vidx, pos, 'R') |
|
||||||
elif tr == '10': |
|
||||||
a.set_value(vidx, pos, 'F') |
|
||||||
elif tr == '--': |
|
||||||
a.set_value(vidx, pos, '-') |
|
||||||
else: |
|
||||||
a.set_value(vidx, pos, 'X') |
|
||||||
return a |
|
||||||
|
|
||||||
def __add__(self, other): |
|
||||||
a = PackedVectors(self.nvectors + other.nvectors, self.width, max(self.vdim, other.vdim)) |
|
||||||
# a.bits[:self.bits.shape[0], 0] = self.bits[:, 0] |
|
||||||
# if self.vdim == 2: |
|
||||||
# a.bits[:self.bits.shape[0], 1] = self.care_bits |
|
||||||
# elif self.vdim == 3: |
|
||||||
# a.bits[:self.bits.shape[0], 1] = ~self.value_bits |
|
||||||
# a.bits[:self.bits.shape[0], 2] = self.toggle_bits |
|
||||||
for i in range(self.nvectors): |
|
||||||
a[i] = self[i] |
|
||||||
for i in range(len(other)): |
|
||||||
a[self.nvectors+i] = other[i] |
|
||||||
return a |
|
||||||
|
|
||||||
def __len__(self): |
|
||||||
return self.nvectors |
|
||||||
|
|
||||||
def randomize(self, one_probability=0.5): |
|
||||||
for data in self.bits: |
|
||||||
data[0] = np.packbits((np.random.rand(self.nvectors) < one_probability).astype(int)) |
|
||||||
if self.vdim == 2: |
|
||||||
data[1] = 255 |
|
||||||
elif self.vdim == 3: |
|
||||||
data[1] = ~np.packbits((np.random.rand(self.nvectors) < one_probability).astype(int)) |
|
||||||
data[2] = data[0] ^ ~data[1] |
|
||||||
|
|
||||||
def copy(self, selection_mask=None): |
|
||||||
if selection_mask is not None: |
|
||||||
cpy = PackedVectors(popcount(selection_mask), len(self.bits), self.vdim) |
|
||||||
cur = 0 |
|
||||||
for vidx in range(self.nvectors): |
|
||||||
if bit_in(selection_mask, vidx): |
|
||||||
cpy[cur] = self[vidx] |
|
||||||
cur += 1 |
|
||||||
else: |
|
||||||
cpy = PackedVectors(self.nvectors, len(self.bits), self.vdim) |
|
||||||
np.copyto(cpy.bits, self.bits) |
|
||||||
return cpy |
|
||||||
|
|
||||||
@property |
|
||||||
def care_bits(self): |
|
||||||
if self.vdim == 1: |
|
||||||
return self.bits[:, 0] | 255 |
|
||||||
elif self.vdim == 2: |
|
||||||
return self.bits[:, 1] |
|
||||||
elif self.vdim == 3: |
|
||||||
return (self.bits[:, 0] ^ self.bits[:, 1]) | self.bits[:, 2] |
|
||||||
|
|
||||||
@property |
|
||||||
def initial_bits(self): |
|
||||||
return self.bits[:, 0] |
|
||||||
|
|
||||||
@property |
|
||||||
def value_bits(self): |
|
||||||
if self.vdim == 3: |
|
||||||
return ~self.bits[:, 1] |
|
||||||
else: |
|
||||||
return self.bits[:, 0] |
|
||||||
|
|
||||||
@property |
|
||||||
def toggle_bits(self): |
|
||||||
if self.vdim == 3: |
|
||||||
return self.bits[:, 2] |
|
||||||
else: |
|
||||||
return self.bits[:, 0] & 0 |
|
||||||
|
|
||||||
def get_value(self, vector, position): |
|
||||||
if vector >= self.nvectors: |
|
||||||
raise IndexError(f'vector out of range: {vector} >= {self.nvectors}') |
|
||||||
a = self.bits[position, :, vector // 8] |
|
||||||
m = self.mask[vector % 8] |
|
||||||
if self.vdim == 1: |
|
||||||
return '1' if a[0] & m[1] else '0' |
|
||||||
elif self.vdim == 2: |
|
||||||
if a[0] & m[1]: |
|
||||||
return '1' if a[1] & m[1] else 'X' |
|
||||||
else: |
|
||||||
return '0' if a[1] & m[1] else '-' |
|
||||||
elif self.vdim == 3: |
|
||||||
if a[2] & m[1]: |
|
||||||
if a[0] & m[1]: |
|
||||||
return 'F' if a[1] & m[1] else 'N' |
|
||||||
else: |
|
||||||
return 'P' if a[1] & m[1] else 'R' |
|
||||||
else: |
|
||||||
if a[0] & m[1]: |
|
||||||
return 'X' if a[1] & m[1] else '1' |
|
||||||
else: |
|
||||||
return '0' if a[1] & m[1] else '-' |
|
||||||
|
|
||||||
def get_values_for_position(self, position): |
|
||||||
return ''.join(self.get_value(x, position) for x in range(self.nvectors)) |
|
||||||
|
|
||||||
def set_value(self, vector, position, v): |
|
||||||
if vector >= self.nvectors: |
|
||||||
raise IndexError(f'vector out of range: {vector} >= {self.nvectors}') |
|
||||||
a = self.bits[position, :, vector // 8] |
|
||||||
m = self.mask[vector % 8] |
|
||||||
if self.vdim == 1: |
|
||||||
self._set_value_vd1(a, m, v) |
|
||||||
elif self.vdim == 2: |
|
||||||
self._set_value_vd2(a, m, v) |
|
||||||
elif self.vdim == 3: |
|
||||||
self._set_value_vd3(a, m, v) |
|
||||||
|
|
||||||
def set_values(self, vector, v, mapping=None, inversions=None): |
|
||||||
if vector >= self.nvectors: |
|
||||||
raise IndexError(f'vector out of range: {vector} >= {self.nvectors}') |
|
||||||
if not mapping: |
|
||||||
mapping = [y for y in range(len(v))] |
|
||||||
if inversions is None: |
|
||||||
inversions = [False] * len(v) |
|
||||||
for i, c in enumerate(v): |
|
||||||
if inversions[i]: |
|
||||||
if c == '1': |
|
||||||
c = '0' |
|
||||||
elif c == '0': |
|
||||||
c = '1' |
|
||||||
elif c == 'H': |
|
||||||
c = 'L' |
|
||||||
elif c == 'L': |
|
||||||
c = 'H' |
|
||||||
elif c == 'R': |
|
||||||
c = 'F' |
|
||||||
elif c == 'F': |
|
||||||
c = 'R' |
|
||||||
self.set_value(vector, mapping[i], c) |
|
||||||
|
|
||||||
def set_values_for_position(self, position, values): |
|
||||||
for i, v in enumerate(values): |
|
||||||
self.set_value(i, position, v) |
|
||||||
|
|
||||||
def __setitem__(self, vector, value): |
|
||||||
for i, c in enumerate(value): |
|
||||||
self.set_value(vector, i, c) |
|
||||||
|
|
||||||
def __getitem__(self, vector): |
|
||||||
if isinstance(vector, slice): |
|
||||||
first = self.get_values_for_position(0)[vector] |
|
||||||
ret = PackedVectors(len(first), self.width, self.vdim) |
|
||||||
ret.set_values_for_position(0, first) |
|
||||||
for pos in range(1, self.width): |
|
||||||
ret.set_values_for_position(pos, self.get_values_for_position(pos)[vector]) |
|
||||||
return ret |
|
||||||
return ''.join(self.get_value(vector, pos) for pos in range(len(self.bits))) |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
def _set_value_vd1(a, m, v): |
|
||||||
if v in [True, 1, '1', 'H', 'h']: |
|
||||||
a[0] |= m[1] |
|
||||||
else: |
|
||||||
a[0] &= m[0] |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
def _set_value_vd2(a, m, v): |
|
||||||
if v in [True, 1, '1', 'H', 'h']: |
|
||||||
a[0] |= m[1] |
|
||||||
a[1] |= m[1] |
|
||||||
elif v in [False, 0, '0', 'L', 'l']: |
|
||||||
a[0] &= m[0] |
|
||||||
a[1] |= m[1] |
|
||||||
elif v in ['X', 'x']: |
|
||||||
a[0] |= m[1] |
|
||||||
a[1] &= m[0] |
|
||||||
else: |
|
||||||
a[0] &= m[0] |
|
||||||
a[1] &= m[0] |
|
||||||
|
|
||||||
# i fb act |
|
||||||
# a 0 1 2 |
|
||||||
# - 0 0 0 None, '-' |
|
||||||
# 0 0 1 0 False, 0, '0', 'l', 'L' |
|
||||||
# 1 1 0 0 True, 1, '1', 'h', 'H' |
|
||||||
# X 1 1 0 'x', 'X' |
|
||||||
# / 0 0 1 '/', 'r', 'R' |
|
||||||
# ^ 0 1 1 '^', 'p', 'P' |
|
||||||
# v 1 0 1 'v', 'n', 'N' |
|
||||||
# \ 1 1 1 '\', 'f', 'F' |
|
||||||
@staticmethod |
|
||||||
def _set_value_vd3(a, m, v): |
|
||||||
if v in [False, 0, '0', 'L', 'l']: |
|
||||||
a[0] &= m[0] |
|
||||||
a[1] |= m[1] |
|
||||||
a[2] &= m[0] |
|
||||||
elif v in [True, 1, '1', 'H', 'h']: |
|
||||||
a[0] |= m[1] |
|
||||||
a[1] &= m[0] |
|
||||||
a[2] &= m[0] |
|
||||||
elif v in ['X', 'x']: |
|
||||||
a[0] |= m[1] |
|
||||||
a[1] |= m[1] |
|
||||||
a[2] &= m[0] |
|
||||||
elif v in ['/', 'r', 'R']: |
|
||||||
a[0] &= m[0] |
|
||||||
a[1] &= m[0] |
|
||||||
a[2] |= m[1] |
|
||||||
elif v in ['^', 'p', 'P']: |
|
||||||
a[0] &= m[0] |
|
||||||
a[1] |= m[1] |
|
||||||
a[2] |= m[1] |
|
||||||
elif v in ['v', 'n', 'N']: |
|
||||||
a[0] |= m[1] |
|
||||||
a[1] &= m[0] |
|
||||||
a[2] |= m[1] |
|
||||||
elif v in ['\\', 'f', 'F']: |
|
||||||
a[0] |= m[1] |
|
||||||
a[1] |= m[1] |
|
||||||
a[2] |= m[1] |
|
||||||
else: |
|
||||||
a[0] &= m[0] |
|
||||||
a[1] &= m[0] |
|
||||||
a[2] &= m[0] |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
return f'<PackedVectors nvectors={self.nvectors}, width={self.width}, vdim={self.vdim}>' |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
lst = [] |
|
||||||
for p in range(self.nvectors): |
|
||||||
lst.append(''.join(self.get_value(p, w) for w in range(len(self.bits)))) |
|
||||||
if len(lst) == 0: return '' |
|
||||||
if len(lst[0]) > 64: |
|
||||||
lst = [s[:32] + '...' + s[-32:] for s in lst] |
|
||||||
if len(lst) <= 16: |
|
||||||
return '\n'.join(lst) |
|
||||||
else: |
|
||||||
return '\n'.join(lst[:8]) + '\n...\n' + '\n'.join(lst[-8:]) |
|
||||||
|
|
||||||
def diff(self, other, out=None): |
|
||||||
if out is None: |
|
||||||
out = np.zeros((self.width, self.bits.shape[-1]), dtype='uint8') |
|
||||||
out[...] = (self.value_bits ^ other.value_bits) & self.care_bits & other.care_bits |
|
||||||
return out |
|
@ -1,317 +0,0 @@ |
|||||||
import numpy as np |
|
||||||
import math |
|
||||||
from .wave_sim import WaveSim |
|
||||||
from . import cuda |
|
||||||
|
|
||||||
TMAX = np.float32(2 ** 127) # almost np.PINF for 32-bit floating point values |
|
||||||
TMAX_OVL = np.float32(1.1 * 2 ** 127) # almost np.PINF with overflow mark |
|
||||||
TMIN = np.float32(-2 ** 127) # almost np.NINF for 32-bit floating point values |
|
||||||
|
|
||||||
|
|
||||||
class WaveSimCuda(WaveSim): |
|
||||||
def __init__(self, circuit, timing, sims=8, wavecaps=16, strip_forks=False, keep_waveforms=True): |
|
||||||
super().__init__(circuit, timing, sims, wavecaps, strip_forks, keep_waveforms) |
|
||||||
|
|
||||||
self.tdata = np.zeros((len(self.interface), 3, (sims - 1) // 8 + 1), dtype='uint8') |
|
||||||
|
|
||||||
self.d_state = cuda.to_device(self.state) |
|
||||||
self.d_sat = cuda.to_device(self.sat) |
|
||||||
self.d_ops = cuda.to_device(self.ops) |
|
||||||
self.d_timing = cuda.to_device(self.timing) |
|
||||||
self.d_tdata = cuda.to_device(self.tdata) |
|
||||||
self.d_cdata = cuda.to_device(self.cdata) |
|
||||||
|
|
||||||
self._block_dim = (32, 16) |
|
||||||
|
|
||||||
def get_line_delay(self, line, polarity): |
|
||||||
return self.d_timing[line, 0, polarity] |
|
||||||
|
|
||||||
def set_line_delay(self, line, polarity, delay): |
|
||||||
self.d_timing[line, 0, polarity] = delay |
|
||||||
|
|
||||||
def assign(self, vectors, time=0.0, offset=0): |
|
||||||
assert (offset % 8) == 0 |
|
||||||
byte_offset = offset // 8 |
|
||||||
assert byte_offset < vectors.bits.shape[-1] |
|
||||||
pdim = min(vectors.bits.shape[-1] - byte_offset, self.tdata.shape[-1]) |
|
||||||
|
|
||||||
self.tdata[..., 0:pdim] = vectors.bits[..., byte_offset:pdim + byte_offset] |
|
||||||
if vectors.vdim == 1: |
|
||||||
self.tdata[:, 1, 0:pdim] = ~self.tdata[:, 1, 0:pdim] |
|
||||||
self.tdata[:, 2, 0:pdim] = 0 |
|
||||||
cuda.to_device(self.tdata, to=self.d_tdata) |
|
||||||
|
|
||||||
grid_dim = self._grid_dim(self.sims, len(self.interface)) |
|
||||||
assign_kernel[grid_dim, self._block_dim](self.d_state, self.d_sat, self.ppi_offset, |
|
||||||
len(self.interface), self.d_tdata, time) |
|
||||||
|
|
||||||
def _grid_dim(self, x, y): |
|
||||||
gx = math.ceil(x / self._block_dim[0]) |
|
||||||
gy = math.ceil(y / self._block_dim[1]) |
|
||||||
return gx, gy |
|
||||||
|
|
||||||
def propagate(self, sims=None, sd=0.0, seed=1): |
|
||||||
if sims is None: |
|
||||||
sims = self.sims |
|
||||||
else: |
|
||||||
sims = min(sims, self.sims) |
|
||||||
for op_start, op_stop in zip(self.level_starts, self.level_stops): |
|
||||||
grid_dim = self._grid_dim(sims, op_stop - op_start) |
|
||||||
wave_kernel[grid_dim, self._block_dim](self.d_ops, op_start, op_stop, self.d_state, self.sat, int(0), |
|
||||||
sims, self.d_timing, sd, seed) |
|
||||||
cuda.synchronize() |
|
||||||
self.lst_eat_valid = False |
|
||||||
|
|
||||||
def wave(self, line, vector): |
|
||||||
if line < 0: |
|
||||||
return None |
|
||||||
mem, wcap, _ = self.sat[line] |
|
||||||
if mem < 0: |
|
||||||
return None |
|
||||||
return self.d_state[mem:mem + wcap, vector] |
|
||||||
|
|
||||||
def capture(self, time=TMAX, sd=0, seed=1, cdata=None, offset=0): |
|
||||||
grid_dim = self._grid_dim(self.sims, len(self.interface)) |
|
||||||
capture_kernel[grid_dim, self._block_dim](self.d_state, self.d_sat, self.ppo_offset, |
|
||||||
self.d_cdata, time, sd * math.sqrt(2), seed) |
|
||||||
self.cdata[...] = self.d_cdata |
|
||||||
if cdata is not None: |
|
||||||
assert offset < cdata.shape[1] |
|
||||||
cap_dim = min(cdata.shape[1] - offset, self.sims) |
|
||||||
cdata[:, offset:cap_dim + offset] = self.cdata[:, 0:cap_dim] |
|
||||||
self.lst_eat_valid = True |
|
||||||
return self.cdata |
|
||||||
|
|
||||||
def reassign(self, time=0.0): |
|
||||||
grid_dim = self._grid_dim(self.sims, len(self.interface)) |
|
||||||
reassign_kernel[grid_dim, self._block_dim](self.d_state, self.d_sat, self.ppi_offset, self.ppo_offset, |
|
||||||
self.d_cdata, time) |
|
||||||
cuda.synchronize() |
|
||||||
|
|
||||||
def wavecaps(self): |
|
||||||
gx = math.ceil(len(self.circuit.lines) / 512) |
|
||||||
wavecaps_kernel[gx, 512](self.d_state, self.d_sat, self.sims) |
|
||||||
self.sat[...] = self.d_sat |
|
||||||
return self.sat[..., 2] |
|
||||||
|
|
||||||
|
|
||||||
@cuda.jit() |
|
||||||
def wavecaps_kernel(state, sat, sims): |
|
||||||
idx = cuda.grid(1) |
|
||||||
if idx >= len(sat): return |
|
||||||
|
|
||||||
lidx, lcap, _ = sat[idx] |
|
||||||
if lidx < 0: return |
|
||||||
|
|
||||||
wcap = 0 |
|
||||||
for sidx in range(sims): |
|
||||||
for tidx in range(lcap): |
|
||||||
t = state[lidx + tidx, sidx] |
|
||||||
if tidx > wcap: |
|
||||||
wcap = tidx |
|
||||||
if t >= TMAX: break |
|
||||||
|
|
||||||
sat[idx, 2] = wcap + 1 |
|
||||||
|
|
||||||
|
|
||||||
@cuda.jit() |
|
||||||
def reassign_kernel(state, sat, ppi_offset, ppo_offset, cdata, ppi_time): |
|
||||||
vector, y = cuda.grid(2) |
|
||||||
if vector >= state.shape[-1]: return |
|
||||||
if ppo_offset + y >= len(sat): return |
|
||||||
|
|
||||||
ppo, ppo_cap, _ = sat[ppo_offset + y] |
|
||||||
ppi, ppi_cap, _ = sat[ppi_offset + y] |
|
||||||
if ppo < 0: return |
|
||||||
if ppi < 0: return |
|
||||||
|
|
||||||
ppo_val = int(cdata[y, vector, 1]) |
|
||||||
ppi_val = int(0) |
|
||||||
for tidx in range(ppi_cap): |
|
||||||
t = state[ppi + tidx, vector] |
|
||||||
if t >= TMAX: break |
|
||||||
ppi_val ^= 1 |
|
||||||
|
|
||||||
# make new waveform at PPI |
|
||||||
toggle = 0 |
|
||||||
if ppi_val: |
|
||||||
state[ppi + toggle, vector] = TMIN |
|
||||||
toggle += 1 |
|
||||||
if ppi_val != ppo_val: |
|
||||||
state[ppi + toggle, vector] = ppi_time |
|
||||||
toggle += 1 |
|
||||||
state[ppi + toggle, vector] = TMAX |
|
||||||
|
|
||||||
|
|
||||||
@cuda.jit() |
|
||||||
def capture_kernel(state, sat, ppo_offset, cdata, time, s_sqrt2, seed): |
|
||||||
x, y = cuda.grid(2) |
|
||||||
if ppo_offset + y >= len(sat): return |
|
||||||
line, tdim, _ = sat[ppo_offset + y] |
|
||||||
if line < 0: return |
|
||||||
if x >= state.shape[-1]: return |
|
||||||
vector = x |
|
||||||
m = 0.5 |
|
||||||
acc = 0.0 |
|
||||||
eat = TMAX |
|
||||||
lst = TMIN |
|
||||||
tog = 0 |
|
||||||
ovl = 0 |
|
||||||
val = int(0) |
|
||||||
final = int(0) |
|
||||||
for tidx in range(tdim): |
|
||||||
t = state[line + tidx, vector] |
|
||||||
if t >= TMAX: |
|
||||||
if t == TMAX_OVL: |
|
||||||
ovl = 1 |
|
||||||
break |
|
||||||
m = -m |
|
||||||
final ^= 1 |
|
||||||
if t < time: |
|
||||||
val ^= 1 |
|
||||||
if t <= TMIN: continue |
|
||||||
if s_sqrt2 > 0: |
|
||||||
acc += m * (1 + math.erf((t - time) / s_sqrt2)) |
|
||||||
eat = min(eat, t) |
|
||||||
lst = max(lst, t) |
|
||||||
tog += 1 |
|
||||||
if s_sqrt2 > 0: |
|
||||||
if m < 0: |
|
||||||
acc += 1 |
|
||||||
if acc >= 0.99: |
|
||||||
val = 1 |
|
||||||
elif acc > 0.01: |
|
||||||
seed = (seed << 4) + (vector << 20) + (y << 1) |
|
||||||
seed = int(0xDEECE66D) * seed + 0xB |
|
||||||
seed = int(0xDEECE66D) * seed + 0xB |
|
||||||
rnd = float((seed >> 8) & 0xffffff) / float(1 << 24) |
|
||||||
val = rnd < acc |
|
||||||
else: |
|
||||||
val = 0 |
|
||||||
else: |
|
||||||
acc = val |
|
||||||
|
|
||||||
cdata[y, vector, 0] = acc |
|
||||||
cdata[y, vector, 1] = val |
|
||||||
cdata[y, vector, 2] = final |
|
||||||
cdata[y, vector, 3] = (val != final) |
|
||||||
cdata[y, vector, 4] = eat |
|
||||||
cdata[y, vector, 5] = lst |
|
||||||
cdata[y, vector, 6] = ovl |
|
||||||
|
|
||||||
|
|
||||||
@cuda.jit() |
|
||||||
def assign_kernel(state, sat, ppi_offset, intf_len, tdata, time): |
|
||||||
x, y = cuda.grid(2) |
|
||||||
if y >= intf_len: return |
|
||||||
line = sat[ppi_offset + y, 0] |
|
||||||
if line < 0: return |
|
||||||
sdim = state.shape[-1] |
|
||||||
if x >= sdim: return |
|
||||||
vector = x |
|
||||||
a0 = tdata[y, 0, vector // 8] |
|
||||||
a1 = tdata[y, 1, vector // 8] |
|
||||||
a2 = tdata[y, 2, vector // 8] |
|
||||||
m = np.uint8(1 << (7 - (vector % 8))) |
|
||||||
toggle = 0 |
|
||||||
if a0 & m: |
|
||||||
state[line + toggle, x] = TMIN |
|
||||||
toggle += 1 |
|
||||||
if (a2 & m) and ((a0 & m) == (a1 & m)): |
|
||||||
state[line + toggle, x] = time |
|
||||||
toggle += 1 |
|
||||||
state[line + toggle, x] = TMAX |
|
||||||
|
|
||||||
|
|
||||||
@cuda.jit(device=True) |
|
||||||
def rand_gauss(seed, sd): |
|
||||||
clamp = 0.5 |
|
||||||
if sd <= 0.0: |
|
||||||
return 1.0 |
|
||||||
while True: |
|
||||||
x = -6.0 |
|
||||||
for i in range(12): |
|
||||||
seed = int(0xDEECE66D) * seed + 0xB |
|
||||||
x += float((seed >> 8) & 0xffffff) / float(1 << 24) |
|
||||||
x *= sd |
|
||||||
if abs(x) <= clamp: |
|
||||||
break |
|
||||||
return x + 1.0 |
|
||||||
|
|
||||||
|
|
||||||
@cuda.jit() |
|
||||||
def wave_kernel(ops, op_start, op_stop, state, sat, st_start, st_stop, line_times, sd, seed): |
|
||||||
x, y = cuda.grid(2) |
|
||||||
st_idx = st_start + x |
|
||||||
op_idx = op_start + y |
|
||||||
if st_idx >= st_stop: return |
|
||||||
if op_idx >= op_stop: return |
|
||||||
lut = ops[op_idx, 0] |
|
||||||
z_idx = ops[op_idx, 1] |
|
||||||
a_idx = ops[op_idx, 2] |
|
||||||
b_idx = ops[op_idx, 3] |
|
||||||
overflows = int(0) |
|
||||||
|
|
||||||
_seed = (seed << 4) + (z_idx << 20) + (st_idx << 1) |
|
||||||
|
|
||||||
a_mem = sat[a_idx, 0] |
|
||||||
b_mem = sat[b_idx, 0] |
|
||||||
z_mem, z_cap, _ = sat[z_idx] |
|
||||||
|
|
||||||
a_cur = int(0) |
|
||||||
b_cur = int(0) |
|
||||||
z_cur = lut & 1 |
|
||||||
if z_cur == 1: |
|
||||||
state[z_mem, st_idx] = TMIN |
|
||||||
|
|
||||||
a = state[a_mem, st_idx] + line_times[a_idx, 0, z_cur] * rand_gauss(_seed ^ a_mem ^ z_cur, sd) |
|
||||||
b = state[b_mem, st_idx] + line_times[b_idx, 0, z_cur] * rand_gauss(_seed ^ b_mem ^ z_cur, sd) |
|
||||||
|
|
||||||
previous_t = TMIN |
|
||||||
|
|
||||||
current_t = min(a, b) |
|
||||||
inputs = int(0) |
|
||||||
|
|
||||||
while current_t < TMAX: |
|
||||||
z_val = z_cur & 1 |
|
||||||
if b < a: |
|
||||||
b_cur += 1 |
|
||||||
b = state[b_mem + b_cur, st_idx] |
|
||||||
b += line_times[b_idx, 0, z_val ^ 1] * rand_gauss(_seed ^ b_mem ^ z_val ^ 1, sd) |
|
||||||
thresh = line_times[b_idx, 1, z_val] * rand_gauss(_seed ^ b_mem ^ z_val, sd) |
|
||||||
inputs ^= 2 |
|
||||||
next_t = b |
|
||||||
else: |
|
||||||
a_cur += 1 |
|
||||||
a = state[a_mem + a_cur, st_idx] |
|
||||||
a += line_times[a_idx, 0, z_val ^ 1] * rand_gauss(_seed ^ a_mem ^ z_val ^ 1, sd) |
|
||||||
thresh = line_times[a_idx, 1, z_val] * rand_gauss(_seed ^ a_mem ^ z_val, sd) |
|
||||||
inputs ^= 1 |
|
||||||
next_t = a |
|
||||||
|
|
||||||
if (z_cur & 1) != ((lut >> inputs) & 1): |
|
||||||
# we generate a toggle in z_mem, if: |
|
||||||
# ( it is the first toggle in z_mem OR |
|
||||||
# following toggle is earlier OR |
|
||||||
# pulse is wide enough ) AND enough space in z_mem. |
|
||||||
if z_cur == 0 or next_t < current_t or (current_t - previous_t) > thresh: |
|
||||||
if z_cur < (z_cap - 1): |
|
||||||
state[z_mem + z_cur, st_idx] = current_t |
|
||||||
previous_t = current_t |
|
||||||
z_cur += 1 |
|
||||||
else: |
|
||||||
overflows += 1 |
|
||||||
previous_t = state[z_mem + z_cur - 1, st_idx] |
|
||||||
z_cur -= 1 |
|
||||||
else: |
|
||||||
z_cur -= 1 |
|
||||||
if z_cur > 0: |
|
||||||
previous_t = state[z_mem + z_cur - 1, st_idx] |
|
||||||
else: |
|
||||||
previous_t = TMIN |
|
||||||
current_t = min(a, b) |
|
||||||
|
|
||||||
if overflows > 0: |
|
||||||
state[z_mem + z_cur, st_idx] = TMAX_OVL |
|
||||||
else: |
|
||||||
state[z_mem + z_cur, st_idx] = a if a > b else b # propagate overflow flags by storing biggest TMAX from input |
|
@ -0,0 +1,214 @@ |
|||||||
|
import kyupy.logic as lg |
||||||
|
|
||||||
|
|
||||||
|
def test_mvarray(): |
||||||
|
|
||||||
|
# instantiation with shape |
||||||
|
|
||||||
|
ary = lg.MVArray(4) |
||||||
|
assert ary.length == 1 |
||||||
|
assert len(ary) == 1 |
||||||
|
assert ary.width == 4 |
||||||
|
|
||||||
|
ary = lg.MVArray((3, 2)) |
||||||
|
assert ary.length == 2 |
||||||
|
assert len(ary) == 2 |
||||||
|
assert ary.width == 3 |
||||||
|
|
||||||
|
# instantiation with single vector |
||||||
|
|
||||||
|
ary = lg.MVArray([1, 0, 1]) |
||||||
|
assert ary.length == 1 |
||||||
|
assert ary.width == 3 |
||||||
|
assert str(ary) == "['101']" |
||||||
|
assert ary[0] == '101' |
||||||
|
|
||||||
|
ary = lg.MVArray("10X-") |
||||||
|
assert ary.length == 1 |
||||||
|
assert ary.width == 4 |
||||||
|
assert str(ary) == "['10X-']" |
||||||
|
assert ary[0] == '10X-' |
||||||
|
|
||||||
|
ary = lg.MVArray("1") |
||||||
|
assert ary.length == 1 |
||||||
|
assert ary.width == 1 |
||||||
|
|
||||||
|
ary = lg.MVArray(["1"]) |
||||||
|
assert ary.length == 1 |
||||||
|
assert ary.width == 1 |
||||||
|
|
||||||
|
# instantiation with multiple vectors |
||||||
|
|
||||||
|
ary = lg.MVArray([[0, 0], [0, 1], [1, 0], [1, 1]]) |
||||||
|
assert ary.length == 4 |
||||||
|
assert ary.width == 2 |
||||||
|
|
||||||
|
ary = lg.MVArray(["000", "001", "110", "---"]) |
||||||
|
assert ary.length == 4 |
||||||
|
assert ary.width == 3 |
||||||
|
assert str(ary) == "['000', '001', '110', '---']" |
||||||
|
assert ary[2] == '110' |
||||||
|
|
||||||
|
# casting to 2-valued logic |
||||||
|
|
||||||
|
ary = lg.MVArray([0, 1, 2, None], m=2) |
||||||
|
assert ary.data[0] == lg.ZERO |
||||||
|
assert ary.data[1] == lg.ONE |
||||||
|
assert ary.data[2] == lg.ZERO |
||||||
|
assert ary.data[3] == lg.ZERO |
||||||
|
|
||||||
|
ary = lg.MVArray("0-X1PRFN", m=2) |
||||||
|
assert ary.data[0] == lg.ZERO |
||||||
|
assert ary.data[1] == lg.ZERO |
||||||
|
assert ary.data[2] == lg.ZERO |
||||||
|
assert ary.data[3] == lg.ONE |
||||||
|
assert ary.data[4] == lg.ZERO |
||||||
|
assert ary.data[5] == lg.ONE |
||||||
|
assert ary.data[6] == lg.ZERO |
||||||
|
assert ary.data[7] == lg.ONE |
||||||
|
|
||||||
|
# casting to 4-valued logic |
||||||
|
|
||||||
|
ary = lg.MVArray([0, 1, 2, None, 'F'], m=4) |
||||||
|
assert ary.data[0] == lg.ZERO |
||||||
|
assert ary.data[1] == lg.ONE |
||||||
|
assert ary.data[2] == lg.UNKNOWN |
||||||
|
assert ary.data[3] == lg.UNASSIGNED |
||||||
|
assert ary.data[4] == lg.ZERO |
||||||
|
|
||||||
|
ary = lg.MVArray("0-X1PRFN", m=4) |
||||||
|
assert ary.data[0] == lg.ZERO |
||||||
|
assert ary.data[1] == lg.UNASSIGNED |
||||||
|
assert ary.data[2] == lg.UNKNOWN |
||||||
|
assert ary.data[3] == lg.ONE |
||||||
|
assert ary.data[4] == lg.ZERO |
||||||
|
assert ary.data[5] == lg.ONE |
||||||
|
assert ary.data[6] == lg.ZERO |
||||||
|
assert ary.data[7] == lg.ONE |
||||||
|
|
||||||
|
# casting to 8-valued logic |
||||||
|
|
||||||
|
ary = lg.MVArray([0, 1, 2, None, 'F'], m=8) |
||||||
|
assert ary.data[0] == lg.ZERO |
||||||
|
assert ary.data[1] == lg.ONE |
||||||
|
assert ary.data[2] == lg.UNKNOWN |
||||||
|
assert ary.data[3] == lg.UNASSIGNED |
||||||
|
assert ary.data[4] == lg.FALL |
||||||
|
|
||||||
|
ary = lg.MVArray("0-X1PRFN", m=8) |
||||||
|
assert ary.data[0] == lg.ZERO |
||||||
|
assert ary.data[1] == lg.UNASSIGNED |
||||||
|
assert ary.data[2] == lg.UNKNOWN |
||||||
|
assert ary.data[3] == lg.ONE |
||||||
|
assert ary.data[4] == lg.PPULSE |
||||||
|
assert ary.data[5] == lg.RISE |
||||||
|
assert ary.data[6] == lg.FALL |
||||||
|
assert ary.data[7] == lg.NPULSE |
||||||
|
|
||||||
|
# copy constructor and casting |
||||||
|
|
||||||
|
ary8 = lg.MVArray(ary, m=8) |
||||||
|
assert ary8.length == 1 |
||||||
|
assert ary8.width == 8 |
||||||
|
assert ary8.data[7] == lg.NPULSE |
||||||
|
|
||||||
|
ary4 = lg.MVArray(ary, m=4) |
||||||
|
assert ary4.data[1] == lg.UNASSIGNED |
||||||
|
assert ary4.data[7] == lg.ONE |
||||||
|
|
||||||
|
ary2 = lg.MVArray(ary, m=2) |
||||||
|
assert ary2.data[1] == lg.ZERO |
||||||
|
assert ary2.data[7] == lg.ONE |
||||||
|
|
||||||
|
|
||||||
|
def test_mv_operations(): |
||||||
|
x1_2v = lg.MVArray("0011", m=2) |
||||||
|
x2_2v = lg.MVArray("0101", m=2) |
||||||
|
x1_4v = lg.MVArray("0000XXXX----1111", m=4) |
||||||
|
x2_4v = lg.MVArray("0X-10X-10X-10X-1", m=4) |
||||||
|
x1_8v = lg.MVArray("00000000XXXXXXXX--------11111111PPPPPPPPRRRRRRRRFFFFFFFFNNNNNNNN", m=8) |
||||||
|
x2_8v = lg.MVArray("0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN", m=8) |
||||||
|
|
||||||
|
assert lg.mv_not(x1_2v)[0] == '1100' |
||||||
|
assert lg.mv_not(x1_4v)[0] == '1111XXXXXXXX0000' |
||||||
|
assert lg.mv_not(x1_8v)[0] == '11111111XXXXXXXXXXXXXXXX00000000NNNNNNNNFFFFFFFFRRRRRRRRPPPPPPPP' |
||||||
|
|
||||||
|
assert lg.mv_or(x1_2v, x2_2v)[0] == '0111' |
||||||
|
assert lg.mv_or(x1_4v, x2_4v)[0] == '0XX1XXX1XXX11111' |
||||||
|
assert lg.mv_or(x1_8v, x2_8v)[0] == '0XX1PRFNXXX1XXXXXXX1XXXX11111111PXX1PRFNRXX1RRNNFXX1FNFNNXX1NNNN' |
||||||
|
|
||||||
|
assert lg.mv_and(x1_2v, x2_2v)[0] == '0001' |
||||||
|
assert lg.mv_and(x1_4v, x2_4v)[0] == '00000XXX0XXX0XX1' |
||||||
|
assert lg.mv_and(x1_8v, x2_8v)[0] == '000000000XXXXXXX0XXXXXXX0XX1PRFN0XXPPPPP0XXRPRPR0XXFPPFF0XXNPRFN' |
||||||
|
|
||||||
|
assert lg.mv_xor(x1_2v, x2_2v)[0] == '0110' |
||||||
|
assert lg.mv_xor(x1_4v, x2_4v)[0] == '0XX1XXXXXXXX1XX0' |
||||||
|
assert lg.mv_xor(x1_8v, x2_8v)[0] == '0XX1PRFNXXXXXXXXXXXXXXXX1XX0NFRPPXXNPRFNRXXFRPNFFXXRFNPRNXXPNFRP' |
||||||
|
|
||||||
|
|
||||||
|
def test_bparray(): |
||||||
|
|
||||||
|
ary = lg.BPArray(4) |
||||||
|
assert ary.length == 1 |
||||||
|
assert len(ary) == 1 |
||||||
|
assert ary.width == 4 |
||||||
|
|
||||||
|
ary = lg.BPArray((3, 2)) |
||||||
|
assert ary.length == 2 |
||||||
|
assert len(ary) == 2 |
||||||
|
assert ary.width == 3 |
||||||
|
|
||||||
|
assert lg.MVArray(lg.BPArray("01", m=2))[0] == '01' |
||||||
|
assert lg.MVArray(lg.BPArray("0X-1", m=4))[0] == '0X-1' |
||||||
|
assert lg.MVArray(lg.BPArray("0X-1PRFN", m=8))[0] == '0X-1PRFN' |
||||||
|
|
||||||
|
x1_2v = lg.BPArray("0011", m=2) |
||||||
|
x2_2v = lg.BPArray("0101", m=2) |
||||||
|
x1_4v = lg.BPArray("0000XXXX----1111", m=4) |
||||||
|
x2_4v = lg.BPArray("0X-10X-10X-10X-1", m=4) |
||||||
|
x1_8v = lg.BPArray("00000000XXXXXXXX--------11111111PPPPPPPPRRRRRRRRFFFFFFFFNNNNNNNN", m=8) |
||||||
|
x2_8v = lg.BPArray("0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN0X-1PRFN", m=8) |
||||||
|
|
||||||
|
out_2v = lg.BPArray((4, 1), m=2) |
||||||
|
out_4v = lg.BPArray((16, 1), m=4) |
||||||
|
out_8v = lg.BPArray((64, 1), m=8) |
||||||
|
|
||||||
|
lg.bp_buf(out_2v.data, x1_2v.data) |
||||||
|
lg.bp_buf(out_4v.data, x1_4v.data) |
||||||
|
lg.bp_buf(out_8v.data, x1_8v.data) |
||||||
|
|
||||||
|
assert lg.MVArray(out_2v)[0] == '0011' |
||||||
|
assert lg.MVArray(out_4v)[0] == '0000XXXXXXXX1111' |
||||||
|
assert lg.MVArray(out_8v)[0] == '00000000XXXXXXXXXXXXXXXX11111111PPPPPPPPRRRRRRRRFFFFFFFFNNNNNNNN' |
||||||
|
|
||||||
|
lg.bp_not(out_2v.data, x1_2v.data) |
||||||
|
lg.bp_not(out_4v.data, x1_4v.data) |
||||||
|
lg.bp_not(out_8v.data, x1_8v.data) |
||||||
|
|
||||||
|
assert lg.MVArray(out_2v)[0] == '1100' |
||||||
|
assert lg.MVArray(out_4v)[0] == '1111XXXXXXXX0000' |
||||||
|
assert lg.MVArray(out_8v)[0] == '11111111XXXXXXXXXXXXXXXX00000000NNNNNNNNFFFFFFFFRRRRRRRRPPPPPPPP' |
||||||
|
|
||||||
|
lg.bp_or(out_2v.data, x1_2v.data, x2_2v.data) |
||||||
|
lg.bp_or(out_4v.data, x1_4v.data, x2_4v.data) |
||||||
|
lg.bp_or(out_8v.data, x1_8v.data, x2_8v.data) |
||||||
|
|
||||||
|
assert lg.MVArray(out_2v)[0] == '0111' |
||||||
|
assert lg.MVArray(out_4v)[0] == '0XX1XXX1XXX11111' |
||||||
|
assert lg.MVArray(out_8v)[0] == '0XX1PRFNXXX1XXXXXXX1XXXX11111111PXX1PRFNRXX1RRNNFXX1FNFNNXX1NNNN' |
||||||
|
|
||||||
|
lg.bp_and(out_2v.data, x1_2v.data, x2_2v.data) |
||||||
|
lg.bp_and(out_4v.data, x1_4v.data, x2_4v.data) |
||||||
|
lg.bp_and(out_8v.data, x1_8v.data, x2_8v.data) |
||||||
|
|
||||||
|
assert lg.MVArray(out_2v)[0] == '0001' |
||||||
|
assert lg.MVArray(out_4v)[0] == '00000XXX0XXX0XX1' |
||||||
|
assert lg.MVArray(out_8v)[0] == '000000000XXXXXXX0XXXXXXX0XX1PRFN0XXPPPPP0XXRPRPR0XXFPPFF0XXNPRFN' |
||||||
|
|
||||||
|
lg.bp_xor(out_2v.data, x1_2v.data, x2_2v.data) |
||||||
|
lg.bp_xor(out_4v.data, x1_4v.data, x2_4v.data) |
||||||
|
lg.bp_xor(out_8v.data, x1_8v.data, x2_8v.data) |
||||||
|
|
||||||
|
assert lg.MVArray(out_2v)[0] == '0110' |
||||||
|
assert lg.MVArray(out_4v)[0] == '0XX1XXXXXXXX1XX0' |
||||||
|
assert lg.MVArray(out_8v)[0] == '0XX1PRFNXXXXXXXXXXXXXXXX1XX0NFRPPXXNPRFNRXXFRPNFFXXRFNPRNXXPNFRP' |
@ -1,161 +1,96 @@ |
|||||||
from kyupy.logic_sim import LogicSim |
from kyupy.logic_sim import LogicSim |
||||||
from kyupy import bench |
from kyupy import bench |
||||||
from kyupy.packed_vectors import PackedVectors |
from kyupy.logic import MVArray, BPArray |
||||||
|
|
||||||
|
|
||||||
def test_vd1(): |
def test_2v(): |
||||||
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)') |
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)') |
||||||
s = LogicSim(c, 4) |
s = LogicSim(c, 4, m=2) |
||||||
assert len(s.interface) == 5 |
assert len(s.interface) == 5 |
||||||
p = PackedVectors(4, len(s.interface)) |
mva = MVArray(['00000', '01000', '10000', '11000'], m=2) |
||||||
p[0] = '00000' |
bpa = BPArray(mva) |
||||||
p[1] = '01000' |
s.assign(bpa) |
||||||
p[2] = '10000' |
|
||||||
p[3] = '11000' |
|
||||||
s.assign(p) |
|
||||||
s.propagate() |
s.propagate() |
||||||
s.capture(p) |
s.capture(bpa) |
||||||
assert p[0] == '00001' |
mva = MVArray(bpa) |
||||||
assert p[1] == '01011' |
assert mva[0] == '00001' |
||||||
assert p[2] == '10010' |
assert mva[1] == '01011' |
||||||
assert p[3] == '11110' |
assert mva[2] == '10010' |
||||||
|
assert mva[3] == '11110' |
||||||
|
|
||||||
|
|
||||||
def test_vd2(): |
def test_4v(): |
||||||
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)') |
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)') |
||||||
s = LogicSim(c, 16, 2) |
s = LogicSim(c, 16, m=4) |
||||||
assert len(s.interface) == 5 |
assert len(s.interface) == 5 |
||||||
p = PackedVectors(16, len(s.interface), 2) |
mva = MVArray(['00000', '01000', '0-000', '0X000', |
||||||
p[0] = '00000' |
'10000', '11000', '1-000', '1X000', |
||||||
p[1] = '01000' |
'-0000', '-1000', '--000', '-X000', |
||||||
p[2] = '0-000' |
'X0000', 'X1000', 'X-000', 'XX000'], m=4) |
||||||
p[3] = '0X000' |
bpa = BPArray(mva) |
||||||
p[4] = '10000' |
s.assign(bpa) |
||||||
p[5] = '11000' |
|
||||||
p[6] = '1-000' |
|
||||||
p[7] = '1X000' |
|
||||||
p[8] = '-0000' |
|
||||||
p[9] = '-1000' |
|
||||||
p[10] = '--000' |
|
||||||
p[11] = '-X000' |
|
||||||
p[12] = 'X0000' |
|
||||||
p[13] = 'X1000' |
|
||||||
p[14] = 'X-000' |
|
||||||
p[15] = 'XX000' |
|
||||||
s.assign(p) |
|
||||||
s.propagate() |
s.propagate() |
||||||
s.capture(p) |
s.capture(bpa) |
||||||
assert p[0] == '00001' |
mva = MVArray(bpa) |
||||||
assert p[1] == '01011' |
assert mva[0] == '00001' |
||||||
assert p[2] == '0-0X1' |
assert mva[1] == '01011' |
||||||
assert p[3] == '0X0X1' |
assert mva[2] == '0-0X1' |
||||||
assert p[4] == '10010' |
assert mva[3] == '0X0X1' |
||||||
assert p[5] == '11110' |
assert mva[4] == '10010' |
||||||
assert p[6] == '1-X10' |
assert mva[5] == '11110' |
||||||
assert p[7] == '1XX10' |
assert mva[6] == '1-X10' |
||||||
assert p[8] == '-00XX' |
assert mva[7] == '1XX10' |
||||||
assert p[9] == '-1X1X' |
assert mva[8] == '-00XX' |
||||||
assert p[10] == '--XXX' |
assert mva[9] == '-1X1X' |
||||||
assert p[11] == '-XXXX' |
assert mva[10] == '--XXX' |
||||||
assert p[12] == 'X00XX' |
assert mva[11] == '-XXXX' |
||||||
assert p[13] == 'X1X1X' |
assert mva[12] == 'X00XX' |
||||||
assert p[14] == 'X-XXX' |
assert mva[13] == 'X1X1X' |
||||||
assert p[15] == 'XXXXX' |
assert mva[14] == 'X-XXX' |
||||||
|
assert mva[15] == 'XXXXX' |
||||||
|
|
||||||
|
|
||||||
def test_vd3(): |
def test_8v(): |
||||||
c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)') |
c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)') |
||||||
s = LogicSim(c, 64, 3) |
s = LogicSim(c, 64, m=8) |
||||||
assert len(s.interface) == 6 |
assert len(s.interface) == 6 |
||||||
p = PackedVectors(64, len(s.interface), 3) |
mva = MVArray(['000010', '010111', '0-0X1X', '0X0X1X', '0R0R1R', '0F0F1F', '0P0P1P', '0N0N1N', |
||||||
p[0] = '000010' |
'100101', '111100', '1-X10X', '1XX10X', '1RR10F', '1FF10R', '1PP10N', '1NN10P', |
||||||
p[1] = '010111' |
'-00XXX', '-1X1XX', '--XXXX', '-XXXXX', '-RXXXX', '-FXXXX', '-PXXXX', '-NXXXX', |
||||||
p[2] = '0-0X1X' |
'X00XXX', 'X1X1XX', 'X-XXXX', 'XXXXXX', 'XRXXXX', 'XFXXXX', 'XPXXXX', 'XNXXXX', |
||||||
p[3] = '0X0X1X' |
'R00RFR', 'R1R1FF', 'R-XXFX', 'RXXXFX', 'RRRRFP', 'RFPNFN', 'RPPRFR', 'RNRNFF', |
||||||
p[4] = '0R0R1R' |
'F00FRF', 'F1F1RR', 'F-XXRX', 'FXXXRX', 'FRPNRN', 'FFFFRP', 'FPPFRF', 'FNFNRR', |
||||||
p[5] = '0F0F1F' |
'P00PNP', 'P1P1NN', 'P-XXNX', 'PXXXNX', 'PRPRNR', 'PFPFNF', 'PPPPNP', 'PNPNNN', |
||||||
p[6] = '0P0P1P' |
'N00NPN', 'N1N1PP', 'N-XXPX', 'NXXXPX', 'NRRNPF', 'NFFNPR', 'NPPNPN', 'NNNNPP'], m=8) |
||||||
p[7] = '0N0N1N' |
bpa = BPArray(mva) |
||||||
p[8] = '100101' |
s.assign(bpa) |
||||||
p[9] = '111100' |
|
||||||
p[10] = '1-X10X' |
|
||||||
p[11] = '1XX10X' |
|
||||||
p[12] = '1RR10F' |
|
||||||
p[13] = '1FF10R' |
|
||||||
p[14] = '1PP10N' |
|
||||||
p[15] = '1NN10P' |
|
||||||
p[16] = '-00XXX' |
|
||||||
p[17] = '-1X1XX' |
|
||||||
p[18] = '--XXXX' |
|
||||||
p[19] = '-XXXXX' |
|
||||||
p[20] = '-RXXXX' |
|
||||||
p[21] = '-FXXXX' |
|
||||||
p[22] = '-PXXXX' |
|
||||||
p[23] = '-NXXXX' |
|
||||||
p[24] = 'X00XXX' |
|
||||||
p[25] = 'X1X1XX' |
|
||||||
p[26] = 'X-XXXX' |
|
||||||
p[27] = 'XXXXXX' |
|
||||||
p[28] = 'XRXXXX' |
|
||||||
p[29] = 'XFXXXX' |
|
||||||
p[30] = 'XPXXXX' |
|
||||||
p[31] = 'XNXXXX' |
|
||||||
p[32] = 'R00RFR' |
|
||||||
p[33] = 'R1R1FF' |
|
||||||
p[34] = 'R-XXFX' |
|
||||||
p[35] = 'RXXXFX' |
|
||||||
p[36] = 'RRRRFP' |
|
||||||
p[37] = 'RFPNFN' |
|
||||||
p[38] = 'RPPRFR' |
|
||||||
p[39] = 'RNRNFF' |
|
||||||
p[40] = 'F00FRF' |
|
||||||
p[41] = 'F1F1RR' |
|
||||||
p[42] = 'F-XXRX' |
|
||||||
p[43] = 'FXXXRX' |
|
||||||
p[44] = 'FRPNRN' |
|
||||||
p[45] = 'FFFFRP' |
|
||||||
p[46] = 'FPPFRF' |
|
||||||
p[47] = 'FNFNRR' |
|
||||||
p[48] = 'P00PNP' |
|
||||||
p[49] = 'P1P1NN' |
|
||||||
p[50] = 'P-XXNX' |
|
||||||
p[51] = 'PXXXNX' |
|
||||||
p[52] = 'PRPRNR' |
|
||||||
p[53] = 'PFPFNF' |
|
||||||
p[54] = 'PPPPNP' |
|
||||||
p[55] = 'PNPNNN' |
|
||||||
p[56] = 'N00NPN' |
|
||||||
p[57] = 'N1N1PP' |
|
||||||
p[58] = 'N-XXPX' |
|
||||||
p[59] = 'NXXXPX' |
|
||||||
p[60] = 'NRRNPF' |
|
||||||
p[61] = 'NFFNPR' |
|
||||||
p[62] = 'NPPNPN' |
|
||||||
p[63] = 'NNNNPP' |
|
||||||
expect = p.copy() |
|
||||||
s.assign(p) |
|
||||||
s.propagate() |
s.propagate() |
||||||
s.capture(p) |
resp_bp = BPArray(bpa) |
||||||
|
s.capture(resp_bp) |
||||||
|
resp = MVArray(resp_bp) |
||||||
|
|
||||||
for i in range(64): |
for i in range(64): |
||||||
assert p[i] == expect[i] |
assert resp[i] == mva[i] |
||||||
|
|
||||||
|
|
||||||
def test_b01(mydir): |
def test_b01(mydir): |
||||||
c = bench.parse(mydir / 'b01.bench') |
c = bench.load(mydir / 'b01.bench') |
||||||
|
|
||||||
# 2-valued |
# 2-valued |
||||||
s = LogicSim(c, 8) |
s = LogicSim(c, 8, m=2) |
||||||
assert len(s.interface) == 9 |
assert len(s.interface) == 9 |
||||||
t = PackedVectors(8, len(s.interface)) |
mva = MVArray((len(s.interface), 8), m=2) |
||||||
t.randomize() |
# mva.randomize() |
||||||
s.assign(t) |
bpa = BPArray(mva) |
||||||
|
s.assign(bpa) |
||||||
s.propagate() |
s.propagate() |
||||||
s.capture(t) |
s.capture(bpa) |
||||||
|
|
||||||
# 8-valued |
# 8-valued |
||||||
s = LogicSim(c, 8, 3) |
s = LogicSim(c, 8, m=8) |
||||||
t = PackedVectors(8, len(s.interface), 3) |
mva = MVArray((len(s.interface), 8), m=8) |
||||||
t.randomize() |
# mva.randomize() |
||||||
s.assign(t) |
bpa = BPArray(mva) |
||||||
|
s.assign(bpa) |
||||||
s.propagate() |
s.propagate() |
||||||
s.capture(t) |
s.capture(bpa) |
||||||
|
@ -1,88 +0,0 @@ |
|||||||
from kyupy.packed_vectors import PackedVectors |
|
||||||
|
|
||||||
|
|
||||||
def test_basic(): |
|
||||||
ba = PackedVectors(8, 1, 1) |
|
||||||
assert '0\n0\n0\n0\n0\n0\n0\n0' == str(ba) |
|
||||||
ba.set_value(0, 0, 1) |
|
||||||
ba.set_value(1, 0, 'H') |
|
||||||
ba.set_value(2, 0, 'h') |
|
||||||
ba.set_value(3, 0, True) |
|
||||||
ba.set_value(4, 0, 0) |
|
||||||
ba.set_value(5, 0, 'L') |
|
||||||
ba.set_value(6, 0, 'l') |
|
||||||
ba.set_value(7, 0, False) |
|
||||||
assert '1\n1\n1\n1\n0\n0\n0\n0' == str(ba) |
|
||||||
ba.set_value(1, 0, '0') |
|
||||||
ba.set_value(5, 0, '1') |
|
||||||
assert '1\n0\n1\n1\n0\n1\n0\n0' == str(ba) |
|
||||||
ba = PackedVectors(8, 1, 2) |
|
||||||
assert '-\n-\n-\n-\n-\n-\n-\n-' == str(ba) |
|
||||||
ba.set_value(0, 0, 1) |
|
||||||
ba.set_value(7, 0, 0) |
|
||||||
ba.set_value(4, 0, 'X') |
|
||||||
assert '1\n-\n-\n-\nX\n-\n-\n0' == str(ba) |
|
||||||
ba.set_value(4, 0, '-') |
|
||||||
assert '1\n-\n-\n-\n-\n-\n-\n0' == str(ba) |
|
||||||
ba = PackedVectors(8, 2, 2) |
|
||||||
assert '--\n--\n--\n--\n--\n--\n--\n--' == str(ba) |
|
||||||
ba.set_value(0, 0, '1') |
|
||||||
ba.set_value(7, 1, '0') |
|
||||||
ba.set_values(1, 'XX') |
|
||||||
assert '1-\nXX\n--\n--\n--\n--\n--\n-0' == str(ba) |
|
||||||
|
|
||||||
|
|
||||||
def test_8v(): |
|
||||||
ba = PackedVectors(1, 8, 3) |
|
||||||
assert '--------' == str(ba) |
|
||||||
ba.set_values(0, r'-x01^v\/') |
|
||||||
assert r'-X01PNFR' == str(ba) |
|
||||||
ba.set_values(0, '-XLHPNFR') |
|
||||||
assert r'-X01PNFR' == str(ba) |
|
||||||
ba.set_values(0, '-xlhpnfr') |
|
||||||
assert r'-X01PNFR' == str(ba) |
|
||||||
p1 = PackedVectors(1, 8, 1) |
|
||||||
p2 = PackedVectors(1, 8, 1) |
|
||||||
p1.set_values(0, '01010101') |
|
||||||
p2.set_values(0, '00110011') |
|
||||||
p = PackedVectors.from_pair(p1, p2) |
|
||||||
assert r'0FR10FR1' == str(p) |
|
||||||
p1 = PackedVectors(1, 8, 2) |
|
||||||
p2 = PackedVectors(1, 8, 2) |
|
||||||
p1.set_values(0, '0101-X-X') |
|
||||||
p2.set_values(0, '00110011') |
|
||||||
p = PackedVectors.from_pair(p1, p2) |
|
||||||
assert r'0FR1----' == str(p) |
|
||||||
p1.set_values(0, '0101-X-X') |
|
||||||
p2.set_values(0, '-X-X--XX') |
|
||||||
p = PackedVectors.from_pair(p1, p2) |
|
||||||
assert r'--------' == str(p) |
|
||||||
|
|
||||||
|
|
||||||
def test_slicing(): |
|
||||||
lv = PackedVectors(3, 2, 1) |
|
||||||
assert '00\n00\n00' == str(lv) |
|
||||||
lv.set_value(1, 0, '1') |
|
||||||
lv.set_value(1, 1, '1') |
|
||||||
assert '00' == lv[0] |
|
||||||
assert '11' == lv[1] |
|
||||||
assert 3 == len(lv) |
|
||||||
lv2 = lv[1:3] |
|
||||||
assert 2 == len(lv2) |
|
||||||
assert '11' == lv2[0] |
|
||||||
assert '00' == lv2[1] |
|
||||||
|
|
||||||
|
|
||||||
def test_copy(): |
|
||||||
lv1 = PackedVectors(8, 1, 1) |
|
||||||
lv1.set_values_for_position(0, '01010101') |
|
||||||
lv2 = PackedVectors(8, 1, 1) |
|
||||||
lv2.set_values_for_position(0, '00100101') |
|
||||||
diff = lv1.diff(lv2) |
|
||||||
lv3 = lv1.copy(selection_mask=diff) |
|
||||||
assert str(lv3) == '1\n0\n1' |
|
||||||
lv4 = lv1.copy(selection_mask=~diff) |
|
||||||
assert str(lv4) == '0\n0\n1\n0\n1' |
|
||||||
lv5 = lv3 + lv4 |
|
||||||
assert str(lv5) == '1\n0\n1\n0\n0\n1\n0\n1' |
|
||||||
|
|
Loading…
Reference in new issue