Migration to new logic value representation

This commit is contained in:
Stefan Holst committed 2020-12-21 19:16:52 +09:00
1 parent 7bcfbf502b
commit 7f035c1ac5
12 files changed
+616 -940

No files matched your search

+195 -13
View File
@@ -181,6 +181,21 @@ def mv_xor(x1, x2, out=None):
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.
@@ -188,10 +203,10 @@ class MVArray:
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 or more axes:
An MVArray always has 2 axes:
* Second-last axis is PI/PO/FF position, the length of this axis is called "width".
* Last axis is vector/pattern, the length of this axis is called "length".
* 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".
"""
@@ -203,18 +218,22 @@ class MVArray:
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]
if isinstance(a, str): a = [a]
self.data = np.asarray(interpret(a), dtype=np.uint8)
if self.data.ndim == 1:
self.data = self.data[:, np.newaxis]
else:
self.data = np.moveaxis(self.data, -2, -1)
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:
@@ -231,8 +250,166 @@ class MVArray:
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 str([''.join(chars[v] for v in self.data[:, idx]) for idx in range(self.length)])
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):
# 4-valued:
# 0 1 - X
# 0 0 1 X X
# 1 1 1 1 1
# - X 1 X X
# X X 1 X X
# 8-valued: o[0]: o[1]: o[2]:
# 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N
# 0 0 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 1 1 1 1
# 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# - X 1 X X X X X X 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# X X 1 X X X X X X 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# R R 1 X X R N R R 0 1 1 1 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 1 1
# F F 1 X X N F F F 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 0 0 1 1 1 1
# P P 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 1 0 0 0 1 1 1 1
# N N 1 X X R F N N 1 1 1 1 0 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 1 1
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):
# 4-valued:
# 0 1 - X
# 0 0 0 0 0
# 1 0 1 X X
# - 0 X X X
# X 0 X X X
# 8-valued:
# 0 1 - X R F P N
# 0 0 0 0 0 0 0 0 0
# 1 0 1 X X R F P N
# - 0 X X X X X X X
# X 0 X X X X X X X
# R 0 R X X R R P R
# F 0 F X X R F P F
# P 0 P X X P P P P
# N 0 N X X R F P N
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):
# 4-valued:
# 0 1 - X
# 0 0 1 X X
# 1 1 0 X X
# - X X X X
# X X X X X
# 8-valued: o[0]: o[1]: o[2]:
# 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N
# 0 0 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 1 1 1 1
# 1 1 0 X X F R N P 1 0 1 1 1 0 1 0 0 1 1 1 1 0 0 1 0 0 0 0 1 1 1 1
# - X X X X X X X X 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# X X X X X X X X X 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# R R F X X P N R F 0 1 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1
# F F R X X N P F R 1 0 1 1 1 0 1 0 1 0 1 1 0 1 1 0 1 1 0 0 1 1 1 1
# P P N X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 1 1 0 0 1 1 1 1
# N N P X X F R N P 1 0 1 1 1 0 1 0 0 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1
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:
@@ -240,9 +417,9 @@ class BPArray:
The primary use of this format is in aiding efficient bit-parallel logic simulation.
The secondary benefit over MVArray is its memory efficiency.
Direct value manipulations are more expensive than with MVArray.
It is advised to first construct a MVArray, pack it into a BPArray for simulation and unpack the results
back into a MVArray for value access.
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.
@@ -252,6 +429,7 @@ class BPArray:
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
@@ -264,8 +442,12 @@ class BPArray:
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
+42 -235
View File
@@ -1,22 +1,25 @@
import math
import numpy as np
from . import packed_vectors
from . import logic
class LogicSim:
"""A bit-parallel naïve combinational simulator for 2-, 4-, or 8-valued logic.
"""
def __init__(self, circuit, nvectors=1, vdim=1):
def __init__(self, circuit, sims=1, m=8):
assert m in [2, 4, 8]
self.m = m
mdim = math.ceil(math.log2(m))
self.circuit = circuit
self.nvectors = nvectors
nbytes = (nvectors - 1) // 8 + 1
self.sims = sims
nbytes = (sims - 1) // 8 + 1
self.interface = list(circuit.interface) + [n for n in circuit.nodes if 'dff' in n.kind.lower()]
self.state = np.zeros((len(circuit.lines), vdim, nbytes), dtype='uint8')
self.state = np.zeros((len(circuit.lines), mdim, nbytes), dtype='uint8')
self.state_epoch = np.zeros(len(circuit.nodes), dtype='int8') - 1
self.tmp = np.zeros((5, vdim, nbytes), dtype='uint8')
self.zero = np.zeros((vdim, nbytes), dtype='uint8')
if vdim > 1:
self.zero[1] = 255
self.tmp = np.zeros((5, mdim, nbytes), dtype='uint8')
self.zero = np.zeros((mdim, nbytes), dtype='uint8')
self.epoch = 0
self.fork_vd1 = self.fork_vdx
@@ -46,7 +49,7 @@ class LogicSim:
self.nbuff_vd3 = self.fork_vd3
self.xor2_vd3 = self.xor_vd3
known_fct = [(f[:-4], getattr(self, f)) for f in dir(self) if f.endswith(f'_vd{vdim}')]
known_fct = [(f[:-4], getattr(self, f)) for f in dir(self) if f.endswith(f'_vd{mdim}')]
self.node_fct = []
for n in circuit.nodes:
t = n.kind.lower().replace('__fork__', 'fork')
@@ -60,9 +63,9 @@ class LogicSim:
def assign(self, stimuli):
"""Assign stimuli to the primary inputs and state-elements (flip-flops)."""
if isinstance(stimuli, packed_vectors.PackedVectors):
stimuli = stimuli.bits
for (stim, node) in zip(stimuli, self.interface):
if hasattr(stimuli, 'data'):
stimuli = stimuli.data
for stim, node in zip(stimuli, self.interface):
if len(node.outs) == 0: continue
outputs = [self.state[line.index] if line else self.tmp[3] for line in node.outs]
self.node_fct[node.index]([stim], outputs)
@@ -80,11 +83,12 @@ class LogicSim:
def capture(self, responses):
"""Capture the current values at the primary outputs and in the state-elements (flip-flops)."""
if isinstance(responses, packed_vectors.PackedVectors):
responses = responses.bits
for (resp, node) in zip(responses, self.interface):
if hasattr(responses, 'data'):
responses = responses.data
for resp, node in zip(responses, self.interface):
if len(node.ins) == 0: continue
resp[...] = self.state[node.ins[0].index]
# print(responses)
def propagate(self):
"""Propagate the input values towards the outputs (Perform all logic operations in topological order)."""
@@ -98,8 +102,7 @@ class LogicSim:
self.state_epoch[line.reader.index] = self.epoch
self.epoch = (self.epoch + 1) % 128
@staticmethod
def fork_vdx(inputs, outputs):
def fork_vdx(self, inputs, outputs):
for o in outputs: o[...] = inputs[0]
def const0_vdx(self, _, outputs):
@@ -107,40 +110,34 @@ class LogicSim:
# 2-valued simulation
@staticmethod
def not_vd1(inputs, outputs):
def not_vd1(self, inputs, outputs):
outputs[0][0] = ~inputs[0][0]
def const1_vd1(self, _, outputs):
for o in outputs: o[...] = self.zero
self.not_vd1(outputs, outputs)
@staticmethod
def and_vd1(inputs, outputs):
def and_vd1(self, inputs, outputs):
o = outputs[0]
o[0] = inputs[0][0]
for i in inputs[1:]: o[0] &= i[0]
@staticmethod
def or_vd1(inputs, outputs):
def or_vd1(self, inputs, outputs):
o = outputs[0]
o[0] = inputs[0][0]
for i in inputs[1:]: o[0] |= i[0]
@staticmethod
def xor_vd1(inputs, outputs):
def xor_vd1(self, inputs, outputs):
o = outputs[0]
o[0] = inputs[0][0]
for i in inputs[1:]: o[0] ^= i[0]
@staticmethod
def sdff_vd1(inputs, outputs):
def sdff_vd1(self, inputs, outputs):
outputs[0][0] = inputs[0][0]
if len(outputs) > 1:
outputs[1][0] = ~inputs[0][0]
@staticmethod
def dff_vd1(inputs, outputs):
def dff_vd1(self, inputs, outputs):
outputs[0][0] = inputs[0][0]
if len(outputs) > 1:
outputs[1][0] = ~inputs[0][0]
@@ -158,93 +155,26 @@ class LogicSim:
self.not_vd1(outputs, outputs)
# 4-valued simulation
# sym [0] [1] (value, care)
# 0 0 1
# 1 1 1
# - 0 0
# X 1 0
@staticmethod
def not_vd2(inputs, outputs):
# 4-valued not:
# i: 0 1 - X
# o: 1 0 X X
# o0 1 0 1 1
# o1 1 1 0 0
outputs[0][0] = ~inputs[0][0] | ~inputs[0][1] # value = 0 or DC
outputs[0][1] = inputs[0][1] # care = C
def not_vd2(self, inputs, outputs):
logic.bp_not(outputs[0], inputs[0])
def and_vd2(self, inputs, outputs):
# 4-valued: o[0]: o[1]:
# 0 1 - X 0 1 - X 0 1 - X
# 0 0 0 0 0 0 0 0 0 1 1 1 1
# 1 0 1 X X 0 1 1 1 1 1 0 0
# - 0 X X X 0 1 1 1 1 0 0 0
# X 0 X X X 0 1 1 1 1 0 0 0
i = inputs[0]
any0 = self.tmp[0]
anyd = self.tmp[1]
any0[0] = ~i[0] & i[1]
anyd[0] = ~i[1]
for i in inputs[1:]:
any0[0] |= ~i[0] & i[1]
anyd[0] |= ~i[1]
o = outputs[0]
o[0] = ~any0[0] # value = no0
o[1] = any0[0] | ~anyd[0] # care = any0 or noDC
logic.bp_and(outputs[0], *inputs)
def or_vd2(self, inputs, outputs):
# 4-valued: o[0]: o[1]:
# 0 1 - X 0 1 - X 0 1 - X
# 0 0 1 X X 0 1 1 1 1 1 0 0
# 1 1 1 1 1 1 1 1 1 1 1 1 1
# - X 1 X X 1 1 1 1 0 1 0 0
# X X 1 X X 1 1 1 1 0 1 0 0
i = inputs[0]
any1 = self.tmp[0]
anyd = self.tmp[1]
any1[0] = i[0] & i[1]
anyd[0] = ~i[1]
for i in inputs[1:]:
any1[0] |= i[0] & i[1]
anyd[0] |= ~i[1]
o = outputs[0]
o[0] = any1[0] | anyd[0] # value = any1 or anyDC
o[1] = any1[0] | ~anyd[0] # care = any1 or noDC
logic.bp_or(outputs[0], *inputs)
def xor_vd2(self, inputs, outputs):
# 4-valued: o[0]: o[1]:
# 0 1 - X 0 1 - X 0 1 - X
# 0 0 1 X X 0 1 1 1 1 1 0 0
# 1 1 0 X X 1 0 1 1 1 1 0 0
# - X X X X 1 1 1 1 0 0 0 0
# X X X X X 1 1 1 1 0 0 0 0
i = inputs[0]
odd1 = self.tmp[0]
anyd = self.tmp[1]
odd1[0] = i[0] & i[1]
anyd[0] = ~i[1]
for i in inputs[1:]:
odd1[0] ^= i[0] & i[1]
anyd[0] |= ~i[1]
o = outputs[0]
o[0] = odd1[0] | anyd[0] # value = odd1 or anyDC
o[1] = ~anyd[0] # care = noDC
logic.bp_xor(outputs[0], *inputs)
def sdff_vd2(self, inputs, outputs):
self.dff_vd2(inputs, outputs)
if len(outputs) > 1:
outputs[1][0] = ~inputs[0][0] | ~inputs[0][1] # value = 0 or DC
outputs[1][1] = inputs[0][1] # care = C
logic.bp_not(outputs[1], inputs[0])
@staticmethod
def dff_vd2(inputs, outputs):
outputs[0][0] = inputs[0][0] | ~inputs[0][1] # value = 1 or DC
outputs[0][1] = inputs[0][1] # care = C
def dff_vd2(self, inputs, outputs):
logic.bp_buf(outputs[0], inputs[0])
def nand_vd2(self, inputs, outputs):
self.and_vd2(inputs, outputs)
@@ -263,149 +193,26 @@ class LogicSim:
self.not_vd2(outputs, outputs)
# 8-valued simulation
# sym [0] [1] [2] (initial value, ~final value, toggles present?)
# 0 0 1 0
# 1 1 0 0
# - 0 0 0
# X 1 1 0
# R 0 0 1 _/"
# F 1 1 1 "\_
# P 0 1 1 _/\_
# N 1 0 1 "\/"
def not_vd3(self, inputs, outputs):
# 8-valued not:
# i: 0 1 - X R F P N
# i0 0 1 0 1 0 1 0 1
# i1 1 0 0 1 0 1 1 0
# i2 0 0 0 0 1 1 1 1
# o: 1 0 X X F R N P
# o0 1 0 1 1 1 0 1 0
# o1 0 1 1 1 1 0 0 1
# o2 0 0 0 0 1 1 1 1
i = inputs[0]
dc = self.tmp[0]
dc[0] = ~(i[0] ^ i[1]) & ~i[2]
dc = self.tmp[0]
outputs[0][0] = ~i[0] | dc[0] # init.v = ~i0 or DC
outputs[0][1] = ~i[1] | dc[0] # init.v = ~i1 or DC
outputs[0][2] = i[2] # toggles = i2
logic.bp_not(outputs[0], inputs[0])
def and_vd3(self, inputs, outputs):
# 8-valued: o[0]: o[1]: o[2]:
# 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N
# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# 1 0 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 1 1 1 1
# - 0 X X X X X X X 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# X 0 X X X X X X X 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# R 0 R X X R R P R 0 0 1 1 0 0 0 0 1 0 1 1 0 0 1 0 0 1 0 0 1 1 1 1
# F 0 F X X R F P F 0 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 0 1 0 0 1 1 1 1
# P 0 P X X P P P P 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1 1 0 1 0 0 1 1 1 1
# N 0 N X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 1 0 0 1 1 1 1
i = inputs[0]
anyi0 = self.tmp[0]
anyf0 = self.tmp[1]
anyd = self.tmp[2]
any0 = self.tmp[3]
any_t = self.tmp[4]
anyd[0] = ~(i[0] ^ i[1]) & ~i[2]
anyi0[0] = ~i[0] & ~anyd[0]
anyf0[0] = i[1] & ~anyd[0]
any_t[0] = i[2]
any0[0] = anyi0[0] & anyf0[0] & ~i[2]
for i in inputs[1:]:
dc = ~(i[0] ^ i[1]) & ~i[2]
anyd[0] |= dc
anyi0[0] |= ~i[0] & ~dc
anyf0[0] |= i[1] & ~dc
any_t[0] |= i[2]
any0[0] |= ~i[0] & ~dc & i[1] & ~i[2]
o = outputs[0]
o[0] = (~anyi0[0] | anyd[0]) & ~any0[0] # initial = no_i0 or DC
o[1] = anyf0[0] | anyd[0] # ~final = ~no_f0 or DC
o[2] = any_t[0] & ~(anyd[0] | any0[0]) # toggle = anyT and noDC and no0
logic.bp_and(outputs[0], *inputs)
def or_vd3(self, inputs, outputs):
# 8-valued: o[0]: o[1]: o[2]:
# 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N
# 0 0 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 1 1 1 1
# 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# - X 1 X X X X X X 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# X X 1 X X X X X X 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# R R 1 X X R N R R 0 1 1 1 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 1 1
# F F 1 X X N F F F 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 0 0 1 1 1 1
# P P 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 1 0 0 0 1 1 1 1
# N N 1 X X R F N N 1 1 1 1 0 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 1 1 1 1
i = inputs[0]
anyi1 = self.tmp[0]
anyf1 = self.tmp[1]
anyd = self.tmp[2]
any1 = self.tmp[3]
any_t = self.tmp[4]
anyd[0] = ~(i[0] ^ i[1]) & ~i[2]
anyi1[0] = i[0] & ~anyd[0]
anyf1[0] = ~i[1] & ~anyd[0]
any_t[0] = i[2]
any1[0] = (anyi1[0] & anyf1[0]) & ~i[2]
for i in inputs[1:]:
dc = ~(i[0] ^ i[1]) & ~i[2]
anyd[0] |= dc
anyi1[0] |= i[0] & ~dc
anyf1[0] |= ~i[1] & ~dc
any_t[0] |= i[2]
any1[0] |= i[0] & ~dc & ~i[1] & ~i[2]
o = outputs[0]
o[0] = anyi1[0] | anyd[0] # initial = i1 or DC
o[1] = (~anyf1[0] | anyd[0]) & ~any1[0] # ~final = f1 or DC
o[2] = any_t[0] & ~(anyd[0] | any1[0]) # toggle = anyT and no(DC or 1)
logic.bp_or(outputs[0], *inputs)
def xor_vd3(self, inputs, outputs):
# 8-valued: o[0]: o[1]: o[2]:
# 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N 0 1 - X R F P N
# 0 0 1 X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 1 1 1 1
# 1 1 0 X X F R N P 1 0 1 1 1 0 1 0 0 1 1 1 1 0 0 1 0 0 0 0 1 1 1 1
# - X X X X X X X X 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# X X X X X X X X X 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
# R R F X X P N R F 0 1 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1
# F F R X X N P F R 1 0 1 1 1 0 1 0 1 0 1 1 0 1 1 0 1 1 0 0 1 1 1 1
# P P N X X R F P N 0 1 1 1 0 1 0 1 1 0 1 1 0 1 1 0 1 1 0 0 1 1 1 1
# N N P X X F R N P 1 0 1 1 1 0 1 0 0 1 1 1 1 0 0 1 1 1 0 0 1 1 1 1
i = inputs[0]
odd0 = self.tmp[0]
odd1 = self.tmp[1]
anyd = self.tmp[2]
anyt = self.tmp[3]
odd0[0] = i[0]
odd1[0] = i[1]
anyd[0] = ~(i[0] ^ i[1]) & ~i[2]
anyt[0] = i[2]
for i in inputs[1:]:
odd0[0] ^= i[0]
odd1[0] ^= i[1]
anyd[0] |= ~(i[0] ^ i[1]) & ~i[2]
anyt[0] |= i[2]
o = outputs[0]
o[0] = odd0[0] | anyd[0]
o[1] = ~odd1[0] | anyd[0]
o[2] = anyt[0] & ~anyd[0]
logic.bp_xor(outputs[0], *inputs)
def sdff_vd3(self, inputs, outputs):
self.dff_vd3(inputs, outputs)
if len(outputs) > 1:
i = inputs[0]
dc = self.tmp[0]
dc[0] = ~(i[0] ^ i[1]) & ~i[2]
outputs[1][0] = ~i[0] | dc[0] # value = 1 or DC
outputs[1][1] = ~i[1] | dc[0] # value = 1 or DC
outputs[1][2] = i[2] # toggle = T
logic.bp_not(outputs[1], inputs[0])
def dff_vd3(self, inputs, outputs):
i = inputs[0]
dc = self.tmp[0]
dc[0] = ~(i[0] ^ i[1]) & ~i[2]
outputs[0][0] = i[0] | dc[0] # value = 1 or DC
outputs[0][1] = i[1] | dc[0] # value = 1 or DC
outputs[0][2] = i[2] # toggle = T
logic.bp_buf(outputs[0], inputs[0])
def nand_vd3(self, inputs, outputs):
self.and_vd3(inputs, outputs)
-300
View File
@@ -1,300 +0,0 @@
import numpy as np
from . import popcount
from .logic import 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
+40 -30
View File
@@ -8,13 +8,13 @@ Call :py:func:`StilFile.tests4v`, :py:func:`StilFile.tests8v`, or :py:func:`Stil
obtain the appropriate vector sets.
"""
from lark import Lark, Transformer
from collections import namedtuple
import re
from collections import namedtuple
from .packed_vectors import PackedVectors
from lark import Lark, Transformer
from . import readtext, logic
from .logic_sim import LogicSim
from . import readtext
Call = namedtuple('Call', ['name', 'parameters'])
@@ -86,59 +86,69 @@ class StilFile:
scan_inversions[chain[-1]] = scan_out_inversion
return interface, pi_map, po_map, scan_maps, scan_inversions
def tests4v(self, circuit):
"""Assembles and returns a scan test pattern set in 4-valued logic for given circuit.
def tests(self, circuit):
"""Assembles and returns a scan test pattern set for given circuit.
This function assumes a static (stuck-at fault) test.
"""
interface, pi_map, po_map, scan_maps, scan_inversions = self._maps(circuit)
tests = PackedVectors(len(self.patterns), len(interface), 2)
tests = logic.MVArray((len(interface), len(self.patterns)))
for i, p in enumerate(self.patterns):
for si_port in self.si_ports.keys():
tests.set_values(i, p.load[si_port], scan_maps[si_port], scan_inversions[si_port])
tests.set_values(i, p.launch['_pi'], pi_map)
pattern = logic.mv_xor(p.load[si_port], scan_inversions[si_port])
tests.data[scan_maps[si_port], i] = pattern.data[:, 0]
tests.data[pi_map, i] = logic.MVArray(p.launch['_pi']).data[:, 0]
return tests
def tests8v(self, circuit):
"""Assembles and returns a scan test pattern set in 8-valued logic for given circuit.
def tests_loc(self, circuit):
"""Assembles and returns a LoC scan test pattern set for given circuit.
This function assumes a launch-on-capture (LoC) delay test.
It performs a logic simulation to obtain the first capture pattern (the one that launches the
delay test) and assembles the test pattern set from from pairs for initialization- and launch-patterns.
"""
interface, pi_map, po_map, scan_maps, scan_inversions = self._maps(circuit)
init = PackedVectors(len(self.patterns), len(interface), 2)
init = logic.MVArray((len(interface), len(self.patterns)), m=4)
# init = PackedVectors(len(self.patterns), len(interface), 2)
for i, p in enumerate(self.patterns):
# init.set_values(i, '0' * len(interface))
for si_port in self.si_ports.keys():
init.set_values(i, p.load[si_port], scan_maps[si_port], scan_inversions[si_port])
init.set_values(i, p.launch['_pi'], pi_map)
sim4v = LogicSim(circuit, len(init), 2)
sim4v.assign(init)
pattern = logic.mv_xor(p.load[si_port], scan_inversions[si_port])
init.data[scan_maps[si_port], i] = pattern.data[:, 0]
init.data[pi_map, i] = logic.MVArray(p.launch['_pi']).data[:, 0]
launch_bp = logic.BPArray(init)
sim4v = LogicSim(circuit, len(init), m=4)
sim4v.assign(launch_bp)
sim4v.propagate()
launch = init.copy()
sim4v.capture(launch)
sim4v.capture(launch_bp)
launch = logic.MVArray(launch_bp)
for i, p in enumerate(self.patterns):
# if there was no launch clock, then init = launch
if ('P' not in p.launch['_pi']) or ('P' not in p.capture['_pi']):
for si_port in self.si_ports.keys():
launch.set_values(i, p.load[si_port], scan_maps[si_port], scan_inversions[si_port])
pattern = logic.mv_xor(p.load[si_port], scan_inversions[si_port])
launch.data[scan_maps[si_port], i] = pattern.data[:, 0]
if '_pi' in p.capture and 'P' in p.capture['_pi']:
launch.set_values(i, p.capture['_pi'], pi_map)
return PackedVectors.from_pair(init, launch)
launch.data[pi_map, i] = logic.MVArray(p.capture['_pi']).data[:, 0]
launch.data[po_map, i] = logic.UNASSIGNED
return logic.mv_transition(init, launch)
def responses4v(self, circuit):
"""Assembles and returns a scan test response pattern set in 4-valued logic for given circuit."""
def responses(self, circuit):
"""Assembles and returns a scan test response pattern set for given circuit."""
interface, pi_map, po_map, scan_maps, scan_inversions = self._maps(circuit)
resp = PackedVectors(len(self.patterns), len(interface), 2)
resp = logic.MVArray((len(interface), len(self.patterns)))
# resp = PackedVectors(len(self.patterns), len(interface), 2)
for i, p in enumerate(self.patterns):
if len(p.capture) > 0:
resp.set_values(i, p.capture['_po'], po_map)
else:
resp.set_values(i, p.launch['_po'], po_map)
resp.data[po_map, i] = logic.MVArray(p.capture['_po'] if len(p.capture) > 0 else p.launch['_po']).data[:, 0]
# if len(p.capture) > 0:
# resp.set_values(i, p.capture['_po'], po_map)
# else:
# resp.set_values(i, p.launch['_po'], po_map)
for so_port in self.so_ports.keys():
resp.set_values(i, p.unload[so_port], scan_maps[so_port], scan_inversions[so_port])
pattern = logic.mv_xor(p.unload[so_port], scan_inversions[so_port])
resp.data[scan_maps[so_port], i] = pattern.data[:, 0]
# resp.set_values(i, p.unload[so_port], scan_maps[so_port], scan_inversions[so_port])
return resp
+19 -15
View File
@@ -260,21 +260,26 @@ class WaveSim:
self.timing[line, 0, polarity] = delay
def assign(self, vectors, time=0.0, offset=0):
nvectors = min(vectors.nvectors - offset, self.sims)
nvectors = min(len(vectors) - offset, self.sims)
for i, node in enumerate(self.interface):
ppi_loc = self.sat[self.ppi_offset + i, 0]
if ppi_loc < 0: continue
for p in range(nvectors):
vector = p + offset
a = vectors.bits[i, :, vector // 8]
a = vectors.data[i, :, vector // 8]
m = self.mask[vector % 8]
toggle = 0
if a[0] & m[1]:
self.state[ppi_loc, p] = TMIN
toggle += 1
if (len(a) > 2) and (a[2] & m[1]) and ((a[0] & m[1]) == (a[1] & m[1])):
self.state[ppi_loc + toggle, p] = time
toggle += 1
if len(a) <= 2:
if a[0] & m[1]:
self.state[ppi_loc, p] = TMIN
toggle += 1
else:
if a[1] & m[1]:
self.state[ppi_loc, p] = TMIN
toggle += 1
if (a[2] & m[1]) and ((a[0] & m[1]) != (a[1] & m[1])):
self.state[ppi_loc + toggle, p] = time
toggle += 1
self.state[ppi_loc + toggle, p] = TMAX
def propagate(self, sims=None, sd=0.0, seed=1):
@@ -563,12 +568,11 @@ class WaveSimCuda(WaveSim):
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])
assert byte_offset < vectors.data.shape[-1]
pdim = min(vectors.data.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[..., 0:pdim] = vectors.data[..., byte_offset:pdim + byte_offset]
if vectors.m == 2:
self.tdata[:, 2, 0:pdim] = 0
cuda.to_device(self.tdata, to=self.d_tdata)
@@ -745,10 +749,10 @@ def assign_kernel(state, sat, ppi_offset, intf_len, tdata, time):
a2 = tdata[y, 2, vector // 8]
m = np.uint8(1 << (7 - (vector % 8)))
toggle = 0
if a0 & m:
if a1 & m:
state[line + toggle, x] = TMIN
toggle += 1
if (a2 & m) and ((a0 & m) == (a1 & m)):
if (a2 & m) and ((a0 & m) != (a1 & m)):
state[line + toggle, x] = time
toggle += 1
state[line + toggle, x] = TMAX