Browse Source
- Circuit: is now pickleable and comparable - Circuit: utilities for locating/indexing io-ports - Verilog: parser fixes, support yosys-style verilog - SDF: parser fixes, full XOR support - STIL: parser fixes - Simulators: faster, up to 4-input cells, pickleable - WaveSim: WSA calculation support - WaveSim: Per-simulation parameters and delays - Logic: Data are now raw numpy arrays - Logic: More tools for bit-packing - Added DEF parser - Better techlib support for NanGate, SAED, GSC180 - Tests and docs improvementsmain
Stefan Holst
1 year ago
49 changed files with 7176 additions and 3873 deletions
@ -1,10 +1,12 @@
@@ -1,10 +1,12 @@
|
||||
**/__pycache__ |
||||
**/.ipynb_checkpoints |
||||
**/.pytest_cache |
||||
**/.DS_Store |
||||
**/*.pyc |
||||
__pycache__ |
||||
.ipynb_checkpoints |
||||
.pytest_cache |
||||
.DS_Store |
||||
*.pyc |
||||
docs/_build |
||||
build |
||||
dist |
||||
.idea |
||||
.vscode |
||||
src/kyupy.egg-info |
||||
*nogit* |
||||
|
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
Circuit Graph - :mod:`kyupy.circuit` |
||||
==================================== |
||||
|
||||
.. automodule:: kyupy.circuit |
||||
|
||||
.. autoclass:: kyupy.circuit.Node |
||||
:members: |
||||
|
||||
.. autoclass:: kyupy.circuit.Line |
||||
:members: |
||||
|
||||
.. autoclass:: kyupy.circuit.Circuit |
||||
:members: |
@ -1,29 +0,0 @@
@@ -1,29 +0,0 @@
|
||||
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,7 @@
@@ -0,0 +1,7 @@
|
||||
Multi-Valued Logic - :mod:`kyupy.logic` |
||||
======================================= |
||||
|
||||
.. automodule:: kyupy.logic |
||||
:members: |
||||
|
||||
|
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
Technology Libraries |
||||
==================== |
||||
|
||||
.. automodule:: kyupy.techlib |
||||
:members: |
||||
|
||||
|
@ -0,0 +1,297 @@
@@ -0,0 +1,297 @@
|
||||
"""A simple and incomplete parser for the Design Exchange Format (DEF). |
||||
|
||||
This parser extracts information on components and nets from DEF files and make them available |
||||
as an intermediate representation (:class:`DefFile` object). |
||||
""" |
||||
|
||||
from collections import defaultdict |
||||
|
||||
from lark import Lark, Transformer, Tree |
||||
|
||||
from kyupy import readtext |
||||
|
||||
|
||||
class DefNet: |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.pins = [] |
||||
|
||||
@property |
||||
def wires(self): |
||||
ww = defaultdict(list) |
||||
[ww[dw.layer].append((int(dw.width), dw.wire_points)) for dw in self.routed if len(dw.wire_points) > 0] |
||||
return ww |
||||
|
||||
@property |
||||
def vias(self): |
||||
vv = defaultdict(list) |
||||
[vv[vtype].extend(locs) for dw in self.routed for vtype, locs in dw.vias.items()] |
||||
return vv |
||||
|
||||
|
||||
class DefWire: |
||||
def __init__(self): |
||||
self.layer = None |
||||
self.width = None |
||||
self.points = [] |
||||
|
||||
@property |
||||
def wire_points(self): |
||||
start = [self.points[0]] |
||||
rest = [p for p in self.points[1:] if not isinstance(p[0], str)] # skip over vias |
||||
return start + rest if len(rest) > 0 else [] |
||||
|
||||
@property |
||||
def vias(self): |
||||
vv = defaultdict(list) |
||||
loc = self.points[0] |
||||
for p in self.points[1:]: |
||||
if not isinstance(p[0], str): # new location |
||||
loc = (loc[0] if p[0] is None else p[0], loc[1] if p[1] is None else p[1]) # if None, keep previous value |
||||
continue |
||||
vtype, param = p |
||||
if isinstance(param, tuple): # expand "DO x BY y STEP xs ys" |
||||
x_cnt, y_cnt, x_sp, y_sp = param |
||||
[vv[vtype].append((loc[0] + x*x_sp, loc[1] + y*y_sp, 'N')) for x in range(x_cnt) for y in range(y_cnt)] |
||||
else: |
||||
vv[vtype].append((loc[0], loc[1], param or 'N')) |
||||
return vv |
||||
|
||||
def __repr__(self): |
||||
return f'<DefWire {self.layer} {self.width} {self.points}>' |
||||
|
||||
|
||||
class DefVia: |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.rowcol = [1, 1] |
||||
self.cutspacing = [0, 0] |
||||
|
||||
|
||||
class DefPin: |
||||
def __init__(self, name): |
||||
self.name = name |
||||
self.points = [] |
||||
|
||||
|
||||
class DefFile: |
||||
"""Intermediate representation of a DEF file.""" |
||||
def __init__(self): |
||||
self.rows = [] |
||||
self.tracks = [] |
||||
self.units = [] |
||||
self.vias = {} |
||||
self.components = {} |
||||
self.pins = {} |
||||
self.specialnets = {} |
||||
self.nets = {} |
||||
|
||||
|
||||
class DefTransformer(Transformer): |
||||
def __init__(self): self.def_file = DefFile() |
||||
def start(self, args): return self.def_file |
||||
def design(self, args): self.def_file.design = args[0].value |
||||
def point(self, args): return tuple(int(arg.value) if arg != '*' else None for arg in args) |
||||
def do_step(self, args): return tuple(map(int, args)) |
||||
def spnet_wires(self, args): return args[0].lower(), args[1:] |
||||
def net_wires(self, args): return args[0].lower(), args[1:] |
||||
def sppoints(self, args): return args |
||||
def points(self, args): return args |
||||
def net_pin(self, args): return '__pin__', (args[0].value, args[1].value) |
||||
def net_opt(self, args): return args[0].lower(), args[1].value |
||||
|
||||
def file_stmt(self, args): |
||||
value = args[1].value |
||||
value = value[1:-1] if value[0] == '"' else value |
||||
setattr(self.def_file, args[0].lower(), value) |
||||
|
||||
def design_stmt(self, args): |
||||
stmt = args[0].lower() |
||||
if stmt == 'units': self.def_file.units.append((args[1].value, args[2].value, int(args[3]))) |
||||
elif stmt == 'diearea': self.def_file.diearea = args[1:] |
||||
elif stmt == 'row': |
||||
self.def_file.rows.append((args[1].value, # rowName |
||||
args[2].value, # siteName |
||||
(int(args[3]), int(args[4])), # origin x/y |
||||
args[5].value, # orientation |
||||
max(args[6][0], args[6][1]), # number of sites |
||||
max(args[6][2], args[6][3]) # site width |
||||
)) |
||||
elif stmt == 'tracks': |
||||
self.def_file.tracks.append((args[1].value, # orientation |
||||
int(args[2]), # start |
||||
int(args[3]), # number of tracks |
||||
int(args[4]), # spacing |
||||
args[5].value # layer |
||||
)) |
||||
|
||||
def vias_stmt(self, args): |
||||
via = DefVia(args[0].value) |
||||
[setattr(via, opt, val) for opt, val in args[1:]] |
||||
self.def_file.vias[via.name] = via |
||||
|
||||
def vias_opt(self, args): |
||||
opt = args[0].lower() |
||||
if opt in ['viarule', 'pattern']: val = args[1].value |
||||
elif opt in ['layers']: val = [arg.value for arg in args[1:]] |
||||
else: val = [int(arg) for arg in args[1:]] |
||||
return opt, val |
||||
|
||||
def comp_stmt(self, args): |
||||
name = args[0].value |
||||
kind = args[1].value |
||||
point = args[2] |
||||
orientation = args[3].value |
||||
self.def_file.components[name] = (kind, point, orientation) |
||||
|
||||
def pins_stmt(self, args): |
||||
pin = DefPin(args[0].value) |
||||
[pin.points.append(val) if opt == 'placed' else setattr(pin, opt, val) for opt, val in args[1:]] |
||||
self.def_file.pins[pin.name] = pin |
||||
|
||||
def pins_opt(self, args): |
||||
opt = args[0].lower() |
||||
if opt in ['net', 'direction', 'use']: val = args[1].value |
||||
elif opt in ['layer']: val = [args[1].value] + args[2:] |
||||
elif opt in ['placed']: val = (args[1][0], args[1][1], args[2].value) |
||||
else: val = [] |
||||
return opt, val |
||||
|
||||
def spnets_stmt(self, args): |
||||
dnet = DefNet(args[0].value) |
||||
for arg in args[1:]: |
||||
if arg[0] == '__pin__': dnet.pins.append(arg[1]) |
||||
else: setattr(dnet, arg[0], arg[1]) |
||||
self.def_file.specialnets[dnet.name] = dnet |
||||
|
||||
def nets_stmt(self, args): |
||||
dnet = DefNet(args[0].value) |
||||
for arg in args[1:]: |
||||
if arg[0] == '__pin__': dnet.pins.append(arg[1]) |
||||
else: setattr(dnet, arg[0], arg[1]) |
||||
self.def_file.nets[dnet.name] = dnet |
||||
|
||||
def spwire(self, args): |
||||
wire = DefWire() |
||||
wire.layer = args[0].value |
||||
wire.width = args[1].value |
||||
wire.points = args[-1] |
||||
return wire |
||||
|
||||
def wire(self, args): |
||||
wire = DefWire() |
||||
wire.layer = args[0].value |
||||
wire.points = args[-1] |
||||
return wire |
||||
|
||||
def sppoints_via(self, args): |
||||
if len(args) == 1: return args[0].value, None |
||||
else: return args[0].value, args[1] |
||||
|
||||
def points_via(self, args): |
||||
if len(args) == 1: return args[0].value, 'N' |
||||
else: return args[0].value, args[1].value.strip() |
||||
|
||||
|
||||
GRAMMAR = r""" |
||||
start: /#[^\n]*/? file_stmt* |
||||
|
||||
?file_stmt: /VERSION/ ID ";" |
||||
| /DIVIDERCHAR/ STRING ";" |
||||
| /BUSBITCHARS/ STRING ";" |
||||
| design |
||||
|
||||
design: "DESIGN" ID ";" design_stmt* "END" "DESIGN" |
||||
|
||||
?design_stmt: /UNITS/ ID ID NUMBER ";" |
||||
| /DIEAREA/ point+ ";" |
||||
| /ROW/ ID ID NUMBER NUMBER ID do_step ";" |
||||
| /TRACKS/ /[XY]/ NUMBER "DO" NUMBER "STEP" NUMBER "LAYER" ID ";" |
||||
| propdef | vias | nondef | comp | pins | pinprop | spnets | nets |
||||
|
||||
propdef: "PROPERTYDEFINITIONS" propdef_stmt* "END" "PROPERTYDEFINITIONS" |
||||
propdef_stmt: /COMPONENTPIN/ ID ID ";" |
||||
|
||||
vias: "VIAS" NUMBER ";" vias_stmt* "END" "VIAS" |
||||
vias_stmt: "-" ID vias_opt* ";" |
||||
vias_opt: "+" /VIARULE/ ID |
||||
| "+" /CUTSIZE/ NUMBER NUMBER |
||||
| "+" /LAYERS/ ID ID ID |
||||
| "+" /CUTSPACING/ NUMBER NUMBER |
||||
| "+" /ENCLOSURE/ NUMBER NUMBER NUMBER NUMBER |
||||
| "+" /ROWCOL/ NUMBER NUMBER |
||||
| "+" /PATTERN/ ID |
||||
|
||||
nondef: "NONDEFAULTRULES" NUMBER ";" nondef_stmt+ "END" "NONDEFAULTRULES" |
||||
nondef_stmt: "-" ID ( "+" /HARDSPACING/ |
||||
| "+" /LAYER/ ID "WIDTH" NUMBER "SPACING" NUMBER |
||||
| "+" /VIA/ ID )* ";" |
||||
|
||||
comp: "COMPONENTS" NUMBER ";" comp_stmt* "END" "COMPONENTS" |
||||
comp_stmt: "-" ID ID "+" "PLACED" point ID ";" |
||||
|
||||
pins: "PINS" NUMBER ";" pins_stmt* "END" "PINS" |
||||
pins_stmt: "-" ID pins_opt* ";" |
||||
pins_opt: "+" /NET/ ID |
||||
| "+" /SPECIAL/ |
||||
| "+" /DIRECTION/ ID |
||||
| "+" /USE/ ID |
||||
| "+" /PORT/ |
||||
| "+" /LAYER/ ID point point |
||||
| "+" /PLACED/ point ID |
||||
|
||||
pinprop: "PINPROPERTIES" NUMBER ";" pinprop_stmt* "END" "PINPROPERTIES" |
||||
pinprop_stmt: "-" "PIN" ID "+" "PROPERTY" ID STRING ";" |
||||
|
||||
spnets: "SPECIALNETS" NUMBER ";" spnets_stmt* "END" "SPECIALNETS" |
||||
spnets_stmt: "-" ID ( net_pin | net_opt | spnet_wires )* ";" |
||||
|
||||
spnet_wires: "+" ( /COVER/ | /FIXED/ | /ROUTED/ ) spwire ( "NEW" spwire )* |
||||
|
||||
spwire: ID NUMBER spwire_opt* sppoints |
||||
spwire_opt: "+" /SHAPE/ ID |
||||
| "+" /STYLE/ ID |
||||
|
||||
sppoints: point ( point | sppoints_via )+ |
||||
sppoints_via: ID do_step? |
||||
|
||||
nets: "NETS" NUMBER ";" nets_stmt* "END" "NETS" |
||||
nets_stmt: "-" ID ( net_pin | net_opt | net_wires )* ";" |
||||
|
||||
net_pin: "(" ID ID ")" |
||||
net_opt: "+" /USE/ ID |
||||
| "+" /NONDEFAULTRULE/ ID |
||||
net_wires: "+" ( /COVER/ | /FIXED/ | /ROUTED/ | /NOSHIELD/ ) wire ( "NEW" wire )* |
||||
|
||||
wire: ID wire_opt points |
||||
wire_opt: ( "TAPER" | "TAPERRULE" ID )? ("STYLE" ID)? |
||||
|
||||
points: point ( point | points_via )+ |
||||
points_via: ID ORIENTATION? |
||||
|
||||
point: "(" (NUMBER|/\*/) (NUMBER|/\*/) NUMBER? ")" |
||||
|
||||
do_step: "DO" NUMBER "BY" NUMBER "STEP" (NUMBER|SIGNED_NUMBER) (NUMBER|SIGNED_NUMBER) |
||||
|
||||
ORIENTATION.2: /F?[NWES]/ WS |
||||
ID: /[^ \t\f\r\n+][^ \t\f\r\n]*/ |
||||
STRING : "\"" /.*?/s /(?<!\\)(\\\\)*?/ "\"" |
||||
WS: /[ \t\f\r\n]/ |
||||
|
||||
%import common.NUMBER |
||||
%import common.SIGNED_NUMBER |
||||
%ignore WS (/#[^\n]*/)? |
||||
""" |
||||
|
||||
|
||||
def parse(text): |
||||
"""Parses the given ``text`` and returns a :class:`DefFile` object.""" |
||||
return Lark(GRAMMAR, parser="lalr", transformer=DefTransformer()).parse(text) |
||||
|
||||
|
||||
def load(file): |
||||
"""Parses the contents of ``file`` and returns a :class:`DefFile` object. |
||||
|
||||
Files with `.gz`-suffix are decompressed on-the-fly. |
||||
""" |
||||
return parse(readtext(file)) |
@ -0,0 +1,333 @@
@@ -0,0 +1,333 @@
|
||||
|
||||
from collections import defaultdict |
||||
from bisect import bisect, insort_left |
||||
|
||||
import numpy as np |
||||
|
||||
BUF1 = np.uint16(0b1010_1010_1010_1010) |
||||
INV1 = ~BUF1 |
||||
|
||||
AND2 = np.uint16(0b1000_1000_1000_1000) |
||||
AND3 = np.uint16(0b1000_0000_1000_0000) |
||||
AND4 = np.uint16(0b1000_0000_0000_0000) |
||||
|
||||
NAND2, NAND3, NAND4 = ~AND2, ~AND3, ~AND4 |
||||
|
||||
OR2 = np.uint16(0b1110_1110_1110_1110) |
||||
OR3 = np.uint16(0b1111_1110_1111_1110) |
||||
OR4 = np.uint16(0b1111_1111_1111_1110) |
||||
|
||||
NOR2, NOR3, NOR4 = ~OR2, ~OR3, ~OR4 |
||||
|
||||
XOR2 = np.uint16(0b0110_0110_0110_0110) |
||||
XOR3 = np.uint16(0b1001_0110_1001_0110) |
||||
XOR4 = np.uint16(0b0110_1001_1001_0110) |
||||
|
||||
XNOR2, XNOR3, XNOR4 = ~XOR2, ~XOR3, ~XOR4 |
||||
|
||||
AO21 = np.uint16(0b1111_1000_1111_1000) # (i0 & i1) | i2 |
||||
AO22 = np.uint16(0b1111_1000_1000_1000) # (i0 & i1) | (i2 & i3) |
||||
OA21 = np.uint16(0b1110_0000_1110_0000) # (i0 | i1) & i2 |
||||
OA22 = np.uint16(0b1110_1110_1110_0000) # (i0 | i1) & (i2 | i3) |
||||
|
||||
AOI21, AOI22, OAI21, OAI22 = ~AO21, ~AO22, ~OA21, ~OA22 |
||||
|
||||
AO211 = np.uint16(0b1111_1111_1111_1000) # (i0 & i1) | i2 | i3 |
||||
OA211 = np.uint16(0b1110_0000_0000_0000) # (i0 | i1) & i2 & i3 |
||||
|
||||
AOI211, OAI211 = ~AO211, ~OA211 |
||||
|
||||
MUX21 = np.uint16(0b1100_1010_1100_1010) # z = i1 if i2 else i0 (i2 is select) |
||||
|
||||
names = dict([(v, k) for k, v in globals().items() if isinstance(v, np.uint16)]) |
||||
|
||||
kind_prefixes = { |
||||
'nand': (NAND4, NAND3, NAND2), |
||||
'nor': (NOR4, NOR3, NOR2), |
||||
'and': (AND4, AND3, AND2), |
||||
'or': (OR4, OR3, OR2), |
||||
'isolor': (OR2, OR2, OR2), |
||||
'xor': (XOR4, XOR3, XOR2), |
||||
'xnor': (XNOR4, XNOR3, XNOR2), |
||||
|
||||
'not': (INV1, INV1, INV1), |
||||
'inv': (INV1, INV1, INV1), |
||||
'ibuf': (INV1, INV1, INV1), |
||||
'__const1__': (INV1, INV1, INV1), |
||||
'tieh': (INV1, INV1, INV1), |
||||
|
||||
'buf': (BUF1, BUF1, BUF1), |
||||
'nbuf': (BUF1, BUF1, BUF1), |
||||
'delln': (BUF1, BUF1, BUF1), |
||||
'__const0__': (BUF1, BUF1, BUF1), |
||||
'tiel': (BUF1, BUF1, BUF1), |
||||
|
||||
'ao211': (AO211, AO211, AO211), |
||||
'oa211': (OA211, OA211, OA211), |
||||
'aoi211': (AOI211, AOI211, AOI211), |
||||
'oai211': (OAI211, OAI211, OAI211), |
||||
|
||||
'ao22': (AO22, AO22, AO22), |
||||
'aoi22': (AOI22, AOI22, AOI22), |
||||
'ao21': (AO21, AO21, AO21), |
||||
'aoi21': (AOI21, AOI21, AOI21), |
||||
|
||||
'oa22': (OA22, OA22, OA22), |
||||
'oai22': (OAI22, OAI22, OAI22), |
||||
'oa21': (OA21, OA21, OA21), |
||||
'oai21': (OAI21, OAI21, OAI21), |
||||
|
||||
'mux21': (MUX21, MUX21, MUX21), |
||||
} |
||||
|
||||
class Heap: |
||||
def __init__(self): |
||||
self.chunks = dict() # map start location to chunk size |
||||
self.released = list() # chunks that were released |
||||
self.current_size = 0 |
||||
self.max_size = 0 |
||||
|
||||
def alloc(self, size): |
||||
for idx, loc in enumerate(self.released): |
||||
if self.chunks[loc] == size: |
||||
del self.released[idx] |
||||
return loc |
||||
if self.chunks[loc] > size: # split chunk |
||||
chunksize = self.chunks[loc] |
||||
self.chunks[loc] = size |
||||
self.chunks[loc + size] = chunksize - size |
||||
self.released[idx] = loc + size # move released pointer: loc -> loc+size |
||||
return loc |
||||
# no previously released chunk; make new one |
||||
loc = self.current_size |
||||
self.chunks[loc] = size |
||||
self.current_size += size |
||||
self.max_size = max(self.max_size, self.current_size) |
||||
return loc |
||||
|
||||
def free(self, loc): |
||||
size = self.chunks[loc] |
||||
if loc + size == self.current_size: # end of managed area, remove chunk |
||||
del self.chunks[loc] |
||||
self.current_size -= size |
||||
# check and remove prev chunk if free |
||||
if len(self.released) > 0: |
||||
prev = self.released[-1] |
||||
if prev + self.chunks[prev] == self.current_size: |
||||
chunksize = self.chunks[prev] |
||||
del self.chunks[prev] |
||||
del self.released[-1] |
||||
self.current_size -= chunksize |
||||
return |
||||
released_idx = bisect(self.released, loc) |
||||
if released_idx < len(self.released) and loc + size == self.released[released_idx]: # next chunk is free, merge |
||||
chunksize = size + self.chunks[loc + size] |
||||
del self.chunks[loc + size] |
||||
self.chunks[loc] = chunksize |
||||
size = self.chunks[loc] |
||||
self.released[released_idx] = loc |
||||
else: |
||||
insort_left(self.released, loc) # put in a new release |
||||
if released_idx > 0: # check if previous chunk is free |
||||
prev = self.released[released_idx - 1] |
||||
if prev + self.chunks[prev] == loc: # previous chunk is adjacent to freed one, merge |
||||
chunksize = size + self.chunks[prev] |
||||
del self.chunks[loc] |
||||
self.chunks[prev] = chunksize |
||||
del self.released[released_idx] |
||||
|
||||
def __repr__(self): |
||||
r = [] |
||||
for loc in sorted(self.chunks.keys()): |
||||
size = self.chunks[loc] |
||||
released_idx = bisect(self.released, loc) |
||||
is_released = released_idx > 0 and len(self.released) > 0 and self.released[released_idx - 1] == loc |
||||
r.append(f'{loc:5d}: {"free" if is_released else "used"} {size}') |
||||
return "\n".join(r) |
||||
|
||||
|
||||
class SimOps: |
||||
"""A static scheduler that translates a Circuit into a topologically sorted list of basic logic operations (self.ops) and |
||||
a memory mapping (self.c_locs, self.c_caps) for use in simulators. |
||||
|
||||
:param circuit: The circuit to create a schedule for. |
||||
:param strip_forks: If enabled, the scheduler will not include fork nodes to safe simulation time. |
||||
Stripping forks will cause interconnect delay annotations of lines read by fork nodes to be ignored. |
||||
:param c_reuse: If enabled, memory of intermediate signal waveforms will be re-used. This greatly reduces |
||||
memory footprint, but intermediate signal waveforms become unaccessible after a propagation. |
||||
""" |
||||
def __init__(self, circuit, c_caps=1, c_caps_min=1, a_ctrl=None, c_reuse=False, strip_forks=False): |
||||
self.circuit = circuit |
||||
self.s_len = len(circuit.s_nodes) |
||||
|
||||
if isinstance(c_caps, int): |
||||
c_caps = [c_caps] * (len(circuit.lines)+3) |
||||
|
||||
if a_ctrl is None: |
||||
a_ctrl = np.zeros((len(circuit.lines)+3, 3), dtype=np.int32) # add 3 for zero, tmp, tmp2 |
||||
a_ctrl[:,0] = -1 |
||||
|
||||
# special locations and offsets in c_locs/c_caps |
||||
self.zero_idx = len(circuit.lines) |
||||
self.tmp_idx = self.zero_idx + 1 |
||||
self.tmp2_idx = self.tmp_idx + 1 |
||||
self.ppi_offset = self.tmp2_idx + 1 |
||||
self.ppo_offset = self.ppi_offset + self.s_len |
||||
self.c_locs_len = self.ppo_offset + self.s_len |
||||
|
||||
# translate circuit structure into self.ops |
||||
ops = [] |
||||
interface_dict = dict((n, i) for i, n in enumerate(circuit.s_nodes)) |
||||
for n in circuit.topological_order(): |
||||
if n in interface_dict: |
||||
inp_idx = self.ppi_offset + interface_dict[n] |
||||
if len(n.outs) > 0 and n.outs[0] is not None: # first output of a PI/PPI |
||||
ops.append((BUF1, n.outs[0].index, inp_idx, self.zero_idx, self.zero_idx, self.zero_idx, *a_ctrl[n.outs[0]])) |
||||
if 'dff' in n.kind.lower(): # second output of DFF is inverted |
||||
if len(n.outs) > 1 and n.outs[1] is not None: |
||||
ops.append((INV1, n.outs[1].index, inp_idx, self.zero_idx, self.zero_idx, self.zero_idx, *a_ctrl[n.outs[1]])) |
||||
else: # if not DFF, no output is inverted. |
||||
for o_line in n.outs[1:]: |
||||
if o_line is not None: |
||||
ops.append((BUF1, o_line.index, inp_idx, self.zero_idx, self.zero_idx, self.zero_idx, *a_ctrl[o_line])) |
||||
continue |
||||
# regular node, not PI/PPI or PO/PPO |
||||
o0_idx = n.outs[0].index if len(n.outs) > 0 and n.outs[0] is not None else self.tmp_idx |
||||
i0_idx = n.ins[0].index if len(n.ins) > 0 and n.ins[0] is not None else self.zero_idx |
||||
i1_idx = n.ins[1].index if len(n.ins) > 1 and n.ins[1] is not None else self.zero_idx |
||||
i2_idx = n.ins[2].index if len(n.ins) > 2 and n.ins[2] is not None else self.zero_idx |
||||
i3_idx = n.ins[3].index if len(n.ins) > 3 and n.ins[3] is not None else self.zero_idx |
||||
kind = n.kind.lower() |
||||
if kind == '__fork__': |
||||
if not strip_forks: |
||||
for o_line in n.outs: |
||||
if o_line is not None: |
||||
ops.append((BUF1, o_line.index, i0_idx, i1_idx, i2_idx, i3_idx, *a_ctrl[o_line])) |
||||
continue |
||||
sp = None |
||||
for prefix, prims in kind_prefixes.items(): |
||||
if kind.startswith(prefix): |
||||
sp = prims[0] |
||||
if i3_idx == self.zero_idx: |
||||
sp = prims[1] |
||||
if i2_idx == self.zero_idx: |
||||
sp = prims[2] |
||||
break |
||||
if sp is None: |
||||
print('unknown cell type', kind) |
||||
else: |
||||
ops.append((sp, o0_idx, i0_idx, i1_idx, i2_idx, i3_idx, *a_ctrl[o0_idx])) |
||||
|
||||
self.ops = np.asarray(ops, dtype='int32') |
||||
|
||||
# create a map from fanout lines to stem lines for fork stripping |
||||
stems = np.zeros(self.c_locs_len, dtype='int32') - 1 # default to -1: 'no fanout line' |
||||
if strip_forks: |
||||
for f in circuit.forks.values(): |
||||
prev_line = f.ins[0] |
||||
while prev_line.driver.kind == '__fork__': |
||||
prev_line = prev_line.driver.ins[0] |
||||
stem_idx = prev_line.index |
||||
for ol in f.outs: |
||||
if ol is not None: |
||||
stems[ol] = stem_idx |
||||
|
||||
# calculate level (distance from PI/PPI) and reference count for each line |
||||
levels = np.zeros(self.c_locs_len, dtype='int32') |
||||
ref_count = np.zeros(self.c_locs_len, dtype='int32') |
||||
level_starts = [0] |
||||
current_level = 1 |
||||
for i, op in enumerate(self.ops): |
||||
# if we fork-strip, always take the stems for determining fan-in level |
||||
i0_idx = stems[op[2]] if stems[op[2]] >= 0 else op[2] |
||||
i1_idx = stems[op[3]] if stems[op[3]] >= 0 else op[3] |
||||
i2_idx = stems[op[4]] if stems[op[4]] >= 0 else op[4] |
||||
i3_idx = stems[op[5]] if stems[op[5]] >= 0 else op[5] |
||||
if levels[i0_idx] >= current_level or levels[i1_idx] >= current_level or levels[i2_idx] >= current_level or levels[i3_idx] >= current_level: |
||||
current_level += 1 |
||||
level_starts.append(i) |
||||
levels[op[1]] = current_level # set level of the output line |
||||
ref_count[i0_idx] += 1 |
||||
ref_count[i1_idx] += 1 |
||||
ref_count[i2_idx] += 1 |
||||
ref_count[i3_idx] += 1 |
||||
self.level_starts = np.asarray(level_starts, dtype='int32') |
||||
self.level_stops = np.asarray(level_starts[1:] + [len(self.ops)], dtype='int32') |
||||
|
||||
# combinational signal allocation table. maps line and interface indices to self.c memory locations |
||||
self.c_locs = np.full((self.c_locs_len,), -1, dtype=np.int32) |
||||
self.c_caps = np.zeros((self.c_locs_len,), dtype=np.int32) |
||||
|
||||
h = Heap() |
||||
|
||||
# allocate and keep memory for special fields |
||||
self.c_locs[self.zero_idx], self.c_caps[self.zero_idx] = h.alloc(c_caps_min), c_caps_min |
||||
self.c_locs[self.tmp_idx], self.c_caps[self.tmp_idx] = h.alloc(c_caps_min), c_caps_min |
||||
self.c_locs[self.tmp2_idx], self.c_caps[self.tmp2_idx] = h.alloc(c_caps_min), c_caps_min |
||||
ref_count[self.zero_idx] += 1 |
||||
ref_count[self.tmp_idx] += 1 |
||||
ref_count[self.tmp2_idx] += 1 |
||||
|
||||
# allocate and keep memory for PI/PPI, keep memory for PO/PPO (allocated later) |
||||
for i, n in enumerate(circuit.s_nodes): |
||||
if len(n.outs) > 0: |
||||
self.c_locs[self.ppi_offset + i], self.c_caps[self.ppi_offset + i] = h.alloc(c_caps_min), c_caps_min |
||||
ref_count[self.ppi_offset + i] += 1 |
||||
if len(n.ins) > 0: |
||||
i0_idx = stems[n.ins[0]] if stems[n.ins[0]] >= 0 else n.ins[0] |
||||
ref_count[i0_idx] += 1 |
||||
|
||||
# allocate memory for the rest of the circuit |
||||
for op_start, op_stop in zip(self.level_starts, self.level_stops): |
||||
free_set = set() |
||||
for op in self.ops[op_start:op_stop]: |
||||
# if we fork-strip, always take the stems |
||||
i0_idx = stems[op[2]] if stems[op[2]] >= 0 else op[2] |
||||
i1_idx = stems[op[3]] if stems[op[3]] >= 0 else op[3] |
||||
i2_idx = stems[op[4]] if stems[op[4]] >= 0 else op[4] |
||||
i3_idx = stems[op[5]] if stems[op[5]] >= 0 else op[5] |
||||
ref_count[i0_idx] -= 1 |
||||
ref_count[i1_idx] -= 1 |
||||
ref_count[i2_idx] -= 1 |
||||
ref_count[i3_idx] -= 1 |
||||
if ref_count[i0_idx] <= 0: free_set.add(self.c_locs[i0_idx]) |
||||
if ref_count[i1_idx] <= 0: free_set.add(self.c_locs[i1_idx]) |
||||
if ref_count[i2_idx] <= 0: free_set.add(self.c_locs[i2_idx]) |
||||
if ref_count[i3_idx] <= 0: free_set.add(self.c_locs[i3_idx]) |
||||
o_idx = op[1] |
||||
cap = max(c_caps_min, c_caps[o_idx]) |
||||
self.c_locs[o_idx], self.c_caps[o_idx] = h.alloc(cap), cap |
||||
if c_reuse: |
||||
for loc in free_set: |
||||
h.free(loc) |
||||
|
||||
# copy memory location and capacity from stems to fanout lines |
||||
for lidx, stem in enumerate(stems): |
||||
if stem >= 0: # if at a fanout line |
||||
self.c_locs[lidx], self.c_caps[lidx] = self.c_locs[stem], self.c_caps[stem] |
||||
|
||||
# copy memory location to PO/PPO area |
||||
for i, n in enumerate(circuit.s_nodes): |
||||
if len(n.ins) > 0: |
||||
self.c_locs[self.ppo_offset + i], self.c_caps[self.ppo_offset + i] = self.c_locs[n.ins[0]], self.c_caps[n.ins[0]] |
||||
|
||||
self.c_len = h.max_size |
||||
|
||||
d = defaultdict(int) |
||||
for op in self.ops[:,0]: d[names[op]] += 1 |
||||
self.prim_counts = dict(d) |
||||
|
||||
self.pi_s_locs = np.flatnonzero(self.c_locs[self.ppi_offset+np.arange(len(self.circuit.io_nodes))] >= 0) |
||||
self.po_s_locs = np.flatnonzero(self.c_locs[self.ppo_offset+np.arange(len(self.circuit.io_nodes))] >= 0) |
||||
self.ppio_s_locs = np.arange(len(self.circuit.io_nodes), self.s_len) |
||||
|
||||
self.pippi_s_locs = np.concatenate([self.pi_s_locs, self.ppio_s_locs]) |
||||
self.poppo_s_locs = np.concatenate([self.po_s_locs, self.ppio_s_locs]) |
||||
|
||||
self.pi_c_locs = self.c_locs[self.ppi_offset+self.pi_s_locs] |
||||
self.po_c_locs = self.c_locs[self.ppo_offset+self.po_s_locs] |
||||
self.ppi_c_locs = self.c_locs[self.ppi_offset+self.ppio_s_locs] |
||||
self.ppo_c_locs = self.c_locs[self.ppo_offset+self.ppio_s_locs] |
||||
|
||||
self.pippi_c_locs = np.concatenate([self.pi_c_locs, self.ppi_c_locs]) |
||||
self.poppo_c_locs = np.concatenate([self.po_c_locs, self.ppo_c_locs]) |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,8 +1,20 @@
@@ -1,8 +1,20 @@
|
||||
import pytest |
||||
|
||||
|
||||
@pytest.fixture |
||||
@pytest.fixture(scope='session') |
||||
def mydir(): |
||||
import os |
||||
from pathlib import Path |
||||
return Path(os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))) |
||||
|
||||
@pytest.fixture(scope='session') |
||||
def b15_2ig_circuit(mydir): |
||||
from kyupy import verilog |
||||
from kyupy.techlib import SAED32 |
||||
return verilog.load(mydir / 'b15_2ig.v.gz', branchforks=True, tlib=SAED32) |
||||
|
||||
@pytest.fixture(scope='session') |
||||
def b15_2ig_delays(mydir, b15_2ig_circuit): |
||||
from kyupy import sdf |
||||
from kyupy.techlib import SAED32 |
||||
return sdf.load(mydir / 'b15_2ig.sdf.gz').iopaths(b15_2ig_circuit, tlib=SAED32)[1:2] |
||||
|
@ -0,0 +1,335 @@
@@ -0,0 +1,335 @@
|
||||
/* Generated by Yosys 0.9 (git sha1 UNKNOWN, gcc 4.8.5 -fPIC -Os) */ |
||||
|
||||
(* top = 1 *) |
||||
(* src = "rng_haltonBase2.v:1" *) |
||||
module rng1(clk, reset, o_output); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
wire [11:0] _00_; |
||||
wire _01_; |
||||
wire _02_; |
||||
wire _03_; |
||||
wire _04_; |
||||
wire _05_; |
||||
wire _06_; |
||||
wire _07_; |
||||
wire _08_; |
||||
wire _09_; |
||||
wire _10_; |
||||
wire _11_; |
||||
wire _12_; |
||||
wire _13_; |
||||
wire _14_; |
||||
wire _15_; |
||||
wire _16_; |
||||
wire _17_; |
||||
wire _18_; |
||||
wire _19_; |
||||
wire _20_; |
||||
wire _21_; |
||||
wire _22_; |
||||
wire _23_; |
||||
wire _24_; |
||||
wire _25_; |
||||
wire _26_; |
||||
wire _27_; |
||||
wire _28_; |
||||
wire _29_; |
||||
wire _30_; |
||||
wire _31_; |
||||
wire _32_; |
||||
wire _33_; |
||||
wire _34_; |
||||
(* src = "rng_haltonBase2.v:2" *) |
||||
input clk; |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:12" *) |
||||
wire \halton.clk ; |
||||
(* init = 12'h000 *) |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:17" *) |
||||
wire [11:0] \halton.counter ; |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:14" *) |
||||
wire [11:0] \halton.o_output ; |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:13" *) |
||||
wire \halton.reset ; |
||||
(* src = "rng_haltonBase2.v:4" *) |
||||
output [11:0] o_output; |
||||
(* src = "rng_haltonBase2.v:3" *) |
||||
input reset; |
||||
AND2X1 _35_ ( |
||||
.IN1(\halton.counter [1]), |
||||
.IN2(\halton.counter [0]), |
||||
.Q(_01_) |
||||
); |
||||
NOR2X0 _36_ ( |
||||
.IN1(\halton.counter [1]), |
||||
.IN2(\halton.counter [0]), |
||||
.QN(_02_) |
||||
); |
||||
NOR3X0 _37_ ( |
||||
.IN1(reset), |
||||
.IN2(_01_), |
||||
.IN3(_02_), |
||||
.QN(_00_[1]) |
||||
); |
||||
AND2X1 _38_ ( |
||||
.IN1(\halton.counter [2]), |
||||
.IN2(_01_), |
||||
.Q(_03_) |
||||
); |
||||
NOR2X0 _39_ ( |
||||
.IN1(\halton.counter [2]), |
||||
.IN2(_01_), |
||||
.QN(_04_) |
||||
); |
||||
NOR3X0 _40_ ( |
||||
.IN1(reset), |
||||
.IN2(_03_), |
||||
.IN3(_04_), |
||||
.QN(_00_[2]) |
||||
); |
||||
AND4X1 _41_ ( |
||||
.IN1(\halton.counter [1]), |
||||
.IN2(\halton.counter [0]), |
||||
.IN3(\halton.counter [2]), |
||||
.IN4(\halton.counter [3]), |
||||
.Q(_05_) |
||||
); |
||||
NOR2X0 _42_ ( |
||||
.IN1(\halton.counter [3]), |
||||
.IN2(_03_), |
||||
.QN(_06_) |
||||
); |
||||
NOR3X0 _43_ ( |
||||
.IN1(reset), |
||||
.IN2(_05_), |
||||
.IN3(_06_), |
||||
.QN(_00_[3]) |
||||
); |
||||
AND2X1 _44_ ( |
||||
.IN1(\halton.counter [4]), |
||||
.IN2(_05_), |
||||
.Q(_07_) |
||||
); |
||||
NOR2X0 _45_ ( |
||||
.IN1(\halton.counter [4]), |
||||
.IN2(_05_), |
||||
.QN(_08_) |
||||
); |
||||
NOR3X0 _46_ ( |
||||
.IN1(reset), |
||||
.IN2(_07_), |
||||
.IN3(_08_), |
||||
.QN(_00_[4]) |
||||
); |
||||
AND2X1 _47_ ( |
||||
.IN1(\halton.counter [5]), |
||||
.IN2(_07_), |
||||
.Q(_09_) |
||||
); |
||||
NOR2X0 _48_ ( |
||||
.IN1(\halton.counter [5]), |
||||
.IN2(_07_), |
||||
.QN(_10_) |
||||
); |
||||
NOR3X0 _49_ ( |
||||
.IN1(reset), |
||||
.IN2(_09_), |
||||
.IN3(_10_), |
||||
.QN(_00_[5]) |
||||
); |
||||
AND4X1 _50_ ( |
||||
.IN1(\halton.counter [4]), |
||||
.IN2(\halton.counter [5]), |
||||
.IN3(\halton.counter [6]), |
||||
.IN4(_05_), |
||||
.Q(_11_) |
||||
); |
||||
NOR2X0 _51_ ( |
||||
.IN1(\halton.counter [6]), |
||||
.IN2(_09_), |
||||
.QN(_12_) |
||||
); |
||||
NOR3X0 _52_ ( |
||||
.IN1(reset), |
||||
.IN2(_11_), |
||||
.IN3(_12_), |
||||
.QN(_00_[6]) |
||||
); |
||||
AND2X1 _53_ ( |
||||
.IN1(\halton.counter [7]), |
||||
.IN2(_11_), |
||||
.Q(_13_) |
||||
); |
||||
NOR2X0 _54_ ( |
||||
.IN1(\halton.counter [7]), |
||||
.IN2(_11_), |
||||
.QN(_14_) |
||||
); |
||||
NOR3X0 _55_ ( |
||||
.IN1(reset), |
||||
.IN2(_13_), |
||||
.IN3(_14_), |
||||
.QN(_00_[7]) |
||||
); |
||||
AND3X1 _56_ ( |
||||
.IN1(\halton.counter [7]), |
||||
.IN2(\halton.counter [8]), |
||||
.IN3(_11_), |
||||
.Q(_15_) |
||||
); |
||||
NOR2X0 _57_ ( |
||||
.IN1(\halton.counter [8]), |
||||
.IN2(_13_), |
||||
.QN(_16_) |
||||
); |
||||
NOR3X0 _58_ ( |
||||
.IN1(reset), |
||||
.IN2(_15_), |
||||
.IN3(_16_), |
||||
.QN(_00_[8]) |
||||
); |
||||
AND4X1 _59_ ( |
||||
.IN1(\halton.counter [7]), |
||||
.IN2(\halton.counter [8]), |
||||
.IN3(\halton.counter [9]), |
||||
.IN4(_11_), |
||||
.Q(_17_) |
||||
); |
||||
NOR2X0 _60_ ( |
||||
.IN1(\halton.counter [9]), |
||||
.IN2(_15_), |
||||
.QN(_18_) |
||||
); |
||||
NOR3X0 _61_ ( |
||||
.IN1(reset), |
||||
.IN2(_17_), |
||||
.IN3(_18_), |
||||
.QN(_00_[9]) |
||||
); |
||||
AND2X1 _62_ ( |
||||
.IN1(\halton.counter [10]), |
||||
.IN2(_17_), |
||||
.Q(_19_) |
||||
); |
||||
NOR2X0 _63_ ( |
||||
.IN1(\halton.counter [10]), |
||||
.IN2(_17_), |
||||
.QN(_20_) |
||||
); |
||||
NOR3X0 _64_ ( |
||||
.IN1(reset), |
||||
.IN2(_19_), |
||||
.IN3(_20_), |
||||
.QN(_00_[10]) |
||||
); |
||||
AND3X1 _65_ ( |
||||
.IN1(\halton.counter [10]), |
||||
.IN2(\halton.counter [11]), |
||||
.IN3(_17_), |
||||
.Q(_21_) |
||||
); |
||||
AOI21X1 _66_ ( |
||||
.IN1(\halton.counter [10]), |
||||
.IN2(_17_), |
||||
.IN3(\halton.counter [11]), |
||||
.QN(_22_) |
||||
); |
||||
NOR3X0 _67_ ( |
||||
.IN1(reset), |
||||
.IN2(_21_), |
||||
.IN3(_22_), |
||||
.QN(_00_[11]) |
||||
); |
||||
NOR2X0 _68_ ( |
||||
.IN1(reset), |
||||
.IN2(\halton.counter [0]), |
||||
.QN(_00_[0]) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _69_ ( |
||||
.CLK(clk), |
||||
.D(_00_[0]), |
||||
.Q(\halton.counter [0]), |
||||
.QN(_23_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _70_ ( |
||||
.CLK(clk), |
||||
.D(_00_[1]), |
||||
.Q(\halton.counter [1]), |
||||
.QN(_24_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _71_ ( |
||||
.CLK(clk), |
||||
.D(_00_[2]), |
||||
.Q(\halton.counter [2]), |
||||
.QN(_25_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _72_ ( |
||||
.CLK(clk), |
||||
.D(_00_[3]), |
||||
.Q(\halton.counter [3]), |
||||
.QN(_26_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _73_ ( |
||||
.CLK(clk), |
||||
.D(_00_[4]), |
||||
.Q(\halton.counter [4]), |
||||
.QN(_27_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _74_ ( |
||||
.CLK(clk), |
||||
.D(_00_[5]), |
||||
.Q(\halton.counter [5]), |
||||
.QN(_28_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _75_ ( |
||||
.CLK(clk), |
||||
.D(_00_[6]), |
||||
.Q(\halton.counter [6]), |
||||
.QN(_29_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _76_ ( |
||||
.CLK(clk), |
||||
.D(_00_[7]), |
||||
.Q(\halton.counter [7]), |
||||
.QN(_30_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _77_ ( |
||||
.CLK(clk), |
||||
.D(_00_[8]), |
||||
.Q(\halton.counter [8]), |
||||
.QN(_31_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _78_ ( |
||||
.CLK(clk), |
||||
.D(_00_[9]), |
||||
.Q(\halton.counter [9]), |
||||
.QN(_32_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _79_ ( |
||||
.CLK(clk), |
||||
.D(_00_[10]), |
||||
.Q(\halton.counter [10]), |
||||
.QN(_33_) |
||||
); |
||||
(* src = "rng_haltonBase2.v:7|rng_haltonBase2.v:19" *) |
||||
DFFX1 _80_ ( |
||||
.CLK(clk), |
||||
.D(_00_[11]), |
||||
.Q(\halton.counter [11]), |
||||
.QN(_34_) |
||||
); |
||||
assign \halton.clk = clk; |
||||
assign \halton.o_output = { \halton.counter [0], \halton.counter [1], \halton.counter [2], \halton.counter [3], \halton.counter [4], \halton.counter [5], \halton.counter [6], \halton.counter [7], \halton.counter [8], \halton.counter [9], \halton.counter [10], \halton.counter [11] }; |
||||
assign \halton.reset = reset; |
||||
assign o_output = { \halton.counter [0], \halton.counter [1], \halton.counter [2], \halton.counter [3], \halton.counter [4], \halton.counter [5], \halton.counter [6], \halton.counter [7], \halton.counter [8], \halton.counter [9], \halton.counter [10], \halton.counter [11] }; |
||||
endmodule |
@ -1,252 +1,75 @@
@@ -1,252 +1,75 @@
|
||||
import numpy as np |
||||
import kyupy.logic as lg |
||||
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp |
||||
|
||||
|
||||
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' |
||||
|
||||
x30_2v = lg.MVArray("0000", m=2) |
||||
x31_2v = lg.MVArray("1111", m=2) |
||||
x30_4v = lg.MVArray("0000000000000000", m=4) |
||||
x31_4v = lg.MVArray("1111111111111111", m=4) |
||||
x30_8v = lg.MVArray("0000000000000000000000000000000000000000000000000000000000000000", m=8) |
||||
x31_8v = lg.MVArray("1111111111111111111111111111111111111111111111111111111111111111", m=8) |
||||
|
||||
assert lg.mv_latch(x1_2v, x2_2v, x30_2v)[0] == '0001' |
||||
assert lg.mv_latch(x1_2v, x2_2v, x31_2v)[0] == '1011' |
||||
assert lg.mv_latch(x1_4v, x2_4v, x30_4v)[0] == '0XX00XXX0XXX0XX1' |
||||
assert lg.mv_latch(x1_4v, x2_4v, x31_4v)[0] == '1XX01XXX1XXX1XX1' |
||||
assert lg.mv_latch(x1_8v, x2_8v, x30_8v)[0] == '0XX000000XXXXXXX0XXXXXXX0XX10R110XX000000XXR0R0R0XXF001F0XX10R11' |
||||
assert lg.mv_latch(x1_8v, x2_8v, x31_8v)[0] == '1XX01F001XXXXXXX1XXXXXXX1XX111111XX01F001XXR110R1XXF1F1F1XX11111' |
||||
def assert_equal_shape_and_contents(actual, desired): |
||||
desired = np.array(desired, dtype=np.uint8) |
||||
assert actual.shape == desired.shape |
||||
np.testing.assert_allclose(actual, desired) |
||||
|
||||
|
||||
def test_mvarray_single_vector(): |
||||
assert_equal_shape_and_contents(mvarray(1, 0, 1), [lg.ONE, lg.ZERO, lg.ONE]) |
||||
assert_equal_shape_and_contents(mvarray([1, 0, 1]), [lg.ONE, lg.ZERO, lg.ONE]) |
||||
assert_equal_shape_and_contents(mvarray('10X-RFPN'), [lg.ONE, lg.ZERO, lg.UNKNOWN, lg.UNASSIGNED, lg.RISE, lg.FALL, lg.PPULSE, lg.NPULSE]) |
||||
assert_equal_shape_and_contents(mvarray(['1']), [lg.ONE]) |
||||
assert_equal_shape_and_contents(mvarray('1'), [lg.ONE]) |
||||
|
||||
|
||||
def test_mvarray_multi_vector(): |
||||
assert_equal_shape_and_contents(mvarray([0, 0], [0, 1], [1, 0], [1, 1]), [[lg.ZERO, lg.ZERO, lg.ONE, lg.ONE], [lg.ZERO, lg.ONE, lg.ZERO, lg.ONE]]) |
||||
assert_equal_shape_and_contents(mvarray('10X', '--1'), [[lg.ONE, lg.UNASSIGNED], [lg.ZERO, lg.UNASSIGNED], [lg.UNKNOWN, lg.ONE]]) |
||||
|
||||
|
||||
def test_mv_ops(): |
||||
x1_8v = mvarray('00000000XXXXXXXX--------11111111PPPPPPPPRRRRRRRRFFFFFFFFNNNNNNNN') |
||||
x2_8v = mvarray('0X-1PRFN'*8) |
||||
|
||||
assert_equal_shape_and_contents(lg.mv_not(x1_8v), mvarray('11111111XXXXXXXXXXXXXXXX00000000NNNNNNNNFFFFFFFFRRRRRRRRPPPPPPPP')) |
||||
assert_equal_shape_and_contents(lg.mv_or(x1_8v, x2_8v), mvarray('0XX1PRFNXXX1XXXXXXX1XXXX11111111PXX1PRFNRXX1RRNNFXX1FNFNNXX1NNNN')) |
||||
assert_equal_shape_and_contents(lg.mv_and(x1_8v, x2_8v), mvarray('000000000XXXXXXX0XXXXXXX0XX1PRFN0XXPPPPP0XXRPRPR0XXFPPFF0XXNPRFN')) |
||||
assert_equal_shape_and_contents(lg.mv_xor(x1_8v, x2_8v), mvarray('0XX1PRFNXXXXXXXXXXXXXXXX1XX0NFRPPXXNPRFNRXXFRPNFFXXRFNPRNXXPNFRP')) |
||||
|
||||
# TODO |
||||
#assert_equal_shape_and_contents(lg.mv_transition(x1_8v, x2_8v), mvarray('0XXR PRFNXXXXXXXXXXXXXXXX1XX0NFRPPXXNPRFNRXXFRPNFFXXRFNPRNXXPNFRP')) |
||||
|
||||
x30_8v = mvarray('0000000000000000000000000000000000000000000000000000000000000000') |
||||
x31_8v = mvarray('1111111111111111111111111111111111111111111111111111111111111111') |
||||
|
||||
assert_equal_shape_and_contents(lg.mv_latch(x1_8v, x2_8v, x30_8v), mvarray('0XX000000XXXXXXX0XXXXXXX0XX10R110XX000000XXR0R0R0XXF001F0XX10R11')) |
||||
assert_equal_shape_and_contents(lg.mv_latch(x1_8v, x2_8v, x31_8v), mvarray('1XX01F001XXXXXXX1XXXXXXX1XX111111XX01F001XXR110R1XXF1F1F1XX11111')) |
||||
|
||||
|
||||
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' |
||||
|
||||
x30_2v = lg.BPArray("0000", m=2) |
||||
x30_4v = lg.BPArray("0000000000000000", m=4) |
||||
x30_8v = lg.BPArray("0000000000000000000000000000000000000000000000000000000000000000", m=8) |
||||
|
||||
lg.bp_latch(out_2v.data, x1_2v.data, x2_2v.data, x30_2v.data) |
||||
lg.bp_latch(out_4v.data, x1_4v.data, x2_4v.data, x30_4v.data) |
||||
lg.bp_latch(out_8v.data, x1_8v.data, x2_8v.data, x30_8v.data) |
||||
|
||||
assert lg.MVArray(out_2v)[0] == '0001' |
||||
assert lg.MVArray(out_4v)[0] == '0XX00XXX0XXX0XX1' |
||||
assert lg.MVArray(out_8v)[0] == '0XX000000XXXXXXX0XXXXXXX0XX10R110XX000000XXR0R0R0XXF001F0XX10R11' |
||||
|
||||
x31_2v = lg.BPArray("1111", m=2) |
||||
x31_4v = lg.BPArray("1111111111111111", m=4) |
||||
x31_8v = lg.BPArray("1111111111111111111111111111111111111111111111111111111111111111", m=8) |
||||
|
||||
lg.bp_latch(out_2v.data, x1_2v.data, x2_2v.data, x31_2v.data) |
||||
lg.bp_latch(out_4v.data, x1_4v.data, x2_4v.data, x31_4v.data) |
||||
lg.bp_latch(out_8v.data, x1_8v.data, x2_8v.data, x31_8v.data) |
||||
|
||||
assert lg.MVArray(out_2v)[0] == '1011' |
||||
assert lg.MVArray(out_4v)[0] == '1XX01XXX1XXX1XX1' |
||||
assert lg.MVArray(out_8v)[0] == '1XX01F001XXXXXXX1XXXXXXX1XX111111XX01F001XXR110R1XXF1F1F1XX11111' |
||||
bpa = bparray('0X-1PRFN') |
||||
assert bpa.shape == (8, 3, 1) |
||||
|
||||
bpa = bparray('0X-1PRFN-') |
||||
assert bpa.shape == (9, 3, 1) |
||||
|
||||
bpa = bparray('000', '001', '010', '011', '100', '101', '110', '111') |
||||
assert bpa.shape == (3, 3, 1) |
||||
|
||||
bpa = bparray('000', '001', '010', '011', '100', '101', '110', '111', 'RFX') |
||||
assert bpa.shape == (3, 3, 2) |
||||
|
||||
assert_equal_shape_and_contents(bp_to_mv(bparray('0X-1PRFN'))[:,0], mvarray('0X-1PRFN')) |
||||
assert_equal_shape_and_contents(bparray('0X-1PRFN'), mv_to_bp(mvarray('0X-1PRFN'))) |
||||
|
||||
x1_8v = bparray('00000000XXXXXXXX--------11111111PPPPPPPPRRRRRRRRFFFFFFFFNNNNNNNN') |
||||
x2_8v = bparray('0X-1PRFN'*8) |
||||
|
||||
out_8v = np.empty((64, 3, 1), dtype=np.uint8) |
||||
|
||||
assert_equal_shape_and_contents(bp_to_mv(lg.bp8v_buf(out_8v, x1_8v))[:,0], mvarray('00000000XXXXXXXXXXXXXXXX11111111PPPPPPPPRRRRRRRRFFFFFFFFNNNNNNNN')) |
||||
assert_equal_shape_and_contents(bp_to_mv(lg.bp8v_or(out_8v, x1_8v, x2_8v))[:,0], mvarray('0XX1PRFNXXX1XXXXXXX1XXXX11111111PXX1PRFNRXX1RRNNFXX1FNFNNXX1NNNN')) |
||||
assert_equal_shape_and_contents(bp_to_mv(lg.bp8v_and(out_8v, x1_8v, x2_8v))[:,0], mvarray('000000000XXXXXXX0XXXXXXX0XX1PRFN0XXPPPPP0XXRPRPR0XXFPPFF0XXNPRFN')) |
||||
assert_equal_shape_and_contents(bp_to_mv(lg.bp8v_xor(out_8v, x1_8v, x2_8v))[:,0], mvarray('0XX1PRFNXXXXXXXXXXXXXXXX1XX0NFRPPXXNPRFNRXXFRPNFFXXRFNPRNXXPNFRP')) |
||||
|
||||
x30_8v = bparray('0000000000000000000000000000000000000000000000000000000000000000') |
||||
x31_8v = bparray('1111111111111111111111111111111111111111111111111111111111111111') |
||||
|
||||
assert_equal_shape_and_contents(bp_to_mv(lg.bp8v_latch(out_8v, x1_8v, x2_8v, x30_8v))[:,0], mvarray('0XX000000XXXXXXX0XXXXXXX0XX10R110XX000000XXR0R0R0XXF001F0XX10R11')) |
||||
assert_equal_shape_and_contents(bp_to_mv(lg.bp8v_latch(out_8v, x1_8v, x2_8v, x31_8v))[:,0], mvarray('1XX01F001XXXXXXX1XXXXXXX1XX111111XX01F001XXR110R1XXF1F1F1XX11111')) |
||||
|
@ -1,135 +1,175 @@
@@ -1,135 +1,175 @@
|
||||
import numpy as np |
||||
|
||||
from kyupy.logic_sim import LogicSim |
||||
from kyupy import bench |
||||
from kyupy.logic import MVArray, BPArray |
||||
from kyupy import bench, logic, sim |
||||
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp |
||||
|
||||
def assert_equal_shape_and_contents(actual, desired): |
||||
desired = np.array(desired, dtype=np.uint8) |
||||
assert actual.shape == desired.shape |
||||
np.testing.assert_allclose(actual, desired) |
||||
|
||||
def test_2v(): |
||||
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, m=2) |
||||
assert len(s.interface) == 5 |
||||
mva = MVArray(['00000', '01000', '10000', '11000'], m=2) |
||||
bpa = BPArray(mva) |
||||
s.assign(bpa) |
||||
s.propagate() |
||||
s.capture(bpa) |
||||
mva = MVArray(bpa) |
||||
assert mva[0] == '00001' |
||||
assert mva[1] == '01011' |
||||
assert mva[2] == '10010' |
||||
assert mva[3] == '11110' |
||||
c = bench.parse(f''' |
||||
input(i3, i2, i1, i0) |
||||
output({",".join([f"o{i:02d}" for i in range(33)])}) |
||||
o00=BUF1(i0) |
||||
o01=INV1(i0) |
||||
o02=AND2(i0,i1) |
||||
o03=AND3(i0,i1,i2) |
||||
o04=AND4(i0,i1,i2,i3) |
||||
o05=NAND2(i0,i1) |
||||
o06=NAND3(i0,i1,i2) |
||||
o07=NAND4(i0,i1,i2,i3) |
||||
o08=OR2(i0,i1) |
||||
o09=OR3(i0,i1,i2) |
||||
o10=OR4(i0,i1,i2,i3) |
||||
o11=NOR2(i0,i1) |
||||
o12=NOR3(i0,i1,i2) |
||||
o13=NOR4(i0,i1,i2,i3) |
||||
o14=XOR2(i0,i1) |
||||
o15=XOR3(i0,i1,i2) |
||||
o16=XOR4(i0,i1,i2,i3) |
||||
o17=XNOR2(i0,i1) |
||||
o18=XNOR3(i0,i1,i2) |
||||
o19=XNOR4(i0,i1,i2,i3) |
||||
o20=AO21(i0,i1,i2) |
||||
o21=OA21(i0,i1,i2) |
||||
o22=AO22(i0,i1,i2,i3) |
||||
o23=OA22(i0,i1,i2,i3) |
||||
o24=AOI21(i0,i1,i2) |
||||
o25=OAI21(i0,i1,i2) |
||||
o26=AOI22(i0,i1,i2,i3) |
||||
o27=OAI22(i0,i1,i2,i3) |
||||
o28=AO211(i0,i1,i2,i3) |
||||
o29=OA211(i0,i1,i2,i3) |
||||
o30=AOI211(i0,i1,i2,i3) |
||||
o31=OAI211(i0,i1,i2,i3) |
||||
o32=MUX21(i0,i1,i2) |
||||
''') |
||||
s = LogicSim(c, 16, m=2) |
||||
bpa = logic.bparray([f'{i:04b}'+('-'*(s.s_len-4)) for i in range(16)]) |
||||
s.s[0] = bpa |
||||
s.s_to_c() |
||||
s.c_prop() |
||||
s.c_to_s() |
||||
mva = logic.bp_to_mv(s.s[1]) |
||||
for res, exp in zip(logic.packbits(mva[4:], dtype=np.uint32), [ |
||||
sim.BUF1, sim.INV1, |
||||
sim.AND2, sim.AND3, sim.AND4, |
||||
sim.NAND2, sim.NAND3, sim.NAND4, |
||||
sim.OR2, sim.OR3, sim.OR4, |
||||
sim.NOR2, sim.NOR3, sim.NOR4, |
||||
sim.XOR2, sim.XOR3, sim.XOR4, |
||||
sim.XNOR2, sim.XNOR3, sim.XNOR4, |
||||
sim.AO21, sim.OA21, |
||||
sim.AO22, sim.OA22, |
||||
sim.AOI21, sim.OAI21, |
||||
sim.AOI22, sim.OAI22, |
||||
sim.AO211, sim.OA211, |
||||
sim.AOI211, sim.OAI211, |
||||
sim.MUX21 |
||||
]): |
||||
assert res == exp, f'Mismatch for SimPrim {sim.names[exp]} res={bin(res)} exp={bin(exp)}' |
||||
|
||||
|
||||
def test_4v(): |
||||
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, m=4) |
||||
assert len(s.interface) == 5 |
||||
mva = MVArray(['00000', '01000', '0-000', '0X000', |
||||
'10000', '11000', '1-000', '1X000', |
||||
'-0000', '-1000', '--000', '-X000', |
||||
'X0000', 'X1000', 'X-000', 'XX000'], m=4) |
||||
bpa = BPArray(mva) |
||||
s.assign(bpa) |
||||
s.propagate() |
||||
s.capture(bpa) |
||||
mva = MVArray(bpa) |
||||
assert mva[0] == '00001' |
||||
assert mva[1] == '01011' |
||||
assert mva[2] == '0-0X1' |
||||
assert mva[3] == '0X0X1' |
||||
assert mva[4] == '10010' |
||||
assert mva[5] == '11110' |
||||
assert mva[6] == '1-X10' |
||||
assert mva[7] == '1XX10' |
||||
assert mva[8] == '-00XX' |
||||
assert mva[9] == '-1X1X' |
||||
assert mva[10] == '--XXX' |
||||
assert mva[11] == '-XXXX' |
||||
assert mva[12] == 'X00XX' |
||||
assert mva[13] == 'X1X1X' |
||||
assert mva[14] == 'X-XXX' |
||||
assert mva[15] == 'XXXXX' |
||||
s = LogicSim(c, 16, m=8) # FIXME: m=4 |
||||
assert s.s_len == 5 |
||||
bpa = bparray( |
||||
'00---', '01---', '0----', '0X---', |
||||
'10---', '11---', '1----', '1X---', |
||||
'-0---', '-1---', '-----', '-X---', |
||||
'X0---', 'X1---', 'X----', 'XX---') |
||||
s.s[0] = bpa |
||||
s.s_to_c() |
||||
s.c_prop() |
||||
s.c_to_s() |
||||
mva = bp_to_mv(s.s[1]) |
||||
assert_equal_shape_and_contents(mva, mvarray( |
||||
'--001', '--011', '--0X1', '--0X1', |
||||
'--010', '--110', '--X10', '--X10', |
||||
'--0XX', '--X1X', '--XXX', '--XXX', |
||||
'--0XX', '--X1X', '--XXX', '--XXX')) |
||||
|
||||
|
||||
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)') |
||||
s = LogicSim(c, 64, m=8) |
||||
assert len(s.interface) == 6 |
||||
mva = MVArray(['000010', '010111', '0-0X1X', '0X0X1X', '0R0R1R', '0F0F1F', '0P0P1P', '0N0N1N', |
||||
'100101', '111100', '1-X10X', '1XX10X', '1RR10F', '1FF10R', '1PP10N', '1NN10P', |
||||
'-00XXX', '-1X1XX', '--XXXX', '-XXXXX', '-RXXXX', '-FXXXX', '-PXXXX', '-NXXXX', |
||||
'X00XXX', 'X1X1XX', 'X-XXXX', 'XXXXXX', 'XRXXXX', 'XFXXXX', 'XPXXXX', 'XNXXXX', |
||||
'R00RFR', 'R1R1FF', 'R-XXFX', 'RXXXFX', 'RRRRFP', 'RFPNFN', 'RPPRFR', 'RNRNFF', |
||||
'F00FRF', 'F1F1RR', 'F-XXRX', 'FXXXRX', 'FRPNRN', 'FFFFRP', 'FPPFRF', 'FNFNRR', |
||||
'P00PNP', 'P1P1NN', 'P-XXNX', 'PXXXNX', 'PRPRNR', 'PFPFNF', 'PPPPNP', 'PNPNNN', |
||||
'N00NPN', 'N1N1PP', 'N-XXPX', 'NXXXPX', 'NRRNPF', 'NFFNPR', 'NPPNPN', 'NNNNPP'], m=8) |
||||
bpa = BPArray(mva) |
||||
s.assign(bpa) |
||||
s.propagate() |
||||
resp_bp = BPArray(bpa) |
||||
s.capture(resp_bp) |
||||
resp = MVArray(resp_bp) |
||||
|
||||
for i in range(64): |
||||
assert resp[i] == mva[i] |
||||
assert s.s_len == 6 |
||||
mva = mvarray( |
||||
'000010', '010111', '0-0X1X', '0X0X1X', '0R0R1R', '0F0F1F', '0P0P1P', '0N0N1N', |
||||
'100101', '111100', '1-X10X', '1XX10X', '1RR10F', '1FF10R', '1PP10N', '1NN10P', |
||||
'-00XXX', '-1X1XX', '--XXXX', '-XXXXX', '-RXXXX', '-FXXXX', '-PXXXX', '-NXXXX', |
||||
'X00XXX', 'X1X1XX', 'X-XXXX', 'XXXXXX', 'XRXXXX', 'XFXXXX', 'XPXXXX', 'XNXXXX', |
||||
'R00RFR', 'R1R1FF', 'R-XXFX', 'RXXXFX', 'RRRRFP', 'RFPNFN', 'RPPRFR', 'RNRNFF', |
||||
'F00FRF', 'F1F1RR', 'F-XXRX', 'FXXXRX', 'FRPNRN', 'FFFFRP', 'FPPFRF', 'FNFNRR', |
||||
'P00PNP', 'P1P1NN', 'P-XXNX', 'PXXXNX', 'PRPRNR', 'PFPFNF', 'PPPPNP', 'PNPNNN', |
||||
'N00NPN', 'N1N1PP', 'N-XXPX', 'NXXXPX', 'NRRNPF', 'NFFNPR', 'NPPNPN', 'NNNNPP') |
||||
tests = np.copy(mva) |
||||
tests[2:] = logic.UNASSIGNED |
||||
bpa = mv_to_bp(tests) |
||||
s.s[0] = bpa |
||||
s.s_to_c() |
||||
s.c_prop() |
||||
s.c_to_s() |
||||
resp = bp_to_mv(s.s[1]) |
||||
|
||||
exp_resp = np.copy(mva) |
||||
exp_resp[:2] = logic.UNASSIGNED |
||||
np.testing.assert_allclose(resp, exp_resp) |
||||
|
||||
|
||||
def test_loop(): |
||||
c = bench.parse('q=dff(d) d=not(q)') |
||||
s = LogicSim(c, 4, m=8) |
||||
assert len(s.interface) == 1 |
||||
mva = MVArray([['0'], ['1'], ['R'], ['F']], m=8) |
||||
assert s.s_len == 1 |
||||
mva = mvarray([['0'], ['1'], ['R'], ['F']]) |
||||
|
||||
s.assign(BPArray(mva)) |
||||
s.propagate() |
||||
resp_bp = BPArray((len(s.interface), s.sims)) |
||||
s.capture(resp_bp) |
||||
resp = MVArray(resp_bp) |
||||
# TODO |
||||
# s.assign(BPArray(mva)) |
||||
# s.propagate() |
||||
# resp_bp = BPArray((len(s.interface), s.sims)) |
||||
# s.capture(resp_bp) |
||||
# resp = MVArray(resp_bp) |
||||
|
||||
assert resp[0] == '1' |
||||
assert resp[1] == '0' |
||||
assert resp[2] == 'F' |
||||
assert resp[3] == 'R' |
||||
# assert resp[0] == '1' |
||||
# assert resp[1] == '0' |
||||
# assert resp[2] == 'F' |
||||
# assert resp[3] == 'R' |
||||
|
||||
resp_bp = s.cycle(resp_bp) |
||||
resp = MVArray(resp_bp) |
||||
# resp_bp = s.cycle(resp_bp) |
||||
# resp = MVArray(resp_bp) |
||||
|
||||
assert resp[0] == '0' |
||||
assert resp[1] == '1' |
||||
assert resp[2] == 'R' |
||||
assert resp[3] == 'F' |
||||
# assert resp[0] == '0' |
||||
# assert resp[1] == '1' |
||||
# assert resp[2] == 'R' |
||||
# assert resp[3] == 'F' |
||||
|
||||
|
||||
def test_latch(): |
||||
c = bench.parse('input(d, t) output(q) q=latch(d, t)') |
||||
s = LogicSim(c, 8, m=8) |
||||
assert len(s.interface) == 4 |
||||
mva = MVArray(['00-0', '00-1', '01-0', '01-1', '10-0', '10-1', '11-0', '11-1'], m=8) |
||||
exp = MVArray(['0000', '0011', '0100', '0100', '1000', '1011', '1111', '1111'], m=8) |
||||
assert s.s_len == 4 |
||||
mva = mvarray('00-0', '00-1', '01-0', '01-1', '10-0', '10-1', '11-0', '11-1') |
||||
exp = mvarray('0000', '0011', '0100', '0100', '1000', '1011', '1111', '1111') |
||||
|
||||
resp = MVArray(s.cycle(BPArray(mva))) |
||||
# TODO |
||||
# resp = MVArray(s.cycle(BPArray(mva))) |
||||
|
||||
for i in range(len(mva)): |
||||
assert resp[i] == exp[i] |
||||
# for i in range(len(mva)): |
||||
# assert resp[i] == exp[i] |
||||
|
||||
|
||||
def test_b01(mydir): |
||||
c = bench.load(mydir / 'b01.bench') |
||||
|
||||
# 2-valued |
||||
s = LogicSim(c, 8, m=2) |
||||
assert len(s.interface) == 9 |
||||
mva = MVArray((len(s.interface), 8), m=2) |
||||
# mva.randomize() |
||||
bpa = BPArray(mva) |
||||
s.assign(bpa) |
||||
s.propagate() |
||||
s.capture(bpa) |
||||
|
||||
# 8-valued |
||||
s = LogicSim(c, 8, m=8) |
||||
mva = MVArray((len(s.interface), 8), m=8) |
||||
# mva.randomize() |
||||
bpa = BPArray(mva) |
||||
s.assign(bpa) |
||||
s.propagate() |
||||
s.capture(bpa) |
||||
mva = np.zeros((s.s_len, 8), dtype=np.uint8) |
||||
s.s[0] = mv_to_bp(mva) |
||||
s.s_to_c() |
||||
s.c_prop() |
||||
s.c_to_s() |
||||
bp_to_mv(s.s[1]) |
||||
|
@ -1,21 +1,21 @@
@@ -1,21 +1,21 @@
|
||||
from kyupy import stil, verilog |
||||
from kyupy.techlib import SAED32 |
||||
|
||||
def test_b15(mydir): |
||||
b15 = verilog.load(mydir / 'b15_2ig.v.gz', tlib=SAED32) |
||||
|
||||
def test_b14(mydir): |
||||
b14 = verilog.load(mydir / 'b14.v.gz') |
||||
|
||||
s = stil.load(mydir / 'b14.stuck.stil.gz') |
||||
s = stil.load(mydir / 'b15_2ig.sa_nf.stil.gz') |
||||
assert len(s.signal_groups) == 10 |
||||
assert len(s.scan_chains) == 1 |
||||
assert len(s.calls) == 2163 |
||||
tests = s.tests(b14) |
||||
resp = s.responses(b14) |
||||
assert len(s.calls) == 1357 |
||||
tests = s.tests(b15) |
||||
resp = s.responses(b15) |
||||
assert len(tests) > 0 |
||||
assert len(resp) > 0 |
||||
|
||||
s2 = stil.load(mydir / 'b14.transition.stil.gz') |
||||
tests = s2.tests_loc(b14) |
||||
resp = s2.responses(b14) |
||||
|
||||
s2 = stil.load(mydir / 'b15_2ig.tf_nf.stil.gz') |
||||
tests = s2.tests_loc(b15) |
||||
resp = s2.responses(b15) |
||||
assert len(tests) > 0 |
||||
assert len(resp) > 0 |
||||
|
||||
|
@ -1,8 +1,45 @@
@@ -1,8 +1,45 @@
|
||||
from kyupy import verilog |
||||
|
||||
from kyupy.techlib import SAED90, SAED32 |
||||
|
||||
def test_b01(mydir): |
||||
with open(mydir / 'b01.v', 'r') as f: |
||||
modules = verilog.parse(f.read()) |
||||
assert modules is not None |
||||
assert verilog.load(mydir / 'b01.v') is not None |
||||
c = verilog.parse(f.read(), tlib=SAED90) |
||||
assert c is not None |
||||
assert verilog.load(mydir / 'b01.v', tlib=SAED90) is not None |
||||
|
||||
assert len(c.nodes) == 139 |
||||
assert len(c.lines) == 203 |
||||
stats = c.stats |
||||
assert stats['input'] == 6 |
||||
assert stats['output'] == 3 |
||||
assert stats['__seq__'] == 5 |
||||
|
||||
|
||||
def test_b15(mydir): |
||||
c = verilog.load(mydir / 'b15_4ig.v.gz', tlib=SAED32) |
||||
assert len(c.nodes) == 12067 |
||||
assert len(c.lines) == 20731 |
||||
stats = c.stats |
||||
assert stats['input'] == 40 |
||||
assert stats['output'] == 71 |
||||
assert stats['__seq__'] == 417 |
||||
|
||||
|
||||
def test_gates(mydir): |
||||
c = verilog.load(mydir / 'gates.v', tlib=SAED90) |
||||
assert len(c.nodes) == 10 |
||||
assert len(c.lines) == 10 |
||||
stats = c.stats |
||||
assert stats['input'] == 2 |
||||
assert stats['output'] == 2 |
||||
assert stats['__seq__'] == 0 |
||||
|
||||
|
||||
def test_halton2(mydir): |
||||
c = verilog.load(mydir / 'rng_haltonBase2.synth_yosys.v', tlib=SAED90) |
||||
assert len(c.nodes) == 146 |
||||
assert len(c.lines) == 210 |
||||
stats = c.stats |
||||
assert stats['input'] == 2 |
||||
assert stats['output'] == 12 |
||||
assert stats['__seq__'] == 12 |
@ -1,150 +1,168 @@
@@ -1,150 +1,168 @@
|
||||
import numpy as np |
||||
|
||||
from kyupy.wave_sim import WaveSim, WaveSimCuda, wave_eval, TMIN, TMAX |
||||
from kyupy.wave_sim import WaveSim, WaveSimCuda, wave_eval_cpu, TMIN, TMAX |
||||
from kyupy.logic_sim import LogicSim |
||||
from kyupy import verilog, sdf, logic |
||||
from kyupy.logic import MVArray, BPArray |
||||
from kyupy import logic, bench, sim |
||||
from kyupy.logic import mvarray |
||||
|
||||
def test_nand_delays(): |
||||
op = (sim.NAND4, 4, 0, 1, 2, 3, -1, 0, 0) |
||||
#op = (0b0111, 4, 0, 1) |
||||
c = np.full((5*16, 1), TMAX) # 5 waveforms of capacity 16 |
||||
c_locs = np.zeros((5,), dtype='int') |
||||
c_caps = np.zeros((5,), dtype='int') |
||||
|
||||
for i in range(5): c_locs[i], c_caps[i] = i*16, 16 # 1:1 mapping |
||||
|
||||
def test_wave_eval(): |
||||
# SDF specifies IOPATH delays with respect to output polarity |
||||
# SDF pulse rejection value is determined by IOPATH causing last transition and polarity of last transition |
||||
line_times = np.zeros((3, 2, 2)) |
||||
line_times[0, 0, 0] = 0.1 # A -> Z rise delay |
||||
line_times[0, 0, 1] = 0.2 # A -> Z fall delay |
||||
line_times[0, 1, 0] = 0.1 # A -> Z negative pulse limit (terminate in rising Z) |
||||
line_times[0, 1, 1] = 0.2 # A -> Z positive pulse limit |
||||
line_times[1, 0, 0] = 0.3 # as above for B -> Z |
||||
line_times[1, 0, 1] = 0.4 |
||||
line_times[1, 1, 0] = 0.3 |
||||
line_times[1, 1, 1] = 0.4 |
||||
|
||||
state = np.zeros((3*16, 1)) + TMAX # 3 waveforms of capacity 16 |
||||
state[::16, 0] = 16 # first entry is capacity |
||||
a = state[0:16, 0] |
||||
b = state[16:32, 0] |
||||
z = state[32:, 0] |
||||
sat = np.zeros((3, 3), dtype='int') |
||||
sat[0] = 0, 16, 0 |
||||
sat[1] = 16, 16, 0 |
||||
sat[2] = 32, 16, 0 |
||||
|
||||
sdata = np.asarray([1, -1, 0, 0], dtype='float32') |
||||
|
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMIN |
||||
|
||||
a[0] = TMIN |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMIN |
||||
|
||||
b[0] = TMIN |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMAX |
||||
|
||||
a[0] = 1 # A _/^^^ |
||||
b[0] = 2 # B __/^^ |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMIN # ^^^\___ B -> Z fall delay |
||||
assert z[1] == 2.4 |
||||
assert z[2] == TMAX |
||||
|
||||
a[0] = TMIN # A ^^^^^^ |
||||
b[0] = TMIN # B ^^^\__ |
||||
b[1] = 2 |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == 2.3 # ___/^^^ B -> Z rise delay |
||||
assert z[1] == TMAX |
||||
|
||||
# pos pulse of 0.35 at B -> 0.45 after delays |
||||
a[0] = TMIN # A ^^^^^^^^ |
||||
b[0] = TMIN |
||||
b[1] = 2 # B ^^\__/^^ |
||||
b[2] = 2.35 |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == 2.3 # __/^^\__ |
||||
assert z[1] == 2.75 |
||||
assert z[2] == TMAX |
||||
|
||||
# neg pulse of 0.45 at B -> 0.35 after delays |
||||
a[0] = TMIN # A ^^^^^^^^ |
||||
b[0] = 2 # B __/^^\__ |
||||
b[1] = 2.45 |
||||
b[2] = TMAX |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMIN # ^^\__/^^ |
||||
assert z[1] == 2.4 |
||||
assert z[2] == 2.75 |
||||
assert z[3] == TMAX |
||||
|
||||
# neg pulse of 0.35 at B -> 0.25 after delays (filtered) |
||||
a[0] = TMIN # A ^^^^^^^^ |
||||
b[0] = 2 # B __/^^\__ |
||||
b[1] = 2.35 |
||||
b[2] = TMAX |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMIN # ^^^^^^ |
||||
assert z[1] == TMAX |
||||
|
||||
# pos pulse of 0.25 at B -> 0.35 after delays (filtered) |
||||
a[0] = TMIN # A ^^^^^^^^ |
||||
b[0] = TMIN |
||||
b[1] = 2 # B ^^\__/^^ |
||||
b[2] = 2.25 |
||||
wave_eval((0b0111, 2, 0, 1), state, sat, 0, line_times, sdata) |
||||
assert z[0] == TMAX # ______ |
||||
|
||||
|
||||
def compare_to_logic_sim(wsim): |
||||
tests = MVArray((len(wsim.interface), wsim.sims)) |
||||
delays = np.zeros((1, 5, 2, 2)) |
||||
delays[0, 0, 0, 0] = 0.1 # A -> Z rise delay |
||||
delays[0, 0, 0, 1] = 0.2 # A -> Z fall delay |
||||
delays[0, 0, 1, 0] = 0.1 # A -> Z negative pulse limit (terminate in rising Z) |
||||
delays[0, 0, 1, 1] = 0.2 # A -> Z positive pulse limit |
||||
delays[0, 1, :, 0] = 0.3 # as above for B -> Z |
||||
delays[0, 1, :, 1] = 0.4 |
||||
delays[0, 2, :, 0] = 0.5 # as above for C -> Z |
||||
delays[0, 2, :, 1] = 0.6 |
||||
delays[0, 3, :, 0] = 0.7 # as above for D -> Z |
||||
delays[0, 3, :, 1] = 0.8 |
||||
|
||||
simctl_int = np.asarray([0], dtype=np.int32) |
||||
|
||||
def wave_assert(inputs, output): |
||||
for i, a in zip(inputs, c.reshape(-1,16)): a[:len(i)] = i |
||||
wave_eval_cpu(op, c, c_locs, c_caps, 0, delays, simctl_int) |
||||
for i, v in enumerate(output): np.testing.assert_allclose(c.reshape(-1,16)[4,i], v) |
||||
|
||||
wave_assert([[TMAX,TMAX],[TMAX,TMAX],[TMIN,TMAX],[TMIN,TMAX]], [TMIN,TMAX]) # NAND(0,0,1,1) => 1 |
||||
wave_assert([[TMIN,TMAX],[TMAX,TMAX],[TMIN,TMAX],[TMIN,TMAX]], [TMIN,TMAX]) # NAND(1,0,1,1) => 1 |
||||
wave_assert([[TMIN,TMAX],[TMIN,TMAX],[TMIN,TMAX],[TMIN,TMAX]], [TMAX]) # NAND(1,1,1,1) => 0 |
||||
|
||||
# Keep inputs C=1 and D=1. |
||||
wave_assert([[1,TMAX],[2,TMAX]], [TMIN,2.4,TMAX]) # _/⎺⎺⎺ NAND __/⎺⎺ => ⎺⎺⎺\___ (B->Z fall delay) |
||||
wave_assert([[TMIN,TMAX],[TMIN,2,TMAX]], [2.3,TMAX]) # ⎺⎺⎺⎺⎺ NAND ⎺⎺\__ => ___/⎺⎺⎺ (B->Z rise delay) |
||||
wave_assert([[TMIN,TMAX],[TMIN,2,2.35,TMAX]], [2.3,2.75,TMAX]) # ⎺⎺⎺⎺⎺ NAND ⎺\_/⎺ => __/⎺⎺\_ (pos pulse, .35@B -> .45@Z) |
||||
wave_assert([[TMIN,TMAX],[TMIN,2,2.25,TMAX]], [TMAX]) # ⎺⎺⎺⎺⎺ NAND ⎺\_/⎺ => _______ (pos pulse, .25@B -> .35@Z, filtered) |
||||
wave_assert([[TMIN,TMAX],[2,2.45,TMAX]], [TMIN,2.4,2.75,TMAX]) # ⎺⎺⎺⎺⎺ NAND _/⎺\_ => ⎺⎺\_/⎺⎺ (neg pulse, .45@B -> .35@Z) |
||||
wave_assert([[TMIN,TMAX],[2,2.35,TMAX]], [TMIN,TMAX]) # ⎺⎺⎺⎺⎺ NAND _/⎺\_ => ⎺⎺⎺⎺⎺⎺⎺ (neg pulse, .35@B -> .25@Z, filtered) |
||||
|
||||
|
||||
def test_tiny_circuit(): |
||||
c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)') |
||||
delays = np.full((1, len(c.lines), 2, 2), 1.0) # unit delay for all lines |
||||
wsim = WaveSim(c, delays) |
||||
assert wsim.s.shape[1] == 5 |
||||
|
||||
# values for x |
||||
wsim.s[:3,0,0] = 0, 10, 0 |
||||
wsim.s[:3,0,1] = 0, 20, 1 |
||||
wsim.s[:3,0,2] = 1, 30, 0 |
||||
wsim.s[:3,0,3] = 1, 40, 1 |
||||
|
||||
# values for y |
||||
wsim.s[:3,1,0] = 1, 50, 0 |
||||
wsim.s[:3,1,1] = 1, 60, 0 |
||||
wsim.s[:3,1,2] = 1, 70, 0 |
||||
wsim.s[:3,1,3] = 0, 80, 1 |
||||
|
||||
wsim.s_to_c() |
||||
|
||||
x_c_loc = wsim.c_locs[wsim.ppi_offset+0] # check x waveforms |
||||
np.testing.assert_allclose(wsim.c[x_c_loc:x_c_loc+3, 0], [TMAX, TMAX, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[x_c_loc:x_c_loc+3, 1], [20, TMAX, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[x_c_loc:x_c_loc+3, 2], [TMIN, 30, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[x_c_loc:x_c_loc+3, 3], [TMIN, TMAX, TMAX]) |
||||
|
||||
y_c_loc = wsim.c_locs[wsim.ppi_offset+1] # check y waveforms |
||||
np.testing.assert_allclose(wsim.c[y_c_loc:y_c_loc+3, 0], [TMIN, 50, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[y_c_loc:y_c_loc+3, 1], [TMIN, 60, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[y_c_loc:y_c_loc+3, 2], [TMIN, 70, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[y_c_loc:y_c_loc+3, 3], [80, TMAX, TMAX]) |
||||
|
||||
wsim.c_prop() |
||||
|
||||
a_c_loc = wsim.c_locs[wsim.ppo_offset+2] # check a waveforms |
||||
np.testing.assert_allclose(wsim.c[a_c_loc:a_c_loc+3, 0], [TMAX, TMAX, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[a_c_loc:a_c_loc+3, 1], [21, 61, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[a_c_loc:a_c_loc+3, 2], [TMIN, 31, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[a_c_loc:a_c_loc+3, 3], [81, TMAX, TMAX]) |
||||
|
||||
o_c_loc = wsim.c_locs[wsim.ppo_offset+3] # check o waveforms |
||||
np.testing.assert_allclose(wsim.c[o_c_loc:o_c_loc+3, 0], [TMIN, 51, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[o_c_loc:o_c_loc+3, 1], [TMIN, TMAX, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[o_c_loc:o_c_loc+3, 2], [TMIN, 71, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[o_c_loc:o_c_loc+3, 3], [TMIN, TMAX, TMAX]) |
||||
|
||||
n_c_loc = wsim.c_locs[wsim.ppo_offset+4] # check n waveforms |
||||
np.testing.assert_allclose(wsim.c[n_c_loc:n_c_loc+3, 0], [TMIN, TMAX, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[n_c_loc:n_c_loc+3, 1], [TMIN, 21, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[n_c_loc:n_c_loc+3, 2], [31, TMAX, TMAX]) |
||||
np.testing.assert_allclose(wsim.c[n_c_loc:n_c_loc+3, 3], [TMAX, TMAX, TMAX]) |
||||
|
||||
wsim.c_to_s() |
||||
|
||||
# check a captures |
||||
np.testing.assert_allclose(wsim.s[3:7, 2, 0], [0, TMAX, TMIN, 0]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 2, 1], [0, 21, 61, 0]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 2, 2], [1, 31, 31, 0]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 2, 3], [0, 81, 81, 1]) |
||||
|
||||
# check o captures |
||||
np.testing.assert_allclose(wsim.s[3:7, 3, 0], [1, 51, 51, 0]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 3, 1], [1, TMAX, TMIN, 1]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 3, 2], [1, 71, 71, 0]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 3, 3], [1, TMAX, TMIN, 1]) |
||||
|
||||
# check o captures |
||||
np.testing.assert_allclose(wsim.s[3:7, 4, 0], [1, TMAX, TMIN, 1]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 4, 1], [1, 21, 21, 0]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 4, 2], [0, 31, 31, 1]) |
||||
np.testing.assert_allclose(wsim.s[3:7, 4, 3], [0, TMAX, TMIN, 0]) |
||||
|
||||
|
||||
def compare_to_logic_sim(wsim: WaveSim): |
||||
choices = np.asarray([logic.ZERO, logic.ONE, logic.RISE, logic.FALL], dtype=np.uint8) |
||||
rng = np.random.default_rng(10) |
||||
tests.data[...] = rng.choice(choices, tests.data.shape) |
||||
tests_bp = BPArray(tests) |
||||
wsim.assign(tests_bp) |
||||
wsim.propagate() |
||||
cdata = wsim.capture() |
||||
|
||||
resp = MVArray(tests) |
||||
|
||||
for iidx, inode in enumerate(wsim.interface): |
||||
if len(inode.ins) > 0: |
||||
for vidx in range(wsim.sims): |
||||
resp.data[iidx, vidx] = logic.ZERO if cdata[iidx, vidx, 0] < 0.5 else logic.ONE |
||||
# resp.set_value(vidx, iidx, 0 if cdata[iidx, vidx, 0] < 0.5 else 1) |
||||
|
||||
lsim = LogicSim(wsim.circuit, len(tests_bp)) |
||||
lsim.assign(tests_bp) |
||||
lsim.propagate() |
||||
exp_bp = BPArray(tests_bp) |
||||
lsim.capture(exp_bp) |
||||
exp = MVArray(exp_bp) |
||||
|
||||
for i in range(8): |
||||
exp_str = exp[i].replace('R', '1').replace('F', '0').replace('P', '0').replace('N', '1') |
||||
res_str = resp[i].replace('R', '1').replace('F', '0').replace('P', '0').replace('N', '1') |
||||
assert res_str == exp_str |
||||
|
||||
|
||||
def test_b14(mydir): |
||||
c = verilog.load(mydir / 'b14.v.gz', branchforks=True) |
||||
df = sdf.load(mydir / 'b14.sdf.gz') |
||||
lt = df.annotation(c) |
||||
wsim = WaveSim(c, lt, 8) |
||||
compare_to_logic_sim(wsim) |
||||
|
||||
|
||||
def test_b14_strip_forks(mydir): |
||||
c = verilog.load(mydir / 'b14.v.gz', branchforks=True) |
||||
df = sdf.load(mydir / 'b14.sdf.gz') |
||||
lt = df.annotation(c) |
||||
wsim = WaveSim(c, lt, 8, strip_forks=True) |
||||
compare_to_logic_sim(wsim) |
||||
|
||||
|
||||
def test_b14_cuda(mydir): |
||||
c = verilog.load(mydir / 'b14.v.gz', branchforks=True) |
||||
df = sdf.load(mydir / 'b14.sdf.gz') |
||||
lt = df.annotation(c) |
||||
wsim = WaveSimCuda(c, lt, 8) |
||||
compare_to_logic_sim(wsim) |
||||
tests = rng.choice(choices, (wsim.s_len, wsim.sims)) |
||||
|
||||
wsim.s[0] = (tests & 2) >> 1 |
||||
wsim.s[3] = (tests & 2) >> 1 |
||||
wsim.s[1] = 0.0 |
||||
wsim.s[2] = tests & 1 |
||||
wsim.s[6] = tests & 1 |
||||
|
||||
wsim.s_to_c() |
||||
wsim.c_prop() |
||||
wsim.c_to_s() |
||||
|
||||
resp = np.array(wsim.s[6], dtype=np.uint8) | (np.array(wsim.s[3], dtype=np.uint8)<<1) |
||||
resp |= ((resp ^ (resp >> 1)) & 1) << 2 # transitions |
||||
resp[wsim.pi_s_locs] = logic.UNASSIGNED |
||||
|
||||
lsim = LogicSim(wsim.circuit, tests.shape[-1]) |
||||
lsim.s[0] = logic.mv_to_bp(tests) |
||||
lsim.s_to_c() |
||||
lsim.c_prop() |
||||
lsim.c_to_s() |
||||
exp = logic.bp_to_mv(lsim.s[1]) |
||||
|
||||
resp[resp == logic.PPULSE] = logic.ZERO |
||||
resp[resp == logic.NPULSE] = logic.ONE |
||||
|
||||
exp[exp == logic.PPULSE] = logic.ZERO |
||||
exp[exp == logic.NPULSE] = logic.ONE |
||||
|
||||
np.testing.assert_allclose(resp, exp) |
||||
|
||||
|
||||
def test_b15(b15_2ig_circuit, b15_2ig_delays): |
||||
compare_to_logic_sim(WaveSim(b15_2ig_circuit, b15_2ig_delays, 8)) |
||||
|
||||
|
||||
def test_b15_strip_forks(b15_2ig_circuit, b15_2ig_delays): |
||||
compare_to_logic_sim(WaveSim(b15_2ig_circuit, b15_2ig_delays, 8, strip_forks=True)) |
||||
|
||||
|
||||
def test_b15_cuda(b15_2ig_circuit, b15_2ig_delays): |
||||
compare_to_logic_sim(WaveSimCuda(b15_2ig_circuit, b15_2ig_delays, 8, strip_forks=True)) |
||||
|
Loading…
Reference in new issue