@ -10,20 +10,35 @@ Circuit graphs also define an ordering of inputs, outputs and other nodes to eas
@@ -10,20 +10,35 @@ Circuit graphs also define an ordering of inputs, outputs and other nodes to eas
"""
from __future__ import annotations
from collections import deque , defaultdict
import re
from typing import Union
import numpy as np
class GrowingList ( list ) :
def __setitem__ ( self , index , value ) :
if index > = len ( self ) :
self . extend ( [ None ] * ( index + 1 - len ( self ) ) )
if value is None : self . has_nones = True
if index == len ( self ) : return super ( ) . append ( value )
if index > len ( self ) :
super ( ) . extend ( [ None ] * ( index + 1 - len ( self ) ) )
self . has_nones = True
super ( ) . __setitem__ ( index , value )
def free_index ( self ) :
return next ( ( i for i , x in enumerate ( self ) if x is None ) , len ( self ) )
def __getitem__ ( self , index ) :
if isinstance ( index , slice ) : return super ( ) . __getitem__ ( index )
return super ( ) . __getitem__ ( index ) if index < len ( self ) else None
@property
def free_idx ( self ) :
fi = len ( self )
if hasattr ( self , ' has_nones ' ) and self . has_nones :
fi = next ( ( i for i , x in enumerate ( self ) if x is None ) , len ( self ) )
self . has_nones = fi < len ( self )
return fi
class IndexList ( list ) :
@ -76,10 +91,10 @@ class Node:
@@ -76,10 +91,10 @@ class Node:
by allocating an array or list : code : ` my_data ` of length : code : ` len ( n . circuit . nodes ) ` and
accessing it by : code : ` my_data [ n . index ] ` or simply by : code : ` my_data [ n ] ` .
"""
self . ins = GrowingList ( )
self . ins : list [ Line ] = GrowingList ( )
""" A list of input connections (:class:`Line` objects).
"""
self . outs = GrowingList ( )
self . outs : list [ Line ] = GrowingList ( )
""" A list of output connections (:class:`Line` objects).
"""
@ -135,7 +150,7 @@ class Line:
@@ -135,7 +150,7 @@ class Line:
Use the explicit case only if connections to specific pins are required .
It may overwrite any previous line references in the connection list of the nodes .
"""
def __init__ ( self , circuit , driver , reader ) :
def __init__ ( self , circuit : Circuit , driver : Union [ Node , tuple [ Node , int ] ] , reader : Union [ Node , tuple [ Node , int ] ] ) :
self . circuit = circuit
""" The :class:`Circuit` object the line is part of.
"""
@ -147,7 +162,7 @@ class Line:
@@ -147,7 +162,7 @@ class Line:
by allocating an array or list : code : ` my_data ` of length : code : ` len ( l . circuit . lines ) ` and
accessing it by : code : ` my_data [ l . index ] ` or simply by : code : ` my_data [ l ] ` .
"""
if not isinstance ( driver , tuple ) : driver = ( driver , driver . outs . free_in de x ( ) )
if not isinstance ( driver , tuple ) : driver = ( driver , driver . outs . free_idx )
self . driver = driver [ 0 ]
""" The :class:`Node` object that drives this line.
"""
@ -157,7 +172,7 @@ class Line:
@@ -157,7 +172,7 @@ class Line:
This is the position in the list : py : attr : ` Node . outs ` of the driving node this line referenced from :
: code : ` self . driver . outs [ self . driver_pin ] == self ` .
"""
if not isinstance ( reader , tuple ) : reader = ( reader , reader . ins . free_in de x ( ) )
if not isinstance ( reader , tuple ) : reader = ( reader , reader . ins . free_idx )
self . reader = reader [ 0 ]
""" The :class:`Node` object that reads this line.
"""
@ -334,15 +349,16 @@ class Circuit:
@@ -334,15 +349,16 @@ class Circuit:
def get_or_add_fork ( self , name ) :
return self . forks [ name ] if name in self . forks else Node ( self , name )
def remove_dangling_nodes ( self , root_node : Node ) :
def remove_dangling_nodes ( self , root_node : Node , keep = [ ] ) :
if len ( [ l for l in root_node . outs if l is not None ] ) > 0 : return
lines = [ l for l in root_node . ins if l is not None ]
drivers = [ l . driver for l in lines ]
if root_node in keep : return
root_node . remove ( )
for l in lines :
l . remove ( )
for d in drivers :
self . remove_dangling_nodes ( d )
self . remove_dangling_nodes ( d , keep = keep )
def eliminate_1to1_forks ( self ) :
""" Removes all forks that drive only one node.
@ -370,6 +386,21 @@ class Circuit:
@@ -370,6 +386,21 @@ class Circuit:
in_line . reader_pin = out_reader_pin
in_line . reader . ins [ in_line . reader_pin ] = in_line
def remove_forks ( self ) :
ios = set ( self . io_nodes )
for n in list ( self . forks . values ( ) ) :
if n in ios : continue
d = None
if ( l := n . ins [ 0 ] ) is not None :
d = l . driver
l . remove ( )
for l in list ( n . outs ) :
if l is None : continue
r , rp = l . reader , l . reader_pin
l . remove ( )
if d is not None : Line ( self , d , ( r , rp ) )
n . remove ( )
def substitute ( self , node , impl ) :
""" Replaces a given node with the given implementation circuit.
@ -428,7 +459,7 @@ class Circuit:
@@ -428,7 +459,7 @@ class Circuit:
for l , ll in zip ( impl_out_lines , node_out_lines ) : # connect outputs
if ll is None :
if l . driver in node_map :
self . remove_dangling_nodes ( node_map [ l . driver ] )
self . remove_dangling_nodes ( node_map [ l . driver ] , keep = set ( self . s_nodes ) )
continue
if len ( l . reader . outs ) > 0 : # output is also read by impl. circuit, connect to fork.
ll . driver = node_map [ l . reader ]
@ -447,6 +478,21 @@ class Circuit:
@@ -447,6 +478,21 @@ class Circuit:
if n . kind in tlib . cells :
self . substitute ( n , tlib . cells [ n . kind ] [ 0 ] )
def remove_constants ( self ) :
c1gen = None
for n in self . nodes :
if n . kind == ' __const0__ ' : # just remove, unconnected inputs are defined 0.
for l in n . outs :
l . remove ( )
n . remove ( )
elif n . kind == ' __const1__ ' :
if c1gen is None : c1gen = Node ( self , ' __const1gen__ ' , ' INV1 ' ) # one unique const 1 generator
for l in n . outs :
r , rp = l . reader , l . reader_pin
l . remove ( )
Line ( self , c1gen , ( r , rp ) )
n . remove ( )
def copy ( self ) :
""" Returns a deep copy of the circuit.
"""
@ -501,14 +547,15 @@ class Circuit:
@@ -501,14 +547,15 @@ class Circuit:
substrings ' dff ' or ' latch ' are yielded first .
"""
visit_count = np . zeros ( len ( self . nodes ) , dtype = np . uint32 )
queue = deque ( n for n in self . nodes if len ( n . ins ) == 0 or ' dff ' in n . kind . lower ( ) or ' latch ' in n . kind . lower ( ) )
start = set ( n for n in self . nodes if len ( n . ins ) == 0 or ' dff ' in n . kind . lower ( ) or ' latch ' in n . kind . lower ( ) )
queue = deque ( start )
while len ( queue ) > 0 :
n = queue . popleft ( )
for line in n . outs :
if line is None : continue
succ = line . reader
visit_count [ succ ] + = 1
if visit_count [ succ ] == len ( succ . ins ) and ' dff ' not in succ . kind . lower ( ) and ' latch ' not in succ . kind . lower ( ) :
if visit_count [ succ ] == len ( succ . ins ) and succ not in start :
queue . append ( succ )
yield n