@ -58,6 +58,12 @@ on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``.
@@ -58,6 +58,12 @@ on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``.
def interpret ( value ) :
""" Converts characters, strings, and lists of them to lists of logic constants defined above.
: param value : A character ( string of length 1 ) , Boolean , Integer , None , or Iterable .
Iterables ( such as strings ) are traversed and their individual characters are interpreted .
: return : A logic constant or a ( possibly multi - dimensional ) list of logic constants .
"""
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 ' ] :
@ -85,6 +91,79 @@ def bit_in(a, pos):
@@ -85,6 +91,79 @@ def bit_in(a, pos):
return a [ pos >> 3 ] & _bit_in_lut [ pos & 7 ]
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 , manipulations of individual values are cheaper than in
: py : class : ` BPArray ` .
: param a : If a tuple is given , it is interpreted as desired shape . To make an array of ` ` n ` ` vectors
compatible with a simulator ` ` sim ` ` , use ` ` ( len ( sim . interface ) , n ) ` ` . If a : py : class : ` BPArray ` or
: py : class : ` MVArray ` is given , a deep copy is made . If a string , a list of strings , a list of characters ,
or a list of lists of characters are given , the data is interpreted best - effort and the array is
initialized accordingly .
: param m : The arity of the logic . Can be set to 2 , 4 , or 8. If None is given , the arity of a given
: py : class : ` BPArray ` or : py : class : ` MVArray ` is used , or , if the array is initialized differently , 8 is used .
"""
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 ( )
""" The wrapped 2-dimensional ndarray of logic values.
* 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 " .
"""
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 } mem= { hr_bytes ( self . data . nbytes ) } > '
def __str__ ( self ) :
return str ( [ self [ idx ] for idx in range ( self . length ) ] )
def __getitem__ ( self , vector_idx ) :
""" Returns a string representing the desired vector. """
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 mv_cast ( * args , m = 8 ) :
return [ a if isinstance ( a , MVArray ) else MVArray ( a , m = m ) for a in args ]
@ -100,6 +179,13 @@ def _mv_not(m, out, inp):
@@ -100,6 +179,13 @@ def _mv_not(m, out, inp):
def mv_not ( x1 , out = None ) :
""" A multi-valued NOT operator.
: param x1 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param out : Optionally an : py : class : ` MVArray ` as storage destination . If None , a new : py : class : ` MVArray `
is returned .
: return : An : py : class : ` MVArray ` with the result .
"""
m = mv_getm ( x1 )
x1 = mv_cast ( x1 , m = m ) [ 0 ]
out = out or MVArray ( x1 . data . shape , m = m )
@ -125,6 +211,14 @@ def _mv_or(m, out, *ins):
@@ -125,6 +211,14 @@ def _mv_or(m, out, *ins):
def mv_or ( x1 , x2 , out = None ) :
""" A multi-valued OR operator.
: param x1 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param x2 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param out : Optionally an : py : class : ` MVArray ` as storage destination . If None , a new : py : class : ` MVArray `
is returned .
: return : An : py : class : ` MVArray ` with the result .
"""
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 )
@ -151,6 +245,14 @@ def _mv_and(m, out, *ins):
@@ -151,6 +245,14 @@ def _mv_and(m, out, *ins):
def mv_and ( x1 , x2 , out = None ) :
""" A multi-valued AND operator.
: param x1 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param x2 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param out : Optionally an : py : class : ` MVArray ` as storage destination . If None , a new : py : class : ` MVArray `
is returned .
: return : An : py : class : ` MVArray ` with the result .
"""
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 )
@ -174,6 +276,14 @@ def _mv_xor(m, out, *ins):
@@ -174,6 +276,14 @@ def _mv_xor(m, out, *ins):
def mv_xor ( x1 , x2 , out = None ) :
""" A multi-valued XOR operator.
: param x1 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param x2 : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param out : Optionally an : py : class : ` MVArray ` as storage destination . If None , a new : py : class : ` MVArray `
is returned .
: return : An : py : class : ` MVArray ` with the result .
"""
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 )
@ -182,6 +292,16 @@ def mv_xor(x1, x2, out=None):
@@ -182,6 +292,16 @@ def mv_xor(x1, x2, out=None):
def mv_transition ( init , final , out = None ) :
""" Computes the logic transitions from the initial values of ``init`` to the final values of ``final``.
Pulses in the input data are ignored . If any of the inputs are ` ` UNKNOWN ` ` , the result is ` ` UNKNOWN ` ` .
If both inputs are ` ` UNASSIGNED ` ` , the result is ` ` UNASSIGNED ` ` .
: param init : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param final : An : py : class : ` MVArray ` or data the : py : class : ` MVArray ` constructor accepts .
: param out : Optionally an : py : class : ` MVArray ` as storage destination . If None , a new : py : class : ` MVArray `
is returned .
: return : An : py : class : ` MVArray ` with the result .
"""
m = mv_getm ( init , final )
init , final = mv_cast ( init , final , m = m )
init = init . data
@ -196,65 +316,46 @@ def mv_transition(init, final, out=None):
@@ -196,65 +316,46 @@ def mv_transition(init, final, out=None):
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 :
class BPArray :
""" An n-dimensional array of m-valued logic values that uses bit-parallel storage.
* 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 " .
The primary use of this format is in aiding efficient bit - parallel logic simulation .
The secondary benefit over : py : class : ` MVArray ` is its memory efficiency .
Accessing individual values is more expensive than with : py : class : ` MVArray ` .
Therefore it may be more efficient to unpack the data into an : py : class : ` MVArray ` and pack it again into a
: py : class : ` BPArray ` for simulation .
See : py : class : ` MVArray ` for constructor parameters .
"""
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 not isinstance ( a , MVArray ) and not isinstance ( a , BPArray ) :
a = MVArray ( a , m )
self . m = a . m
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
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 )
""" The wrapped 3-dimensional ndarray.
self . length = self . data . shape [ - 1 ]
self . width = self . data . shape [ - 2 ]
* Axis 0 is PI / PO / FF position , the length of this axis is called " width " .
* Axis 1 has length ` ` ceil ( log2 ( m ) ) ` ` for storing all bits .
* Axis 2 are the vectors / patterns packed into uint8 words .
"""
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 ' <MVArray length= { self . length } width= { self . width } m= { self . m } mem= { hr_bytes ( 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 ] )
return f ' <BPArray length= { self . length } width= { self . width } m= { self . m } mem= { hr_bytes ( self . data . nbytes ) } > '
def __len__ ( self ) :
return self . length
@ -359,44 +460,3 @@ def bp_xor(out, *ins):
@@ -359,44 +460,3 @@ def bp_xor(out, *ins):
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 } mem= { hr_bytes ( self . data . nbytes ) } > '
def __len__ ( self ) :
return self . length