Compare commits

..

2 Commits
devel ... main

Author SHA1 Message Date
Stefan Holst 3193df726f need 3.12 for generic types 6 months ago
Stefan Holst d247115744 for release 0.0.6 6 months ago
  1. 4
      docs/conf.py
  2. 2
      pyproject.toml
  3. 4
      src/kyupy/bench.py
  4. 26
      src/kyupy/circuit.py
  5. 52
      src/kyupy/logic.py
  6. 2
      src/kyupy/logic_sim.py
  7. 4
      src/kyupy/sdf.py
  8. 7
      src/kyupy/sim.py
  9. 204
      src/kyupy/techlib.py
  10. 218
      src/kyupy/vcd.py
  11. 1
      src/kyupy/verilog.py
  12. 2
      src/kyupy/wave_sim.py

4
docs/conf.py

@ -20,11 +20,11 @@ sys.path.insert(0, os.path.abspath('../src')) @@ -20,11 +20,11 @@ sys.path.insert(0, os.path.abspath('../src'))
# -- Project information -----------------------------------------------------
project = 'KyuPy'
copyright = '2020-2026, Stefan Holst'
copyright = '2020-2025, Stefan Holst'
author = 'Stefan Holst'
# The full version, including alpha/beta/rc tags
release = '0.0.7'
release = '0.0.6'
# -- General configuration ---------------------------------------------------

2
pyproject.toml

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
[project]
name = "kyupy"
version = "0.0.7"
version = "0.0.6"
authors = [
{ name="Stefan Holst", email="mail@s-holst.de" },
]

4
src/kyupy/bench.py

@ -5,10 +5,6 @@ Historically it was first used in the @@ -5,10 +5,6 @@ Historically it was first used in the
`ISCAS89 benchmark set <https://people.engr.ncsu.edu/brglez/CBL/benchmarks/ISCAS89/>`_.
Besides loading these benchmarks, this module is also useful for easily constructing simple circuits:
``c = bench.parse('input(x, y) output(a, o, n) a=and(x,y) o=or(x,y) n=not(x)')``.
This parser automatically translates gate to simulation primitives supported by kyupy.
In most cases this means just adding the number of inputs to the name: e.g. NAND -> NAND2, OR -> OR4, INV -> INV1.
Since simulators only support at most four-input-gates, gates with more than four inputs are implemented as tree.
"""
from lark import Lark, Transformer

26
src/kyupy/circuit.py

@ -448,7 +448,7 @@ class Circuit: @@ -448,7 +448,7 @@ class Circuit:
node_map[n] = Node(self, f'{node.name}~{n.name}', n.kind)
elif len(n.outs) > 0 and len(n.ins) > 0: # output is also read by impl. circuit, need to add a fork.
node_map[n] = Node(self, f'{node.name}~{n.name}')
elif len(n.ins) == 0 and len(n.outs) != 1: # input is read by multiple nodes (or no nodes), need to add fork.
elif len(n.ins) == 0 and len(n.outs) > 1: # input is read by multiple nodes, need to add fork.
node_map[n] = Node(self, f'{node.name}~{n.name}')
for l in impl.lines: # add all internal lines to main circuit
if l.reader in node_map and l.driver in node_map:
@ -614,23 +614,19 @@ class Circuit: @@ -614,23 +614,19 @@ class Circuit:
if marks[n]:
yield n
def fanout(self, origin_nodes: list[Node], node_filter = lambda n: 'dff' not in n.name.lower()):
def fanout(self, origin_nodes):
"""Generator function to iterate over the fan-out cone of a given list of origin nodes.
origin_nodes are yielded first, followed by nodes driven by them in a breadth-first manner.
The search stops at nodes for which node_filter returns False.
Only origin_nodes and nodes for which node_filter returned True are yielded.
By default, search stops at flip-flops.
Nodes are yielded in topological order.
"""
queue = deque(origin_nodes)
yielded = set()
while len(queue) > 0:
n = queue.popleft()
for line in n.outs.without_nones():
succ = line.reader
if succ not in yielded and node_filter(succ):
yielded.add(succ)
queue.append(succ)
marks = [False] * len(self.nodes)
for n in origin_nodes:
marks[n] = True
for n in self.topological_order():
if not marks[n]:
for line in n.ins.without_nones():
marks[n] |= marks[line.driver]
if marks[n]:
yield n
def fanout_free_regions(self):

52
src/kyupy/logic.py

@ -81,29 +81,6 @@ on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``. @@ -81,29 +81,6 @@ on a signal. ``'N'``, ``'n'``, and ``'v'`` are interpreted as ``NPULSE``.
"""
# 0 X - 1 P R F N
_mv_xor = np.array([[0,1,1,3,4,5,6,7], # 0
[1,1,1,1,1,1,1,1], # X
[1,1,1,1,1,1,1,1], # -
[3,1,1,0,7,6,5,4], # 1
[4,1,1,7,4,5,6,7], # P
[5,1,1,6,5,4,7,6], # R
[6,1,1,5,6,7,4,5], # F
[7,1,1,4,7,6,5,4], # N
], dtype=np.uint8)
# 0 X - 1 P R F N
_mv_merge = np.array([[0,0,0,1,1,1,1,1], # 0
[0,1,1,3,4,5,6,7], # X
[0,1,2,3,4,5,6,7], # -
[1,3,3,3,1,1,1,1], # 1
[1,4,4,1,4,1,1,1], # P
[1,5,5,1,1,5,1,1], # R
[1,6,6,1,1,1,6,1], # F
[1,7,7,1,1,1,1,7], # N
], dtype=np.uint8)
def interpret(value):
"""Converts characters, strings, and lists of them to lists of logic constants defined above.
@ -214,26 +191,19 @@ def mv_and(x1, x2, out=None): @@ -214,26 +191,19 @@ def mv_and(x1, x2, out=None):
return out
def mv_xor(x1, x2, out=None):
"""A multi-valued XOR operator.
:param x1: A multi-valued array.
:param x2: A multi-valued array.
:param out: An optional storage destination. If None, a new multi-valued array is returned.
:return: A multi-valued array with the result.
"""
out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8)
out[...] = _mv_xor[x1, x2]
return out
def _mv_xor(out, *ins):
any_unknown = (ins[0] == UNKNOWN) | (ins[0] == UNASSIGNED)
for inp in ins[1:]: any_unknown |= (inp == UNKNOWN) | (inp == UNASSIGNED)
out[...] = ZERO
for inp in ins:
np.bitwise_xor(out, inp & 0b011, out=out)
np.bitwise_or(out, inp & 0b100, out=out)
np.putmask(out, any_unknown, UNKNOWN)
def mv_merge(x1, x2, out=None):
"""A multi-valued merge operator.
Merging identical values ``a`` and ``a`` yields ``a``.
Merging ``X`` and ``-`` yields ``X``.
Merging any value ``a`` with ``X`` or ``-`` yields ``a``.
Merging any other values ``a != b`` yields ``X``.
def mv_xor(x1, x2, out=None):
"""A multi-valued XOR operator.
:param x1: A multi-valued array.
:param x2: A multi-valued array.
@ -241,7 +211,7 @@ def mv_merge(x1, x2, out=None): @@ -241,7 +211,7 @@ def mv_merge(x1, x2, out=None):
:return: A multi-valued array with the result.
"""
out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8)
out[...] = _mv_merge[x1, x2]
_mv_xor(out, x1, x2)
return out

2
src/kyupy/logic_sim.py

@ -492,7 +492,7 @@ class LogicSim4V(sim.SimOps): @@ -492,7 +492,7 @@ class LogicSim4V(sim.SimOps):
self._full_mask = np.full(self.c.shape[-1], 255, dtype=np.uint8)
def __repr__(self):
return f'{{name: "{self.circuit.name}", sims: {self.sims}, s_len: {self.s_len}, c_bytes: {eng(self.c.nbytes)}}}'
return f'{{name: "{self.circuit.name}", sims: {self.sims}, c_bytes: {eng(self.c.nbytes)}}}'
def s_to_c(self):
"""Assigns the values from ``self.s_assign`` to the inputs of the combinational portion.

4
src/kyupy/sdf.py

@ -217,9 +217,9 @@ def parse(text): @@ -217,9 +217,9 @@ def parse(text):
return Lark(GRAMMAR, parser="lalr", transformer=SdfTransformer()).parse(text)
def load(file) -> DelayFile:
def load(file):
"""Parses the contents of ``file`` and returns a :class:`DelayFile` object.
Files with `.gz`-suffix are decompressed on-the-fly.
"""
return parse(readtext(file)) # type: ignore
return parse(readtext(file))

7
src/kyupy/sim.py

@ -191,7 +191,7 @@ class SimOps: @@ -191,7 +191,7 @@ class SimOps:
root_nodes = set([n for n in circuit.s_nodes if len(n.ins) > 0] + [n for n in circuit.nodes if len(n.outs) == 0]) # start from POs, PPOs, and any dangling nodes
readers = np.array([1 if l.reader in root_nodes else len(l.reader.outs) for l in circuit.lines], dtype=np.int32) # for ref-counting forks
level_lines = [n.ins[0] for n in root_nodes if len(n.ins) > 0 ]
level_lines = [n.ins[0] for n in root_nodes]
# FIXME: Should probably instanciate buffers for PPOs and attach DFF clocks
while len(level_lines) > 0: # traverse the circuit level-wise back towards (P)PIs
@ -330,16 +330,11 @@ class SimOps: @@ -330,16 +330,11 @@ class SimOps:
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)
"""Locations in pattern array (axis 0) of all primary inputs (excluding flip-flops)."""
self.po_s_locs = np.flatnonzero(self.c_locs[self.ppo_offset+np.arange(len(self.circuit.io_nodes))] >= 0)
"""Locations in pattern array (axis 0) of all primary outputs (excluding flip-flops)."""
self.ppio_s_locs = np.arange(len(self.circuit.io_nodes), self.s_len)
"""Locations in pattern array (axis 0) of all flip-flops. Pseudo-primary inputs (PPI) and pseudo-primary outputs (PPO) share the same location."""
self.pippi_s_locs = np.concatenate([self.pi_s_locs, self.ppio_s_locs])
"""Locations in pattern array (axis 0) of all primary and pseudo-primary inputs (input ports and flip-flops)."""
self.poppo_s_locs = np.concatenate([self.po_s_locs, self.ppio_s_locs])
"""Locations in pattern array (axis 0) of all primary and pseudo-primary outputs (output ports and flip-flops)."""
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]

204
src/kyupy/techlib.py

@ -10,6 +10,7 @@ from itertools import product @@ -10,6 +10,7 @@ from itertools import product
from . import bench
class TechLib:
"""Class for standard cell library definitions.
@ -127,7 +128,7 @@ OAI21X1 input(A0,A1,B0) output(Y) Y=OAI21(A0,A1,B0) ; @@ -127,7 +128,7 @@ OAI21X1 input(A0,A1,B0) output(Y) Y=OAI21(A0,A1,B0) ;
OAI22X1 input(A0,A1,B0,B1) output(Y) Y=OAI22(A0,A1,B0,B1) ;
OAI33X1 input(A0,A1,A2,B0,B1,B2) output(Y) AA=OR2(A0,A1) BB=OR2(B0,B1) Y=OAI22(AA,A2,BB,B2) ;
ADDFX1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
ADDHX1 input(A,B) output(CO,S) CO=AND2(A,B) S=XOR2(A,B) ;
ADDHX1 input(A,B) output(CO,S) CO=XOR2(A,B) S=AND2(A,B) ;
DFFX1 input(CK,D) output(Q,QN) Q=DFF(D,CK) QN=INV1(Q) ;
DFFSRX1 input(CK,D,RN,SN) output(Q,QN) DR=AND2(D,RN) SET=INV1(SN) DRS=OR2(DR,SET) Q=DFF(DRS,CK) QN=INV1(Q) ;
@ -171,7 +172,7 @@ XNOR2_X1 input(A1,A2) output(ZN) ZN=XNOR2(A1,A2) ; @@ -171,7 +172,7 @@ XNOR2_X1 input(A1,A2) output(ZN) ZN=XNOR2(A1,A2) ;
MUX2_X1 input(I0,I1,S) output(Z) Z=MUX21(I0,I1,S) ;
HA_X1 input(A,B) output(CO,S) CO=AND2(A,B) S=XOR2(A,B) ;
HA_X1 input(A,B) output(CO,S) CO=XOR2(A,B) S=AND2(A,B) ;
FA_X1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(CI,A,B) ;
AOI21_X{1,2} input(A1,A2,B) output(ZN) ZN=AOI21(A1,A2,B) ;
@ -224,7 +225,7 @@ XNOR2_X{1,2} input(A,B) output(ZN) ZN=XNOR2(A,B) ; @@ -224,7 +225,7 @@ XNOR2_X{1,2} input(A,B) output(ZN) ZN=XNOR2(A,B) ;
MUX2_X{1,2} input(A,B,S) output(Z) Z=MUX21(A,B,S) ;
HA_X1 input(A,B) output(CO,S) CO=AND2(A,B) S=XOR2(A,B) ;
HA_X1 input(A,B) output(CO,S) CO=XOR2(A,B) S=AND2(A,B) ;
FA_X1 input(A,B,CI) output(CO,S) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(CI,A,B) ;
AOI21_X{1,2,4} input(A,B1,B2) output(ZN) ZN=AOI21(B1,B2,A) ;
@ -339,7 +340,7 @@ MUX41X{1,2}$ input(A1,A2,A3,A4,S0,S1) output(Y) A=MUX21(A1,A2,S0) B=MUX21(A3,A4, @@ -339,7 +340,7 @@ MUX41X{1,2}$ input(A1,A2,A3,A4,S0,S1) output(Y) A=MUX21(A1,A2,S0) B=MUX21(A3,A4,
DEC24X{1,2}$ input(A0,A1) output(Y0,Y1,Y2,Y3) A0B=INV1(A0) A1B=INV1(A1) Y0=NOR2(A0,A1) Y1=AND(A0,A1B) Y2=AND(A0B,A1) Y3=AND(A0,A1) ;
FADDX{1,2}$ input(A,B,CI) output(S,CO) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=AND2(A0,B0) SO=XOR2(A0,B0) ;
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=XOR2(A0,B0) SO=AND2(A0,B0) ;
{,AO}DFFARX{1,2}$ input(D,CLK,RSTB) output(Q,QN) DR=AND2(D,RSTB) Q=DFF(DR,CLK) QN=INV1(Q) ;
DFFASRX{1,2}$ input(D,CLK,RSTB,SETB) output(Q,QN) DR=AND2(D,RSTB) SET=INV1(SETB) DRS=OR2(DR,SET) Q=DFF(DRS,CLK) QN=INV1(Q) ;
@ -429,7 +430,7 @@ MUX41X{1,2}$ input(IN1,IN2,IN3,IN4,S0,S1) output(Q) A=MUX21(IN1,IN2,S0) B=MUX21( @@ -429,7 +430,7 @@ MUX41X{1,2}$ input(IN1,IN2,IN3,IN4,S0,S1) output(Q) A=MUX21(IN1,IN2,S0) B=MUX21(
DEC24X{1,2}$ input(IN1,IN2) output(Q0,Q1,Q2,Q3) IN1B=INV1(IN1) IN2B=INV1(IN2) Q0=NOR2(IN1,IN2) Q1=AND(IN1,IN2B) Q2=AND(IN1B,IN2) Q3=AND(IN1,IN2) ;
FADDX{1,2}$ input(A,B,CI) output(S,CO) AB=XOR2(A,B) CO=XOR2(AB,CI) S=AO22(AB,CI,A,B) ;
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=AND2(A0,B0) SO=XOR2(A0,B0) ;
HADDX{1,2}$ input(A0,B0) output(SO,C1) C1=XOR2(A0,B0) SO=AND2(A0,B0) ;
{,AO}DFFARX{1,2}$ input(D,CLK,RSTB) output(Q,QN) DR=AND2(D,RSTB) Q=DFF(DR,CLK) QN=INV1(Q) ;
DFFASRX{1,2}$ input(D,CLK,RSTB,SETB) output(Q,QN) DR=AND2(D,RSTB) SET=INV1(SETB) DRS=OR2(DR,SET) Q=DFF(DRS,CLK) QN=INV1(Q) ;
@ -449,196 +450,3 @@ LATCHX{1,2}$ input(D,CLK) output(Q,QN) Q=LATCH(D,CLK) QN=INV1(Q) ; @@ -449,196 +450,3 @@ LATCHX{1,2}$ input(D,CLK) output(Q,QN) Q=LATCH(D,CLK) QN=INV1(Q) ;
"""The SAED 90nm educational technology library.
It defines all cells except: negative-edge flip-flops, tri-state, latches, clock gating, level shifters
"""
SKY130 = TechLib(r"""
$decap_{3,4,6,8,12} ;
$fill_{1,2,4,8} ;
$tap_{1,2} ;
$tapvgnd_1 ;
$tapvgnd2_1 ;
$tapvpwrvgnd_1 ;
$lpflow_decapkapwr_{3,4,6,8,12} ;
$lpflow_bleeder_1 input(SHORT) ;
$conb_1 output(HI,LO) HI=__const1__() LO=__const0__() ;
$macro_sparecell output(LO) LO=__const0__() ;
$diode_2 input(DIODE) ;
$probe_p_8 input(A) output(X) X=BUF1(A) ;
$probec_p_8 input(A) output(X) X=BUF1(A) ;
$inv_{1,2,4,6,8,12,16} input(A) output(Y) Y=INV1(A) ;
$buf_{1,2,4,6,8,12,16} input(A) output(X) X=BUF1(A) ;
$bufbuf_{8,16} input(A) output(X) X=BUF1(A) ;
$bufinv_{8,16} input(A) output(Y) Y=INV1(A) ;
$clkbuf_{1,2,4,8,16} input(A) output(X) X=BUF1(A) ;
$clkinv_{1,2,4,8,16} input(A) output(Y) Y=INV1(A) ;
$clkinvlp_{2,4} input(A) output(Y) Y=INV1(A) ;
$clkdlybuf4s15_{1,2} input(A) output(X) X=BUF1(A) ;
$clkdlybuf4s18_{1,2} input(A) output(X) X=BUF1(A) ;
$clkdlybuf4s25_{1,2} input(A) output(X) X=BUF1(A) ;
$clkdlybuf4s50_{1,2} input(A) output(X) X=BUF1(A) ;
$dlygate4sd1_1 input(A) output(X) X=BUF1(A) ;
$dlygate4sd2_1 input(A) output(X) X=BUF1(A) ;
$dlygate4sd3_1 input(A) output(X) X=BUF1(A) ;
$dlymetal6s2s_1 input(A) output(X) X=BUF1(A) ;
$dlymetal6s4s_1 input(A) output(X) X=BUF1(A) ;
$dlymetal6s6s_1 input(A) output(X) X=BUF1(A) ;
$lpflow_clkbufkapwr_{1,2,4,8,16} input(A) output(X) X=BUF1(A) ;
$lpflow_clkinvkapwr_{1,2,4,8,16} input(A) output(Y) Y=INV1(A) ;
$lpflow_lsbuf_lh_hl_isowell_tap_{1,2,4} input(A) output(X) X=BUF1(A) ;
$lpflow_lsbuf_lh_isowell_4 input(A) output(X) X=BUF1(A) ;
$lpflow_lsbuf_lh_isowell_tap_{1,2,4} input(A) output(X) X=BUF1(A) ;
$and2_{0,1,2,4} input(A,B) output(X) X=AND2(A,B) ;
$and2b_{1,2,4} input(A_N,B) output(X) AN=INV1(A_N) X=AND2(AN,B) ;
$and3_{1,2,4} input(A,B,C) output(X) X=AND3(A,B,C) ;
$and3b_{1,2,4} input(A_N,B,C) output(X) AN=INV1(A_N) X=AND3(AN,B,C) ;
$and4_{1,2,4} input(A,B,C,D) output(X) X=AND4(A,B,C,D) ;
$and4b_{1,2,4} input(A_N,B,C,D) output(X) AN=INV1(A_N) X=AND4(AN,B,C,D) ;
$and4bb_{1,2,4} input(A_N,B_N,C,D) output(X) AN=INV1(A_N) BN=INV1(B_N) X=AND4(AN,BN,C,D) ;
$or2_{0,1,2,4} input(A,B) output(X) X=OR2(A,B) ;
$or2b_{1,2,4} input(A,B_N) output(X) BN=INV1(B_N) X=OR2(A,BN) ;
$or3_{1,2,4} input(A,B,C) output(X) X=OR3(A,B,C) ;
$or3b_{1,2,4} input(A,B,C_N) output(X) CN=INV1(C_N) X=OR3(A,B,CN) ;
$or4_{1,2,4} input(A,B,C,D) output(X) X=OR4(A,B,C,D) ;
$or4b_{1,2,4} input(A,B,C,D_N) output(X) DN=INV1(D_N) X=OR4(A,B,C,DN) ;
$or4bb_{1,2,4} input(A,B,C_N,D_N) output(X) CN=INV1(C_N) DN=INV1(D_N) X=OR4(A,B,CN,DN) ;
$nand2_{1,2,4,8} input(A,B) output(Y) Y=NAND2(A,B) ;
$nand2b_{1,2,4} input(A_N,B) output(Y) AN=INV1(A_N) Y=NAND2(AN,B) ;
$nand3_{1,2,4} input(A,B,C) output(Y) Y=NAND3(A,B,C) ;
$nand3b_{1,2,4} input(A_N,B,C) output(Y) AN=INV1(A_N) Y=NAND3(AN,B,C) ;
$nand4_{1,2,4} input(A,B,C,D) output(Y) Y=NAND4(A,B,C,D) ;
$nand4b_{1,2,4} input(A_N,B,C,D) output(Y) AN=INV1(A_N) Y=NAND4(AN,B,C,D) ;
$nand4bb_{1,2,4} input(A_N,B_N,C,D) output(Y) AN=INV1(A_N) BN=INV1(B_N) Y=NAND4(AN,BN,C,D) ;
$nor2_{1,2,4,8} input(A,B) output(Y) Y=NOR2(A,B) ;
$nor2b_{1,2,4} input(A,B_N) output(Y) BN=INV1(B_N) Y=NOR2(A,BN) ;
$nor3_{1,2,4} input(A,B,C) output(Y) Y=NOR3(A,B,C) ;
$nor3b_{1,2,4} input(A,B,C_N) output(Y) CN=INV1(C_N) Y=NOR3(A,B,CN) ;
$nor4_{1,2,4} input(A,B,C,D) output(Y) Y=NOR4(A,B,C,D) ;
$nor4b_{1,2,4} input(A,B,C,D_N) output(Y) DN=INV1(D_N) Y=NOR4(A,B,C,DN) ;
$nor4bb_{1,2,4} input(A,B,C_N,D_N) output(Y) CN=INV1(C_N) DN=INV1(D_N) Y=NOR4(A,B,CN,DN) ;
$xor2_{1,2,4} input(A,B) output(X) X=XOR2(A,B) ;
$xor3_{1,2,4} input(A,B,C) output(X) X=XOR3(A,B,C) ;
$xnor2_{1,2,4} input(A,B) output(Y) Y=XNOR2(A,B) ;
$xnor3_{1,2,4} input(A,B,C) output(X) X=XNOR3(A,B,C) ;
$maj3_{1,2,4} input(A,B,C) output(X) AB=AND2(A,B) BC=AND2(B,C) AC=AND2(A,C) X=OR3(AB,BC,AC) ;
$mux2_{1,2,4,8} input(A0,A1,S) output(X) X=MUX21(A0,A1,S) ;
$mux2i_{1,2,4} input(A0,A1,S) output(Y) M=MUX21(A0,A1,S) Y=INV1(M) ;
$mux4_{1,2,4} input(A0,A1,A2,A3,S0,S1) output(X) M0=MUX21(A0,A1,S0) M1=MUX21(A2,A3,S0) X=MUX21(M0,M1,S1) ;
$ha_{1,2,4} input(A,B) output(COUT,SUM) SUM=XOR2(A,B) COUT=AND2(A,B) ;
$fa_{1,2,4} input(A,B,CIN) output(COUT,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CIN) COUT=AO22(A,B,AB,CIN) ;
$fah_1 input(A,B,CI) output(COUT,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CI) COUT=AO22(A,B,AB,CI) ;
$fahcin_1 input(A,B,CIN) output(COUT,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CIN) COUT=AO22(A,B,AB,CIN) ;
$fahcon_1 input(A,B,CI) output(COUT_N,SUM) AB=XOR2(A,B) SUM=XOR2(AB,CI) T=AO22(A,B,AB,CI) COUT_N=INV1(T) ;
$a21o_{1,2,4} input(A1,A2,B1) output(X) X=AO21(A1,A2,B1) ;
$a21bo_{1,2,4} input(A1,A2,B1_N) output(X) BN=INV1(B1_N) X=AO21(A1,A2,BN) ;
$a22o_{1,2,4} input(A1,A2,B1,B2) output(X) X=AO22(A1,A2,B1,B2) ;
$a2bb2o_{1,2,4} input(A1_N,A2_N,B1,B2) output(X) AN1=INV1(A1_N) AN2=INV1(A2_N) X=AO22(AN1,AN2,B1,B2) ;
$a211o_{1,2,4} input(A1,A2,B1,C1) output(X) X=AO211(A1,A2,B1,C1) ;
$a221o_{1,2,4} input(A1,A2,B1,B2,C1) output(X) T=AO22(A1,A2,B1,B2) X=OR2(T,C1) ;
$a31o_{1,2,4} input(A1,A2,A3,B1) output(X) AA=AND3(A1,A2,A3) X=OR2(AA,B1) ;
$a311o_{1,2,4} input(A1,A2,A3,B1,C1) output(X) T=AND2(A1,A2) X=AO211(T,A3,B1,C1) ;
$a32o_{1,2,4} input(A1,A2,A3,B1,B2) output(X) T=AND2(A1,A2) X=AO22(T,A3,B1,B2) ;
$a41o_{1,2,4} input(A1,A2,A3,A4,B1) output(X) T1=AND2(A1,A2) T2=AND2(T1,A3) X=AO21(T2,A4,B1) ;
$a2111o_{1,2,4} input(A1,A2,B1,C1,D1) output(X) T=AO211(A1,A2,B1,C1) X=OR2(T,D1) ;
$a21oi_{1,2,4} input(A1,A2,B1) output(Y) Y=AOI21(A1,A2,B1) ;
$a21boi_{0,1,2,4} input(A1,A2,B1_N) output(Y) BN=INV1(B1_N) Y=AOI21(A1,A2,BN) ;
$a22oi_{1,2,4} input(A1,A2,B1,B2) output(Y) Y=AOI22(A1,A2,B1,B2) ;
$a2bb2oi_{1,2,4} input(A1_N,A2_N,B1,B2) output(Y) AN1=INV1(A1_N) AN2=INV1(A2_N) Y=AOI22(AN1,AN2,B1,B2) ;
$a211oi_{1,2,4} input(A1,A2,B1,C1) output(Y) Y=AOI211(A1,A2,B1,C1) ;
$a221oi_{1,2,4} input(A1,A2,B1,B2,C1) output(Y) T=AO22(A1,A2,B1,B2) Y=NOR2(T,C1) ;
$a222oi_1 input(A1,A2,B1,B2,C1,C2) output(Y) AB=AO22(A1,A2,B1,B2) Y=AOI21(C1,C2,AB) ;
$a31oi_{1,2,4} input(A1,A2,A3,B1) output(Y) AA=AND3(A1,A2,A3) Y=NOR2(AA,B1) ;
$a311oi_{1,2,4} input(A1,A2,A3,B1,C1) output(Y) T=AND2(A1,A2) Y=AOI211(T,A3,B1,C1) ;
$a32oi_{1,2,4} input(A1,A2,A3,B1,B2) output(Y) T=AND2(A1,A2) Y=AOI22(T,A3,B1,B2) ;
$a41oi_{1,2,4} input(A1,A2,A3,A4,B1) output(Y) T1=AND2(A1,A2) T2=AND2(T1,A3) Y=AOI21(T2,A4,B1) ;
$a2111oi_{0,1,2,4} input(A1,A2,B1,C1,D1) output(Y) T=AO211(A1,A2,B1,C1) Y=NOR2(T,D1) ;
$o21a_{1,2,4} input(A1,A2,B1) output(X) X=OA21(A1,A2,B1) ;
$o21ba_{1,2,4} input(A1,A2,B1_N) output(X) BN=INV1(B1_N) X=OA21(A1,A2,BN) ;
$o22a_{1,2,4} input(A1,A2,B1,B2) output(X) X=OA22(A1,A2,B1,B2) ;
$o2bb2a_{1,2,4} input(A1_N,A2_N,B1,B2) output(X) T=NAND2(A1_N,A2_N) X=OA21(B1,B2,T) ;
$o211a_{1,2,4} input(A1,A2,B1,C1) output(X) X=OA211(A1,A2,B1,C1) ;
$o221a_{1,2,4} input(A1,A2,B1,B2,C1) output(X) T=OA22(A1,A2,B1,B2) X=AND2(T,C1) ;
$o31a_{1,2,4} input(A1,A2,A3,B1) output(X) T=OR3(A1,A2,A3) X=AND2(T,B1) ;
$o311a_{1,2,4} input(A1,A2,A3,B1,C1) output(X) T=OR2(A1,A2) X=OA211(T,A3,B1,C1) ;
$o32a_{1,2,4} input(A1,A2,A3,B1,B2) output(X) T=OR3(A1,A2,A3) X=OA21(B1,B2,T) ;
$o41a_{1,2,4} input(A1,A2,A3,A4,B1) output(X) T=OR4(A1,A2,A3,A4) X=AND2(T,B1) ;
$o2111a_{1,2,4} input(A1,A2,B1,C1,D1) output(X) T=OA211(A1,A2,B1,C1) X=AND2(T,D1) ;
$o21ai_{0,1,2,4} input(A1,A2,B1) output(Y) Y=OAI21(A1,A2,B1) ;
$o21bai_{1,2,4} input(A1,A2,B1_N) output(Y) BN=INV1(B1_N) Y=OAI21(A1,A2,BN) ;
$o22ai_{1,2,4} input(A1,A2,B1,B2) output(Y) Y=OAI22(A1,A2,B1,B2) ;
$o2bb2ai_{1,2,4} input(A1_N,A2_N,B1,B2) output(Y) T=NAND2(A1_N,A2_N) Y=OAI21(B1,B2,T) ;
$o211ai_{1,2,4} input(A1,A2,B1,C1) output(Y) Y=OAI211(A1,A2,B1,C1) ;
$o221ai_{1,2,4} input(A1,A2,B1,B2,C1) output(Y) T=OA22(A1,A2,B1,B2) Y=NAND2(T,C1) ;
$o31ai_{1,2,4} input(A1,A2,A3,B1) output(Y) T=OR3(A1,A2,A3) Y=NAND2(T,B1) ;
$o311ai_{0,1,2,4} input(A1,A2,A3,B1,C1) output(Y) T=OR2(A1,A2) Y=OAI211(T,A3,B1,C1) ;
$o32ai_{1,2,4} input(A1,A2,A3,B1,B2) output(Y) T=OR3(A1,A2,A3) Y=OAI21(B1,B2,T) ;
$o41ai_{1,2,4} input(A1,A2,A3,A4,B1) output(Y) T=OR4(A1,A2,A3,A4) Y=NAND2(T,B1) ;
$o2111ai_{1,2,4} input(A1,A2,B1,C1,D1) output(Y) T=OA211(A1,A2,B1,C1) Y=NAND2(T,D1) ;
$dfxtp_{1,2,4} input(CLK,D) output(Q) Q=DFF(D,CLK) ;
$dfxbp_{1,2} input(CLK,D) output(Q,Q_N) Q=DFF(D,CLK) Q_N=INV1(Q) ;
$dfrtp_{1,2,4} input(CLK,D,RESET_B) output(Q) DR=AND2(D,RESET_B) Q=DFF(DR,CLK) ;
$dfrbp_{1,2} input(CLK,D,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) Q=DFF(DR,CLK) Q_N=INV1(Q) ;
$dfstp_{1,2,4} input(CLK,D,SET_B) output(Q) S=INV1(SET_B) DS=OR2(D,S) Q=DFF(DS,CLK) ;
$dfsbp_{1,2} input(CLK,D,SET_B) output(Q,Q_N) S=INV1(SET_B) DS=OR2(D,S) Q=DFF(DS,CLK) Q_N=INV1(Q) ;
$dfbbp_1 input(CLK,D,SET_B,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) Q=DFF(DRS,CLK) Q_N=INV1(Q) ;
$dfrtn_1 input(CLK_N,D,RESET_B) output(Q) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) Q=DFF(DR,CLKN) ;
$dfbbn_{1,2} input(CLK_N,D,SET_B,RESET_B) output(Q,Q_N) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) Q=DFF(DRS,CLKN) Q_N=INV1(Q) ;
$sdfxtp_{1,2,4} input(CLK,D,SCD,SCE) output(Q) DI=MUX21(D,SCD,SCE) Q=DFF(DI,CLK) ;
$sdfxbp_{1,2} input(CLK,D,SCD,SCE) output(Q,Q_N) DI=MUX21(D,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
$sdfrtp_{1,2,4} input(CLK,D,SCD,SCE,RESET_B) output(Q) DR=AND2(D,RESET_B) DI=MUX21(DR,SCD,SCE) Q=DFF(DI,CLK) ;
$sdfrbp_{1,2} input(CLK,D,SCD,SCE,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) DI=MUX21(DR,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
$sdfstp_{1,2,4} input(CLK,D,SCD,SCE,SET_B) output(Q) S=INV1(SET_B) DS=OR2(D,S) DI=MUX21(DS,SCD,SCE) Q=DFF(DI,CLK) ;
$sdfsbp_{1,2} input(CLK,D,SCD,SCE,SET_B) output(Q,Q_N) S=INV1(SET_B) DS=OR2(D,S) DI=MUX21(DS,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
$sdfbbp_1 input(CLK,D,SCD,SCE,SET_B,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) DI=MUX21(DRS,SCD,SCE) Q=DFF(DI,CLK) Q_N=INV1(Q) ;
$sdfrtn_1 input(CLK_N,D,SCD,SCE,RESET_B) output(Q) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) DI=MUX21(DR,SCD,SCE) Q=DFF(DI,CLKN) ;
$sdfbbn_{1,2} input(CLK_N,D,SCD,SCE,SET_B,RESET_B) output(Q,Q_N) CLKN=INV1(CLK_N) DR=AND2(D,RESET_B) S=INV1(SET_B) DRS=OR2(DR,S) DI=MUX21(DRS,SCD,SCE) Q=DFF(DI,CLKN) Q_N=INV1(Q) ;
$edfxtp_1 input(CLK,D,DE) output(Q) GCK=AND2(CLK,DE) Q=DFF(D,GCK) ;
$edfxbp_1 input(CLK,D,DE) output(Q,Q_N) GCK=AND2(CLK,DE) Q=DFF(D,GCK) Q_N=INV1(Q) ;
$sedfxtp_{1,2,4} input(CLK,D,DE,SCD,SCE) output(Q) DI=MUX21(D,SCD,SCE) GCK=AND2(CLK,DE) Q=DFF(DI,GCK) ;
$sedfxbp_{1,2} input(CLK,D,DE,SCD,SCE) output(Q,Q_N) DI=MUX21(D,SCD,SCE) GCK=AND2(CLK,DE) Q=DFF(DI,GCK) Q_N=INV1(Q) ;
$dlxtp_1 input(D,GATE) output(Q) Q=LATCH(D,GATE) ;
$dlxtn_{1,2,4} input(D,GATE_N) output(Q) GN=INV1(GATE_N) Q=LATCH(D,GN) ;
$dlxbp_1 input(D,GATE) output(Q,Q_N) Q=LATCH(D,GATE) Q_N=INV1(Q) ;
$dlxbn_{1,2} input(D,GATE_N) output(Q,Q_N) GN=INV1(GATE_N) Q=LATCH(D,GN) Q_N=INV1(Q) ;
$dlrtp_{1,2,4} input(D,GATE,RESET_B) output(Q) DR=AND2(D,RESET_B) Q=LATCH(DR,GATE) ;
$dlrtn_{1,2,4} input(D,GATE_N,RESET_B) output(Q) GN=INV1(GATE_N) DR=AND2(D,RESET_B) Q=LATCH(DR,GN) ;
$dlrbp_{1,2} input(D,GATE,RESET_B) output(Q,Q_N) DR=AND2(D,RESET_B) Q=LATCH(DR,GATE) Q_N=INV1(Q) ;
$dlrbn_{1,2} input(D,GATE_N,RESET_B) output(Q,Q_N) GN=INV1(GATE_N) DR=AND2(D,RESET_B) Q=LATCH(DR,GN) Q_N=INV1(Q) ;
$dlclkp_{1,2,4} input(CLK,GATE) output(GCLK) GCLK=AND2(CLK,GATE) ;
$sdlclkp_{1,2,4} input(CLK,GATE,SCE) output(GCLK) G=OR2(GATE,SCE) GCLK=AND2(CLK,G) ;
$ebufn_{1,2,4,8} input(A,TE_B) output(Z) TE=INV1(TE_B) Z=AND2(A,TE) ;
$einvn_{0,1,2,4,8} input(A,TE_B) output(Z) TE=INV1(TE_B) ZI=INV1(A) Z=AND2(ZI,TE) ;
$einvp_{1,2,4,8} input(A,TE) output(Z) ZI=INV1(A) Z=AND2(ZI,TE) ;
$lpflow_inputiso0n_1 input(A,SLEEP_B) output(X) X=AND2(A,SLEEP_B) ;
$lpflow_inputiso0p_1 input(A,SLEEP) output(X) SB=INV1(SLEEP) X=AND2(A,SB) ;
$lpflow_inputiso1n_1 input(A,SLEEP_B) output(X) X=AND2(A,SLEEP_B) ;
$lpflow_inputiso1p_1 input(A,SLEEP) output(X) SB=INV1(SLEEP) X=AND2(A,SB) ;
$lpflow_isobufsrc_{1,2,4,8,16} input(A,SLEEP) output(X) AI=INV1(A) X=OR2(AI,SLEEP) ;
$lpflow_isobufsrckapwr_16 input(A,SLEEP) output(X) AI=INV1(A) X=OR2(AI,SLEEP) ;
$lpflow_inputisolatch_1 input(D,SLEEP_B) output(Q) Q=LATCH(D,SLEEP_B) ;
""".replace('$','sky130_fd_sc_hd__'))
"""SkyWater 130nm High Density Digital Standard Cells (skywater-pdk-libs-sky130_fd_sc_hd).
"""
techlib_by_name = {k: v for k, v in globals().items() if isinstance(v, TechLib)}

218
src/kyupy/vcd.py

@ -1,218 +0,0 @@ @@ -1,218 +0,0 @@
"""A simple parser for Verilog Change Dump (VCD) files.
This parser loads the changes into a ndarray of logic values.
Axis 0 is the time step, axis 1 is the variable data.
All variable values are flattened. Offsets are stored in each variable metadata.
"""
from collections import namedtuple
from dataclasses import dataclass, field
from lark import Lark, Transformer
import numpy as np
from . import readtext, logic
@dataclass
class Var:
type: str
width: int
idcode: str
reference: str
scope: 'Scope'
locs: list[int] = field(default_factory=list)
class Scope:
def __init__(self, parent, name='', type='module') -> None:
self.name = name
self.type = type
self.parent_scope = parent
self.sub_scopes : list[Scope] = []
self.vars : list = []
@property
def path(self) -> str:
parts = []
s = self
while s is not None and s.name:
parts.append(s.name)
s = s.parent_scope
return '/'.join(reversed(parts))
def __repr__(self) -> str:
return self.path
class VcdHeader:
def __init__(self) -> None:
self.comment: str = ''
self.date: str = ''
self.timescale: str = ''
self.version: str = ''
self.root_scope = Scope(None)
self.current_scope = self.root_scope
def scope(self, name, type):
new_scope = Scope(self.current_scope, name, type)
self.current_scope.sub_scopes.append(new_scope)
self.current_scope = new_scope
def upscope(self):
assert self.current_scope.parent_scope is not None, "root scope has no parent"
self.current_scope = self.current_scope.parent_scope
def add_var(self, type: str, width: int, idcode: str, reference: str):
self.current_scope.vars.append(Var(type, width, idcode, reference, self.current_scope))
def __repr__(self):
lines = [
'VcdHeader(',
f' comment = {self.comment!r}',
f' date = {self.date!r}',
f' timescale = {self.timescale!r}',
f' version = {self.version!r}',
' scopes:',
]
def fmt_scope(scope, indent=4):
lines.append(f'{" " * (indent // 2)}{scope.type}:{scope.name} ({len(scope.vars)} vars)')
for sub in scope.sub_scopes:
fmt_scope(sub, indent + 2)
for sub in self.root_scope.sub_scopes:
fmt_scope(sub)
lines.append(')')
return '\n'.join(lines)
class VcdVarMap:
def __init__(self, header: VcdHeader, var_locs = lambda _: []) -> None:
self.var_list: list[Var] = []
def collect(scope):
for v in scope.vars:
v.locs = var_locs(v)
if v.locs is not None and len(v.locs) > 0:
assert len(v.locs) == v.width, f'{v.reference}: len(locs)={len(v.locs)} != width={v.width}'
self.var_list.append(v)
for sub in scope.sub_scopes:
collect(sub)
collect(header.root_scope)
self.total_width = max((max(v.locs) for v in self.var_list), default=-1) + 1
self.idcode2var = {var.idcode: var for var in self.var_list}
class VcdData:
def __init__(self, var_map: VcdVarMap, steps, data):
self.var_map = var_map
self.steps = steps
self.data = data
def __repr__(self) -> str:
return f'{{variables: {len(self.var_map.var_list)}, bits: {self.data.shape[0]}, steps: {self.data.shape[1]}}}'
class VcdHeaderTransformer(Transformer):
def __init__(self):
super().__init__()
self.header = VcdHeader()
def comment(self, args): self.header.comment = args[0].value.strip()
def date(self, args): self.header.date = args[0].value.strip()
def timescale(self, args): self.header.timescale = args[0].value.strip()
def version(self, args): self.header.version = args[0].value.strip()
def scope(self, args):
type, name = args
self.header.scope(name.value, type.value)
def upscope(self, args):
self.header.upscope()
def var(self, args):
type, width, idcode, reference = args
reference = reference.value.strip()
width = int(width)
self.header.add_var(type.value, width, idcode.value, reference)
def start(self, args):
return self.header
#return tuple(args) if len(args) > 1 else (args[0], None)
GRAMMAR = r"""
start: (_declaration_command)*
_declaration_command: ( comment | date | scope | timescale | upscope | var | version ) "$end"
comment: "$comment" TEXT?
date: "$date" TEXT
scope: "$scope" /[^\s]+/ /[^\s]+/
timescale: "$timescale" TEXT
upscope: "$upscope"
var: "$var" /[^\s]+/ /[^\s]+/ /[^\s]+/ TEXT
version: "$version" TEXT
TEXT: /(?:(?!\$end)[\s\S])+/
%ignore ( /\r?\n/ )+
%ignore /[\t \f]+/
"""
def load(file, var_locs = lambda _: [], step_filter = lambda *_: True, s_len = None) -> VcdData:
"""Parses the contents of ``file`` as Verilog Change Dump (VCD).
:param file: A file name or a file handle. Files with `.gz`-suffix are decompressed on-the-fly.
:param var_locs: A callback ``(var) -> list[int]`` mapping each variable to ndarray column indices. Empty list drops the variable.
:param step_filter: A callback ``(time, values, var_map) -> bool`` to select which timesteps to include.
:param s_len: length of the first axis of returned pattern data.
:return: A VcdData object with metadata and an ndarray with all values.
"""
vcd = readtext(file)
header_size = vcd.find('$enddefinitions')
assert header_size > 0, "invalid VCD file: end of header not found"
vcd_header_str = vcd[:header_size]
vcd_header : VcdHeader = Lark(GRAMMAR, parser="lalr", lexer='contextual', transformer=VcdHeaderTransformer()).parse(vcd_header_str) # type: ignore
vcd_data = vcd[header_size:].splitlines()
var_map = VcdVarMap(vcd_header, var_locs)
width = s_len if s_len is not None else var_map.total_width
chunk_size = 10240
chunks = []
chunk = np.full((chunk_size, width), logic.UNASSIGNED, dtype=np.uint8)
_val_map = {'0': logic.ZERO, '1': logic.ONE,
'x': logic.UNKNOWN, 'X': logic.UNKNOWN,
'z': logic.UNASSIGNED, 'Z': logic.UNASSIGNED}
_pad_char = {'0': '0', '1': '0', 'x': 'x', 'X': 'x', 'z': 'z', 'Z': 'z'}
current_time = None
steps = []
step_idx = 0
for line in vcd_data:
if not line or line[0] == '$':
continue
if line[0] == '#':
if step_filter(current_time, chunk[step_idx], var_map):
step_idx += 1
steps.append(current_time)
if step_idx >= chunk_size:
chunks.append(chunk)
chunk = np.empty((chunk_size, width), dtype=np.uint8)
chunk[0] = chunks[-1][-1]
step_idx = 0
else:
chunk[step_idx] = chunk[step_idx - 1]
current_time = int(line[1:])
continue
if line[0] in ('b', 'B'):
value_str, idcode = line[1:].split(None, 1)
else:
value_str, idcode = line[0], line[1:]
var = var_map.idcode2var.get(idcode)
if var is None:
continue
if len(value_str) < var.width:
value_str = _pad_char.get(value_str[0], '0') * (var.width - len(value_str)) + value_str
for i, c in enumerate(value_str):
if var.locs[i] >= 0:
chunk[step_idx, var.locs[i]] = _val_map.get(c, logic.UNKNOWN)
chunks.append(chunk[:step_idx])
data = np.concatenate(chunks, axis=0).T
return VcdData(var_map, steps, data)

1
src/kyupy/verilog.py

@ -52,7 +52,6 @@ class VerilogTransformer(Transformer): @@ -52,7 +52,6 @@ class VerilogTransformer(Transformer):
@staticmethod
def instantiation(args):
pinmap = {}
if args[2] is not None:
for idx, pin in enumerate(args[2:]):
p = pin.children[0]
if isinstance(p, tuple): # named pin

2
src/kyupy/wave_sim.py

@ -434,7 +434,7 @@ class WaveSimCuda(WaveSim): @@ -434,7 +434,7 @@ class WaveSimCuda(WaveSim):
grid_dim = self._grid_dim(self.sims, self.s_len)
wave_assign_gpu[grid_dim, self._block_dim](self.c, self.s, self.c_locs, self.ppi_offset)
def _grid_dim(self, x, y): return cdiv(int(x), self._block_dim[0]), cdiv(int(y), self._block_dim[1])
def _grid_dim(self, x, y): return cdiv(x, self._block_dim[0]), cdiv(y, self._block_dim[1])
def c_prop(self, sims=None, seed=1, op_from=0, op_to=None, delta=0):
sims = min(sims or self.sims, self.sims)

Loading…
Cancel
Save