Merge branch 'master' into lefdef-enhancments

This commit is contained in:
Matthias Koefferlein 2020-07-18 09:21:16 +02:00
commit 89745fd0de
128 changed files with 8321 additions and 3624 deletions

1
Jenkinsfile vendored
View File

@ -16,6 +16,7 @@ node("master") {
stage("Checkout sources") {
checkout scm
checkout_private()
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

View File

@ -441,14 +441,16 @@ def RunMainBuildBash():
global DeploymentF
global DeploymentP
global PackagePrefix
global MacPkgDir # relative path to package directory
global MacBinDir # relative path to binary directory
global MacBuildDir # relative path to build directory
global MacBuildLog # relative path to build log file
global AbsMacPkgDir # absolute path to package directory
global AbsMacBinDir # absolute path to binary directory
global AbsMacBuildDir # absolute path to build directory
global AbsMacBuildLog # absolute path to build log file
global MacPkgDir # relative path to package directory
global MacBinDir # relative path to binary directory
global MacBuildDir # relative path to build directory
global MacBuildDirQAT # relative path to build directory for QATest
global MacBuildLog # relative path to build log file
global AbsMacPkgDir # absolute path to package directory
global AbsMacBinDir # absolute path to binary directory
global AbsMacBuildDir # absolute path to build directory
global AbsMacBuildDirQAT # absolute path to build directory for QATest
global AbsMacBuildLog # absolute path to build log file
#-----------------------------------------------------
# [1] Set parameters passed to the main Bash script
@ -468,14 +470,16 @@ def RunMainBuildBash():
ruby_python = "R%sP%s" % ( ruby.lower(), python.lower() )
# (C) Target directories and files
MacPkgDir = "./%s%s.pkg.macos-%s-%s-%s" % (PackagePrefix, qt, Platform, mode, ruby_python)
MacBinDir = "./%s.bin.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python)
MacBuildDir = "./%s.build.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python)
MacBuildLog = "./%s.build.macos-%s-%s-%s.log" % ( qt, Platform, mode, ruby_python)
AbsMacPkgDir = "%s/%s%s.pkg.macos-%s-%s-%s" % (ProjectDir, PackagePrefix, qt, Platform, mode, ruby_python)
AbsMacBinDir = "%s/%s.bin.macos-%s-%s-%s" % (ProjectDir, qt, Platform, mode, ruby_python)
AbsMacBuildDir = "%s/%s.build.macos-%s-%s-%s" % (ProjectDir, qt, Platform, mode, ruby_python)
AbsMacBuildLog = "%s/%s.build.macos-%s-%s-%s.log" % (ProjectDir, qt, Platform, mode, ruby_python)
MacPkgDir = "./%s%s.pkg.macos-%s-%s-%s" % (PackagePrefix, qt, Platform, mode, ruby_python)
MacBinDir = "./%s.bin.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python)
MacBuildDir = "./%s.build.macos-%s-%s-%s" % ( qt, Platform, mode, ruby_python)
MacBuildLog = "./%s.build.macos-%s-%s-%s.log" % ( qt, Platform, mode, ruby_python)
AbsMacPkgDir = "%s/%s%s.pkg.macos-%s-%s-%s" % (ProjectDir, PackagePrefix, qt, Platform, mode, ruby_python)
AbsMacBinDir = "%s/%s.bin.macos-%s-%s-%s" % (ProjectDir, qt, Platform, mode, ruby_python)
AbsMacBuildDir = "%s/%s.build.macos-%s-%s-%s" % (ProjectDir, qt, Platform, mode, ruby_python)
AbsMacBuildLog = "%s/%s.build.macos-%s-%s-%s.log" % (ProjectDir, qt, Platform, mode, ruby_python)
MacBuildDirQAT = MacBuildDir + ".macQAT"
AbsMacBuildDirQAT = AbsMacBuildDir + ".macQAT"
# (D) Qt5
if ModuleQt == 'Qt5MacPorts':
@ -554,6 +558,45 @@ def RunMainBuildBash():
print( "### <%s>: successfully built KLayout" % myscript, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "", file=sys.stderr )
#------------------------------------------------------------------------
# [4] Prepare "*.macQAT/" directory for the QATest.
# Binaries under "*.macQAT/" such as *.dylib will be touched later.
#------------------------------------------------------------------------
print( "### Preparing <%s>" % MacBuildDirQAT )
if os.path.isdir( MacBuildDirQAT ):
shutil.rmtree( MacBuildDirQAT )
os.chdir( MacBuildDir )
tarFile = "../macQATest.tar"
tarCmdC = "tar cf %s ." % tarFile
if subprocess.call( tarCmdC, shell=True ) != 0:
print( "", file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to create <%s>" % (myscript, tarFile), file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
return 1
os.chdir( "../" )
os.mkdir( MacBuildDirQAT )
os.chdir( MacBuildDirQAT )
tarCmdX = "tar xf %s" % tarFile
if subprocess.call( tarCmdX, shell=True ) != 0:
print( "", file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to unpack <%s>" % (myscript, tarFile), file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
return 1
os.remove( tarFile )
os.chdir( "../" )
shutil.copy2( "macbuild/macQAT.sh", MacBuildDirQAT )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: prepared the initial *.macQAT/" % myscript, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "", file=sys.stderr )
return 0
#------------------------------------------------------------------------------
@ -573,10 +616,12 @@ def DeployBinariesForBundle():
global MacPkgDir
global MacBinDir
global MacBuildDir
global MacBuildDirQAT
global MacBuildLog
global AbsMacPkgDir
global AbsMacBinDir
global AbsMacBuildDir
global AbsMacBuildDirQAT
global AbsMacBuildLog
global Version
global DeployVerbose

90
macbuild/macQAT.sh Executable file
View File

@ -0,0 +1,90 @@
#!/bin/bash
#===============================================================================
# File: "macbuild/macQAT.sh"
#
# The top Bash script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple
# ßMac OSX platforms.
#
# This script must be copied to a "*.macQAT/" directory to run.
#===============================================================================
#----------------------------------------------------------------
# Functions
#----------------------------------------------------------------
function GetPresentDir() {
path=$1
array=( `echo $path | tr -s '/' ' '`)
last_index=`expr ${#array[@]} - 1`
echo ${array[${last_index}]}
return 0
}
function HidePrivateDir() {
if [ -d "../private" ]; then
ret=$(/bin/mv ../private ../private.stash)
fi
}
function ShowPrivateDir() {
if [ -d "../private.stash" ]; then
ret=$(/bin/mv ../private.stash ../private)
fi
}
#----------------------------------------------------------------
# Variables
#----------------------------------------------------------------
gitSHA1=$(git rev-parse --short HEAD 2>/dev/null)
timestamp=$(date "+%Y_%m%d_%H%M")
presentDir=$(GetPresentDir `pwd`)
logfile=QATest_${gitSHA1}_${timestamp}__${presentDir}.log
#----------------------------------------------------------------
# Environment variables for "ut_runner"
#----------------------------------------------------------------
export TESTSRC=..
export TESTTMP=QATest_${gitSHA1}_${timestamp}__${presentDir}
export DYLD_LIBRARY_PATH=$(pwd):$(pwd)/db_plugins
#----------------------------------------------------------------
# Environment variables for "ut_runner"
#----------------------------------------------------------------
if [ $# -eq 1 ]; then
if [ "$1" == "-h" ]; then
./ut_runner -h
exit 0
fi
if [ "$1" == "-r" ]; then
echo "### Dumping the log to" $logfile "..."
HidePrivateDir
./ut_runner -x pymod -c 2>&1 | tee $logfile
ShowPrivateDir
exit 0
else
echo ""
echo " Git SHA1 = ${gitSHA1}"
echo " Time stamp = ${timestamp}"
echo " Log file = ${logfile}"
echo " Usage:"
echo " ./QATest.sh -h: to get the help of 'ut_runner'"
echo " ./QATest.sh -r: to run the tests with '-c' option: continues after an error"
echo ""
exit 0
fi
else
echo ""
echo " Git SHA1 = ${gitSHA1}"
echo " Time stamp = ${timestamp}"
echo " Log file = ${logfile}"
echo " Usage:"
echo " ./QATest.sh -h: to get the help of 'ut_runner'"
echo " ./QATest.sh -r: to run the tests with '-c' option: continues after an error"
echo ""
exit 0
fi
#--------------
# End of File
#--------------

View File

@ -37,6 +37,7 @@ from build4mac_util import *
def SetGlobals():
global ProjectDir # project directory where "build.sh" exists
global Usage # string on usage
global LatestOS # the latest generic OS name
global GenOSName # generic OS name
global Platform # platform
global PkgDir # the package directory where "klayout.app" exists
@ -57,8 +58,9 @@ def SetGlobals():
global VolumeDMG # the volume name of DMG
global TargetDMG # the name of target DMG file
global RootApplications # reserved directory name for applications
global CatalinaAnaconda3 # True if 'Catalina with Anaconda3'
global CatalinaHomebrew # True if 'Catalina with Homebrew'
global LatestOSMacPorts # True if 'LatestOS with MacPorts'
global LatestOSHomebrew # True if 'LatestOS with Homebrew'
global LatestOSAnaconda3 # True if 'LatestOS with Anaconda3'
global DicLightWeight # dictionary for LW-* packages
global Item3AppleScript # ITEM_3 in the Apple script
# auxiliary variables on platform
@ -98,9 +100,11 @@ def SetGlobals():
quit()
release = int( Release.split(".")[0] ) # take the first of ['19', '0', '0']
LatestOS = ""
if release == 19:
GenOSName = "macOS"
Platform = "Catalina"
LatestOS = Platform
elif release == 18:
GenOSName = "macOS"
Platform = "Mojave"
@ -144,21 +148,30 @@ def SetGlobals():
VolumeDMG = "KLayout"
TargetDMG = ""
RootApplications = "/Applications"
CatalinaAnaconda3 = False
CatalinaHomebrew = False
LatestOSMacPorts = False
LatestOSHomebrew = False
LatestOSAnaconda3 = False
DicLightWeight = dict()
Item3AppleScript = ""
# Populate DicLightWeight
DicLightWeight[ "ana3" ] = dict()
DicLightWeight[ "brew" ] = dict()
DicLightWeight[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip"
DicLightWeight[ "ana3" ]["src"] = "script-bundle-A"
DicLightWeight[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst"
DicLightWeight[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-ReadMeFirst" to {700, 400}'
DicLightWeight[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip"
DicLightWeight[ "brew" ]["src"] = "script-bundle-B"
DicLightWeight[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst"
DicLightWeight[ "brew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}'
DicLightWeight[ "ports" ] = dict()
DicLightWeight[ "brew" ] = dict()
DicLightWeight[ "ana3" ] = dict()
DicLightWeight[ "ports" ]["zip"] = "macbuild/Resources/script-bundle-P.zip"
DicLightWeight[ "ports" ]["src"] = "script-bundle-P"
DicLightWeight[ "ports" ]["des"] = "MacPortsUser-ReadMeFirst"
DicLightWeight[ "ports" ]["item3"] = 'set position of item "MacPortsUser-ReadMeFirst" to {700, 400}'
DicLightWeight[ "brew" ]["zip"] = "macbuild/Resources/script-bundle-B.zip"
DicLightWeight[ "brew" ]["src"] = "script-bundle-B"
DicLightWeight[ "brew" ]["des"] = "HomebrewUser-ReadMeFirst"
DicLightWeight[ "brew" ]["item3"] = 'set position of item "HomebrewUser-ReadMeFirst" to {700, 400}'
DicLightWeight[ "ana3" ]["zip"] = "macbuild/Resources/script-bundle-A.zip"
DicLightWeight[ "ana3" ]["src"] = "script-bundle-A"
DicLightWeight[ "ana3" ]["des"] = "Anaconda3User-ReadMeFirst"
DicLightWeight[ "ana3" ]["item3"] = 'set position of item "Anaconda3User-ReadMeFirst" to {700, 400}'
#------------------------------------------------------------------------------
## To check the contents of the package directory
@ -184,8 +197,9 @@ def CheckPkgDirectory():
global PackagePrefix
global QtIdentification
global RubyPythonID
global CatalinaAnaconda3
global CatalinaHomebrew
global LatestOSMacPorts
global LatestOSHomebrew
global LatestOSAnaconda3
global DicLightWeight
global Item3AppleScript
@ -224,20 +238,25 @@ def CheckPkgDirectory():
RubyPythonID = pkgdirComponents[5]
#-----------------------------------------------------------------------------
# [3] Check if Catalina with Anaconda3 / Homebrew
# [3] Check if the "LatestOS" with MacPorts / Homebrew / Anaconda3
#-----------------------------------------------------------------------------
CatalinaAnaconda3 = Platform == "Catalina"
CatalinaAnaconda3 &= PackagePrefix == "LW"
CatalinaAnaconda3 &= QtIdentification == "qt5Ana3"
CatalinaAnaconda3 &= RubyPythonID == "Rana3Pana3"
LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification == "qt5MP"
LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp37"
CatalinaHomebrew = Platform == "Catalina"
CatalinaHomebrew &= PackagePrefix == "LW"
CatalinaHomebrew &= QtIdentification == "qt5Brew"
CatalinaHomebrew &= RubyPythonID == "Rhb27Phb37"
LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification == "qt5Brew"
LatestOSHomebrew &= RubyPythonID == "Rhb27Phb37"
if CatalinaAnaconda3:
mydic = DicLightWeight["ana3"]
LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW"
LatestOSAnaconda3 &= QtIdentification == "qt5Ana3"
LatestOSAnaconda3 &= RubyPythonID == "Rana3Pana3"
if LatestOSMacPorts:
mydic = DicLightWeight["ports"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
@ -251,7 +270,7 @@ def CheckPkgDirectory():
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if CatalinaHomebrew:
if LatestOSHomebrew:
mydic = DicLightWeight["brew"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
@ -266,6 +285,21 @@ def CheckPkgDirectory():
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSAnaconda3:
mydic = DicLightWeight["ana3"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
#------------------------------------------------------
# [4] Check the presence of the default bundle
#------------------------------------------------------
@ -437,8 +471,8 @@ def MakeTargetDMGFile(msg=""):
WIN_WIDTH='1000', WIN_HEIGHT='500',
FULL_PATH_DS_STORE='/Volumes/%s/.DS_Store' % VolumeDMG,
BACKGROUND_PNG_FILE=BackgroundPNG,
ITEM_1='%s' % BundleName, X1='860', Y1='165',
ITEM_2='Applications', X2='860', Y2='345',
ITEM_1='%s' % BundleName, X1='900', Y1='165',
ITEM_2='Applications', X2='900', Y2='345',
ITEM_3=Item3AppleScript,
CHECK_BASH='[ -f " & dotDSStore & " ]; echo $?'
)

251
macbuild/nightyCatalina.py Executable file
View File

@ -0,0 +1,251 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
import sys
import os
import shutil
import glob
import platform
import optparse
import subprocess
Variation = [ 'std', 'ports', 'brew', 'ana3' ]
Usage = "\n"
Usage += "------------------------------------------------------------------------------------------\n"
Usage += " nightyCatalina.py [EXPERIMENTAL] \n"
Usage += " << To execute the jobs for making KLatyout's DMGs for macOS Catalina >> \n"
Usage += "\n"
Usage += "$ [python] nightyCatalina.py \n"
Usage += " option & argument : comment on option if any | default value\n"
Usage += " -------------------------------------------------------------------+--------------\n"
Usage += " [--build] : build and deploy | disabled\n"
Usage += " [--makedmg <srlno>] : make DMG | disabled\n"
Usage += " [--cleandmg <srlno>] : clean DMG | disabled\n"
Usage += " [--upload <dropbox>] : upload to $HOME/Dropbox/klayout/<dropbox> | disabled\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += "----------------------------------------------------------------------+-------------------\n"
def ParseCommandLineArguments():
global Usage
global Build # operation flag
global MakeDMG # operation flag
global CleanDMG # operation flag
global Upload # operation flag
global SrlDMG # DMG serial number
global Dropbox # Dropbox directory
p = optparse.OptionParser( usage=Usage )
p.add_option( '--build',
action='store_true',
dest='build',
default=False,
help='build and deploy' )
p.add_option( '--makedmg',
dest='makedmg',
help='make DMG' )
p.add_option( '--cleandmg',
dest='cleandmg',
help='clean DMG' )
p.add_option( '--upload',
dest='upload',
help='upload to Dropbox' )
p.add_option( '-?', '--??',
action='store_true',
dest='checkusage',
default=False,
help='check usage (false)' )
p.set_defaults( build = False,
makedmg = "",
cleandmg = "",
upload = "",
checkusage = False )
opt, args = p.parse_args()
if opt.checkusage:
print(Usage)
quit()
Build = False
MakeDMG = False
CleanDMG = False
Upload = False
Build = opt.build
if not opt.makedmg == "":
MakeDMG = True
CleanDMG = False
SrlDMG = int(opt.makedmg)
if not opt.cleandmg == "":
MakeDMG = False
CleanDMG = True
SrlDMG = int(opt.cleandmg)
if not opt.upload == "":
Upload = True
Dropbox = opt.upload
if not (Build or MakeDMG or CleanDMG or Upload):
print( "! No option selected" )
print(Usage)
quit()
def BuildDeploy():
PyBuild = "./build4mac.py"
Build = dict()
Build["std"] = [ '-q', 'Qt5MacPorts', '-r', 'sys', '-p', 'sys' ]
Build["ports"] = [ '-q', 'Qt5MacPorts', '-r', 'MP26', '-p', 'MP37' ]
Build["brew"] = [ '-q', 'Qt5Brew', '-r', 'HB27', '-p', 'HB37' ]
Build["ana3"] = [ '-q', 'Qt5Ana3', '-r', 'Ana3', '-p', 'Ana3' ]
for key in Variation:
command1 = [ PyBuild ] + Build[key]
if key == "std":
command2 = [ PyBuild ] + Build[key] + ['-y']
else:
command2 = [ PyBuild ] + Build[key] + ['-Y']
print(command1)
print(command2)
#continue
if subprocess.call( command1, shell=False ) != 0:
print( "", file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to build KLayout" % PyBuild, file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
sys.exit(1)
else:
print( "", file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: successfully built KLayout" % PyBuild, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "", file=sys.stderr )
if subprocess.call( command2, shell=False ) != 0:
print( "", file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to deploy KLayout" % PyBuild, file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
sys.exit(1)
else:
print( "", file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: successfully deployed KLayout" % PyBuild, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "", file=sys.stderr )
def DMG_Make( srlDMG ):
PyDMG = "./makeDMG4mac.py"
Stash = "./DMGStash"
Pack = dict()
Pack["std"] = [ '-p', 'ST-qt5MP.pkg.macos-Catalina-release-RsysPsys', '-s', '%d' % srlDMG, '-m' ]
Pack["ports"] = [ '-p', 'LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37', '-s', '%d' % srlDMG, '-m' ]
Pack["brew"] = [ '-p', 'LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37', '-s', '%d' % srlDMG, '-m' ]
Pack["ana3"] = [ '-p', 'LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3', '-s', '%d' % srlDMG, '-m' ]
if os.path.isdir( Stash ):
shutil.rmtree( Stash )
os.mkdir( Stash )
for key in Variation:
command3 = [ PyDMG ] + Pack[key]
print(command3)
#continue
if subprocess.call( command3, shell=False ) != 0:
print( "", file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to make KLayout DMG" % PyDMG, file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
sys.exit(1)
else:
print( "", file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: successfully made KLayout DMG" % PyDMG, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "", file=sys.stderr )
dmgs = glob.glob( "*.dmg*" )
for item in dmgs:
shutil.move( item, Stash )
def DMG_Clean( srlDMG ):
PyDMG = "./makeDMG4mac.py"
Stash = "./DMGStash"
Pack = dict()
Pack["std"] = [ '-p', 'ST-qt5MP.pkg.macos-Catalina-release-RsysPsys', '-s', '%d' % srlDMG, '-c' ]
Pack["ports"] = [ '-p', 'LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37', '-s', '%d' % srlDMG, '-c' ]
Pack["brew"] = [ '-p', 'LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37', '-s', '%d' % srlDMG, '-c' ]
Pack["ana3"] = [ '-p', 'LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3', '-s', '%d' % srlDMG, '-c' ]
if os.path.isdir( Stash ):
shutil.rmtree( Stash )
for key in Variation:
command3 = [ PyDMG ] + Pack[key]
print(command3)
#continue
if subprocess.call( command3, shell=False ) != 0:
print( "", file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to clean KLayout DMG" % PyDMG, file=sys.stderr )
print( "-----------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
sys.exit(1)
else:
print( "", file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "### <%s>: successfully cleaned KLayout DMG" % PyDMG, file=sys.stderr )
print( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", file=sys.stderr )
print( "", file=sys.stderr )
def UploadToDropbox( targetdir ):
Stash = "./DMGStash"
distDir = os.environ["HOME"] + "/Dropbox/klayout/" + targetdir
if not os.path.isdir(distDir):
os.makedirs(distDir)
dmgs = glob.glob( "%s/*.dmg*" % Stash )
for item in dmgs:
shutil.copy2( item, distDir )
def Main():
ParseCommandLineArguments()
if Build:
BuildDeploy()
elif MakeDMG:
DMG_Make( SrlDMG )
elif CleanDMG:
DMG_Clean( SrlDMG )
elif Upload:
UploadToDropbox( Dropbox )
#===================================================================================
if __name__ == "__main__":
Main()
#---------------
# End of file
#---------------

View File

@ -87,5 +87,6 @@ BD_PUBLIC int strmrun (int argc, char *argv[])
lym::Macro macro;
macro.load_from (script);
macro.set_file_path (script);
return macro.run ();
}

View File

@ -70,7 +70,11 @@ struct basic_array_iterator
virtual long index_a () const { return -1; }
virtual long index_b () const { return -1; }
virtual size_t quad_id () const { return 0; }
virtual box_type quad_box () const { return box_type::world (); }
virtual void skip_quad () { }
virtual disp_type get () const = 0;
virtual basic_array_iterator<Coord> *clone () const = 0;
@ -111,8 +115,12 @@ struct ArrayBase
virtual bool equal (const ArrayBase *) const = 0;
virtual bool fuzzy_equal (const ArrayBase *) const = 0;
virtual bool less (const ArrayBase *) const = 0;
virtual bool fuzzy_less (const ArrayBase *) const = 0;
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const = 0;
bool in_repository;
@ -554,6 +562,12 @@ struct regular_array
return (m_a == d->m_a && m_b == d->m_b && m_amax == d->m_amax && m_bmax == d->m_bmax);
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const regular_array<Coord> *d = static_cast<const regular_array<Coord> *> (b);
return (m_a.equal (d->m_a) && m_b.equal (d->m_b) && m_amax == d->m_amax && m_bmax == d->m_bmax);
}
virtual bool less (const ArrayBase *b) const
{
const regular_array<Coord> *d = static_cast<const regular_array<Coord> *> (b);
@ -562,6 +576,14 @@ struct regular_array
m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax)))));
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const regular_array<Coord> *d = static_cast<const regular_array<Coord> *> (b);
return m_a.less (d->m_a) || (m_a.equal (d->m_a) && (
m_b.less (d->m_b) || (m_b.equal (d->m_b) && (
m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax)))));
}
virtual bool is_regular_array (vector_type &a, vector_type &b, unsigned long &amax, unsigned long &bmax) const
{
a = m_a;
@ -702,6 +724,18 @@ struct regular_complex_array
return regular_array<Coord>::equal (b);
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const regular_complex_array<Coord> *d = static_cast<const regular_complex_array<Coord> *> (b);
if (fabs (m_acos - d->m_acos) > epsilon) {
return false;
}
if (fabs (m_mag - d->m_mag) > epsilon) {
return false;
}
return regular_array<Coord>::fuzzy_equal (b);
}
virtual bool less (const ArrayBase *b) const
{
const regular_complex_array<Coord> *d = static_cast<const regular_complex_array<Coord> *> (b);
@ -714,6 +748,18 @@ struct regular_complex_array
return regular_array<Coord>::less (b);
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const regular_complex_array<Coord> *d = static_cast<const regular_complex_array<Coord> *> (b);
if (fabs (m_acos - d->m_acos) > epsilon) {
return m_acos < d->m_acos;
}
if (fabs (m_mag - d->m_mag) > epsilon) {
return m_mag < d->m_mag;
}
return regular_array<Coord>::fuzzy_less (b);
}
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (!no_self) {
@ -806,6 +852,21 @@ struct iterated_array_iterator
return new iterated_array_iterator <Coord> (*this);
}
virtual size_t quad_id () const
{
return m_t.quad_id ();
}
virtual box_type quad_box () const
{
return m_t.quad_box ();
}
virtual void skip_quad ()
{
m_t.skip_quad ();
}
private:
box_tree_const_iterator m_b, m_e;
box_tree_touching_iterator m_t;
@ -964,6 +1025,20 @@ struct iterated_array
return true;
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const iterated_array<Coord> *d = static_cast<const iterated_array<Coord> *> (b);
if (m_v.size () != d->m_v.size ()) {
return false;
}
for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) {
if (! p1->equal (*p2)) {
return false;
}
}
return true;
}
virtual bool less (const ArrayBase *b) const
{
const iterated_array<Coord> *d = static_cast<const iterated_array<Coord> *> (b);
@ -978,6 +1053,20 @@ struct iterated_array
return false;
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const iterated_array<Coord> *d = static_cast<const iterated_array<Coord> *> (b);
if (m_v.size () != d->m_v.size ()) {
return (m_v.size () < d->m_v.size ());
}
for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) {
if (! p1->equal (*p2)) {
return (p1->less (*p2));
}
}
return false;
}
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (!no_self) {
@ -1085,6 +1174,18 @@ struct iterated_complex_array
return iterated_array<Coord>::equal (b);
}
virtual bool fuzzy_equal (const ArrayBase *b) const
{
const iterated_complex_array<Coord> *d = static_cast<const iterated_complex_array<Coord> *> (b);
if (fabs (m_acos - d->m_acos) > epsilon) {
return false;
}
if (fabs (m_mag - d->m_mag) > epsilon) {
return false;
}
return iterated_array<Coord>::fuzzy_equal (b);
}
virtual bool less (const ArrayBase *b) const
{
const iterated_complex_array<Coord> *d = static_cast<const iterated_complex_array<Coord> *> (b);
@ -1097,6 +1198,18 @@ struct iterated_complex_array
return iterated_array<Coord>::less (b);
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
const iterated_complex_array<Coord> *d = static_cast<const iterated_complex_array<Coord> *> (b);
if (fabs (m_acos - d->m_acos) > epsilon) {
return m_acos < d->m_acos;
}
if (fabs (m_mag - d->m_mag) > epsilon) {
return m_mag < d->m_mag;
}
return iterated_array<Coord>::fuzzy_less (b);
}
virtual complex_trans_type complex_trans (const simple_trans_type &s) const
{
return complex_trans_type (s, m_acos, m_mag);
@ -1187,7 +1300,12 @@ struct single_complex_inst
return true;
}
virtual bool less (const ArrayBase *b) const
virtual bool fuzzy_equal (const ArrayBase *b) const
{
return equal (b);
}
virtual bool less (const ArrayBase *b) const
{
const double epsilon = 1e-10;
const single_complex_inst<Coord> *d = static_cast<const single_complex_inst<Coord> *> (b);
@ -1200,6 +1318,11 @@ struct single_complex_inst
return false;
}
virtual bool fuzzy_less (const ArrayBase *b) const
{
return less (b);
}
virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const
{
if (!no_self) {
@ -1420,6 +1543,36 @@ struct array_iterator
return mp_base ? mp_base->index_b () : -1;
}
/**
* @brief For iterators supporting quads (iterated arrays), this method will return the quad ID
*/
size_t quad_id () const
{
return mp_base ? mp_base->quad_id () : 0;
}
/**
* @brief For iterators supporting quads (iterated arrays), this method will return the quad bounding box
*
* Note that this method will only return a valid quad box is the quad_id is non-null.
*
* This method will return the bounding box of all array offsets in the quad.
*/
db::box<Coord> quad_box () const
{
return mp_base ? mp_base->quad_box () : db::box<Coord>::world ();
}
/**
* @brief For iterators supporting quads (iterated arrays), this method will skip the current quad
*/
void skip_quad ()
{
if (mp_base) {
mp_base->skip_quad ();
}
}
private:
trans_type m_trans;
basic_array_iterator <Coord> *mp_base;
@ -1592,6 +1745,32 @@ struct array
// .. nothing yet ..
}
/**
* @brief The iterated array constructor
*
* This is basically a convenience function that creates
* an appropriate basic_array object.
*/
template <class Iter>
array (const Obj &obj, const trans_type &trans, Iter from, Iter to)
: m_obj (obj), m_trans (trans), mp_base (new iterated_array <coord_type> (from, to))
{
// .. nothing yet ..
}
/**
* @brief The complex iterated array constructor
*
* This is basically a convenience function that creates
* an appropriate basic_array object.
*/
template <class Iter>
array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to)
: m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array <coord_type> (ct.rcos (), ct.mag (), from, to))
{
// .. nothing yet ..
}
/**
* @brief The singular complex instance constructor
*
@ -1835,6 +2014,32 @@ struct array
}
}
/**
* @brief Gets the bounding box from the iterator's current quad
*
* The bounding box is that of all objects in the current quad and
* is confined to the array's total bounding box.
*/
template <class Iter, class BoxConv>
box_type quad_box (const Iter &iter, const BoxConv &bc) const
{
box_type bb;
if (mp_base) {
bb = mp_base->bbox (box_type (0, 0, 0, 0));
}
bb &= iter.quad_box ();
if (mp_base) {
if (mp_base->is_complex ()) {
return bb * box_type (mp_base->complex_trans (simple_trans_type (m_trans)) * bc (m_obj));
} else {
return bb * (m_trans * bc (m_obj));
}
} else {
return bb * (m_trans * bc (m_obj));
}
}
/**
* @brief The number of single instances in the array
*/
@ -1918,6 +2123,21 @@ struct array
}
}
/**
* @brief Compare operator for equality (fuzzy version)
*/
bool equal (const array<Obj, Trans> &d) const
{
if (! mp_base) {
return (m_trans.equal (d.m_trans) && m_obj == d.m_obj && ! d.mp_base);
} else {
if (! m_trans.equal (d.m_trans) || ! (m_obj == d.m_obj) || type () != d.type ()) {
return false;
}
return mp_base && mp_base->fuzzy_equal (d.mp_base);
}
}
/**
* @brief A sorting order criterion
*/
@ -1943,6 +2163,31 @@ struct array
}
}
/**
* @brief A fuzzy sorting order criterion
*/
bool less (const array<Obj, Trans> &d) const
{
if (! (m_obj == d.m_obj)) {
return (m_obj < d.m_obj);
}
if (! m_trans.equal (d.m_trans)) {
return m_trans.less (d.m_trans);
}
if (type () != d.type ()) {
return (type () < d.type ());
}
if (mp_base == d.mp_base) {
return false;
} else if (! mp_base) {
return true;
} else if (! d.mp_base) {
return false;
} else {
return mp_base->fuzzy_less (d.mp_base);
}
}
/**
* @brief Compare operator
*
@ -2263,7 +2508,7 @@ struct array
}
db::mem_stat (stat, purpose, cat, m_obj, true, (void *) this);
if (mp_base) {
db::mem_stat (stat, purpose, cat, *mp_base, false, (void *) this);
mp_base->mem_stat (stat, purpose, cat, false, (void *) this);
}
}

View File

@ -168,40 +168,43 @@ public:
typedef typename Tree::coord_type coord_type;
typedef typename Tree::box_type box_type;
box_tree_node (box_tree_node *parent, const point_type &center, unsigned int quad)
: m_center (center)
box_tree_node (box_tree_node *parent, const point_type &center, const box_type &qbox, unsigned int quad)
{
for (int i = 0; i < 5; ++i) {
m_lenq[i] = 0;
}
for (int i = 0; i < 4; ++i) {
mp_children[i] = 0;
}
mp_parent = (box_tree_node *)((char *) parent + quad);
if (parent) {
parent->mp_children [quad] = this;
point_type corner;
if (quad == 0) {
corner = qbox.upper_right ();
} else if (quad == 1) {
corner = qbox.upper_left ();
} else if (quad == 2) {
corner = qbox.lower_left ();
} else if (quad == 3) {
corner = qbox.lower_right ();
}
init (parent, center, corner, quad);
}
~box_tree_node ()
{
for (int i = 0; i < 4; ++i) {
if (mp_children [i]) {
delete mp_children [i];
mp_children [i] = 0;
box_tree_node *c = child (i);
if (c) {
delete c;
}
}
}
box_tree_node *clone (box_tree_node *parent = 0, unsigned int quad = 0) const
{
box_tree_node *n = new box_tree_node (parent, m_center, quad);
for (unsigned int i = 0; i < 5; ++i) {
n->m_lenq[i] = m_lenq[i];
}
box_tree_node *n = new box_tree_node (parent, m_center, m_corner, quad);
n->m_lenq = m_lenq;
n->m_len = m_len;
for (unsigned int i = 0; i < 4; ++i) {
if (mp_children[i]) {
mp_children[i]->clone (n, i);
box_tree_node *c = child (i);
if (c) {
c->clone (n, i);
} else {
n->m_childrefs [i] = m_childrefs [i];
}
}
return n;
@ -209,17 +212,39 @@ public:
box_tree_node *child (int i) const
{
return mp_children [i];
if ((m_childrefs [i] & 1) == 0) {
return reinterpret_cast<box_tree_node *> (m_childrefs [i]);
} else {
return 0;
}
}
void lenq (int i, size_t l)
{
m_lenq[i + 1] = l;
if (i < 0) {
m_lenq = l;
} else {
box_tree_node *c = child (i);
if (c) {
c->m_len = l;
} else {
m_childrefs [i] = l * 2 + 1;
}
}
}
size_t lenq (int i) const
{
return m_lenq[i + 1];
if (i < 0) {
return m_lenq;
} else {
box_tree_node *c = child (i);
if (c) {
return c->m_len;
} else {
return m_childrefs [i] >> 1;
}
}
}
box_tree_node *parent () const
@ -238,8 +263,8 @@ public:
stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat);
}
for (int i = 0; i < 4; ++i) {
if (mp_children [i]) {
mp_children [i]->mem_stat (stat, purpose, cat, no_self, parent);
if (child (i)) {
child (i)->mem_stat (stat, purpose, cat, no_self, parent);
}
}
}
@ -249,14 +274,52 @@ public:
return m_center;
}
box_type quad_box (int quad) const
{
box_type qb = box_type::world ();
if (parent ()) {
qb = box_type (m_corner, parent ()->center ());
}
switch (quad) {
case 0: return box_type (m_center, qb.upper_right ());
case 1: return box_type (m_center, qb.upper_left ());
case 2: return box_type (m_center, qb.lower_left ());
case 3: return box_type (m_center, qb.lower_right ());
default: return qb;
}
}
private:
box_tree_node *mp_parent;
size_t m_lenq [5];
box_tree_node *mp_children [4];
point_type m_center;
size_t m_lenq, m_len;
size_t m_childrefs [4];
point_type m_center, m_corner;
box_tree_node (const box_tree_node &d);
box_tree_node &operator= (const box_tree_node &d);
box_tree_node (box_tree_node *parent, const point_type &center, const point_type &corner, unsigned int quad)
{
init (parent, center, corner, quad);
}
void init (box_tree_node *parent, const point_type &center, const point_type &corner, unsigned int quad)
{
m_center = center;
m_corner = corner;
m_lenq = m_len = 0;
for (int i = 0; i < 4; ++i) {
m_childrefs [i] = 0;
}
mp_parent = (box_tree_node *)((char *) parent + quad);
if (parent) {
m_len = (parent->m_childrefs [quad] >> 1);
parent->m_childrefs [quad] = size_t (this);
}
}
};
/**
@ -459,28 +522,9 @@ public:
box_type quad_box () const
{
if (! mp_node) {
return box_type::world ();
} else {
point_type c = mp_node->center ();
box_type qb;
if (! mp_node->parent ()) {
qb = box_type::world ();
} else {
point_type pc = mp_node->parent ()->center ();
qb = box_type (c - (pc - c), pc);
}
switch (m_quad) {
case 0: return box_type (c, qb.upper_right ());
case 1: return box_type (c, qb.upper_left ());
case 2: return box_type (c, qb.lower_left ());
case 3: return box_type (c, qb.lower_right ());
default: return qb;
}
return mp_node->quad_box (m_quad);
}
}
@ -577,12 +621,16 @@ private:
return m_quad < 4;
}
// down one level
// down as many levels as required for the next non-empty quad
// returns true if this is possible
bool down ()
{
box_tree_node *c = mp_node->child (m_quad);
if (c) {
while (true) {
box_tree_node *c = mp_node->child (m_quad);
if (! c) {
return false;
}
mp_node = c;
m_quad = -1;
@ -595,12 +643,11 @@ private:
// nothing to visit: up again
up ();
return false;
} else {
} else if (m_quad < 0) {
// stay in main chunk
return true;
}
} else {
return false;
}
}
@ -670,7 +717,7 @@ private:
* whose box overlaps or touches a specified test box.
*/
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100>
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100, unsigned int thin_aspect = 4>
class box_tree
{
public:
@ -1175,7 +1222,16 @@ private:
// the bins are: overall, ur, ul, ll, lr, empty
element_iterator qloc [6] = { from, from, from, from, from, from };
point_type center (bbox.center ());
point_type center;
if (bbox.width () < bbox.height () / thin_aspect) {
// separate by height only
center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2);
} else if (bbox.height () < bbox.width () / thin_aspect) {
// separate by width only
center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ());
} else {
center = bbox.center ();
}
for (element_iterator e = from; e != to; ++e) {
@ -1224,7 +1280,7 @@ private:
if (nn >= min_quads) {
// create a new node representing this tree
box_tree_node *node = new box_tree_node (parent, center, quad);
box_tree_node *node = new box_tree_node (parent, center, bbox, quad);
if (parent == 0) {
mp_root = node;
}
@ -1460,28 +1516,9 @@ public:
box_type quad_box () const
{
if (! mp_node) {
return box_type::world ();
} else {
point_type c = mp_node->center ();
box_type qb;
if (! mp_node->parent ()) {
qb = box_type::world ();
} else {
point_type pc = mp_node->parent ()->center ();
qb = box_type (c - (pc - c), pc);
}
switch (m_quad) {
case 0: return box_type (c, qb.upper_right ());
case 1: return box_type (c, qb.upper_left ());
case 2: return box_type (c, qb.lower_left ());
case 3: return box_type (c, qb.lower_right ());
default: return qb;
}
return mp_node->quad_box (m_quad);
}
}
@ -1578,12 +1615,16 @@ private:
return m_quad < 4;
}
// down one level
// down as many levels as required for the next non-empty quad
// returns true if this is possible
bool down ()
{
box_tree_node *c = mp_node->child (m_quad);
if (c) {
while (true) {
box_tree_node *c = mp_node->child (m_quad);
if (! c) {
return false;
}
mp_node = c;
m_quad = -1;
@ -1596,12 +1637,11 @@ private:
// nothing to visit: up again
up ();
return false;
} else {
} else if (m_quad < 0) {
// stay in main chunk
return true;
}
} else {
return false;
}
}
@ -1638,7 +1678,7 @@ private:
* is sorted.
*/
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100>
template <class Box, class Obj, class BoxConv, size_t min_bin = 100, size_t min_quads = 100, unsigned int thin_aspect = 4>
class unstable_box_tree
{
public:
@ -2103,7 +2143,16 @@ private:
}
obj_iterator qloc [5] = { from, from, from, from, from };
point_type center (bbox.center ());
point_type center;
if (bbox.width () < bbox.height () / thin_aspect) {
// separate by height only
center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2);
} else if (bbox.height () < bbox.width () / thin_aspect) {
// separate by width only
center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ());
} else {
center = bbox.center ();
}
for (obj_iterator e = from; e != to; ++e) {
@ -2158,7 +2207,7 @@ private:
if (nn >= min_quads) {
// create a new node representing this tree
box_tree_node *node = new box_tree_node (parent, center, quad);
box_tree_node *node = new box_tree_node (parent, center, bbox, quad);
if (parent == 0) {
mp_root = node;
}

View File

@ -301,6 +301,30 @@ public:
return m_instances.transform_into (ref, t);
}
/**
* @brief Transforms the cell by the given transformation.
*
* The transformation is applied to all instances and shapes. Magnified transformations will
* render magnified instances. See \transform_into for a version which avoids this.
*
* @param t The transformation to apply
*/
template <class Trans>
void transform (const Trans &t)
{
m_instances.transform (t);
for (typename shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) {
if (! s->second.empty ()) {
// Note: don't use the copy ctor here - it will copy the attachment to the manager
// and create problems when destroyed. Plus: swap would be more efficient. But by using
// assign_transformed we get undo support for free.
shapes_type d;
d = s->second;
s->second.assign_transformed (d, t);
}
}
}
/**
* @brief Transforms the cell into a new coordinate system.
*

View File

@ -333,20 +333,40 @@ void Circuit::remove_pin (size_t id)
void Circuit::add_net (Net *net)
{
if (! net) {
return;
}
if (net->circuit ()) {
throw tl::Exception (tl::to_string (tr ("Net already part of a circuit")));
}
m_nets.push_back (net);
net->set_circuit (this);
}
void Circuit::remove_net (Net *net)
{
if (! net) {
return;
}
if (net->circuit () != this) {
throw tl::Exception (tl::to_string (tr ("Net not withing given circuit")));
}
m_nets.erase (net);
}
void Circuit::join_nets (Net *net, Net *with)
{
if (! net) {
return;
}
if (net == with || ! with) {
return;
}
if (net->circuit () != this || with->circuit () != this) {
throw tl::Exception (tl::to_string (tr ("Nets not withing given circuit")));
}
while (with->begin_terminals () != with->end_terminals ()) {
db::Device *device = const_cast<db::Device *> (with->begin_terminals ()->device ());
@ -372,6 +392,13 @@ void Circuit::join_nets (Net *net, Net *with)
void Circuit::add_device (Device *device)
{
if (! device) {
return;
}
if (device->circuit ()) {
throw tl::Exception (tl::to_string (tr ("Device already in a circuit")));
}
device->set_circuit (this);
size_t id = 0;
@ -386,11 +413,25 @@ void Circuit::add_device (Device *device)
void Circuit::remove_device (Device *device)
{
if (! device) {
return;
}
if (device->circuit () != this) {
throw tl::Exception (tl::to_string (tr ("Device not withing given circuit")));
}
m_devices.erase (device);
}
void Circuit::add_subcircuit (SubCircuit *subcircuit)
{
if (! subcircuit) {
return;
}
if (subcircuit->circuit ()) {
throw tl::Exception (tl::to_string (tr ("Subcircuit already in a circuit")));
}
subcircuit->set_circuit (this);
size_t id = 0;
@ -405,6 +446,13 @@ void Circuit::add_subcircuit (SubCircuit *subcircuit)
void Circuit::remove_subcircuit (SubCircuit *subcircuit)
{
if (! subcircuit) {
return;
}
if (subcircuit->circuit () != this) {
throw tl::Exception (tl::to_string (tr ("Subcircuit not withing given circuit")));
}
m_subcircuits.erase (subcircuit);
}
@ -420,7 +468,12 @@ void Circuit::unregister_ref (SubCircuit *r)
void Circuit::flatten_subcircuit (SubCircuit *subcircuit)
{
tl_assert (subcircuit != 0);
if (! subcircuit) {
return;
}
if (subcircuit->circuit () != this) {
throw tl::Exception (tl::to_string (tr ("Subcircuit not withing given circuit")));
}
const db::Circuit *c = subcircuit->circuit_ref ();

View File

@ -283,6 +283,12 @@ namespace std
h = hfunc (b, h);
h = hfunc (na, h);
h = hfunc (nb, h);
} else if (o.size () > 1) {
// iterated array
typename db::array <db::CellInst, db::simple_trans<C> >::iterator i = o.begin ();
while (! (++i).at_end ()) {
h = hfunc (*i, h);
}
}
if (o.is_complex ()) {

View File

@ -1406,8 +1406,8 @@ private:
struct InteractionKeyForClustersType
: public InstanceToInstanceInteraction
{
InteractionKeyForClustersType (db::cell_index_type _ci1, db::cell_index_type _ci2, const db::ICplxTrans &_t21, const box_type &_box)
: InstanceToInstanceInteraction (_ci1, 0, _ci2, 0, _t21), box (_box)
InteractionKeyForClustersType (db::cell_index_type _ci1, db::cell_index_type _ci2, const db::ICplxTrans &_t1, const db::ICplxTrans &_t21, const box_type &_box)
: InstanceToInstanceInteraction (_ci1, 0, _ci2, 0, _t1, _t21), box (_box)
{ }
bool operator== (const InteractionKeyForClustersType &other) const
@ -1482,18 +1482,22 @@ private:
InstanceToInstanceInteraction ii_key;
db::ICplxTrans i1t, i2t;
bool fill_cache = false;
// Cache is only used for single instances, non-iterated, simple or regular arrays.
if ((! i1element.at_end () || i1.size () == 1 || ! i1.is_iterated_array ()) &&
(! i2element.at_end () || i2.size () == 1 || ! i2.is_iterated_array ())) {
{
i1t = i1element.at_end () ? i1.complex_trans () : i1.complex_trans (*i1element);
db::ICplxTrans tt1 = t1 * i1t;
i2t = i2element.at_end () ? i2.complex_trans () : i2.complex_trans (*i2element);
db::ICplxTrans tt2 = t2 * i2t;
db::ICplxTrans tt21 = tt1.inverted () * tt2;
db::ICplxTrans cache_norm = tt1.inverted ();
ii_key = InstanceToInstanceInteraction (i1.cell_index (), (! i1element.at_end () || i1.size () == 1) ? 0 : i1.cell_inst ().delegate (),
i2.cell_index (), (! i2element.at_end () || i2.size () == 1) ? 0 : i2.cell_inst ().delegate (),
tt21);
cache_norm, cache_norm * tt2);
instance_interaction_cache_type::iterator ii = mp_instance_interaction_cache->find (ii_key);
if (ii != mp_instance_interaction_cache->end ()) {
@ -1511,6 +1515,9 @@ private:
return;
}
fill_cache = true;
}
// array interactions
@ -1611,15 +1618,19 @@ private:
// return the list of unique interactions
interacting_clusters.insert (interacting_clusters.end (), sorted_interactions.begin (), sorted_interactions.end ());
// normalize transformations in cache
db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted ();
for (std::vector<std::pair<ClusterInstance, ClusterInstance> >::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) {
i->first.transform (i1ti);
i->second.transform (i2ti);
}
if (fill_cache) {
cluster_instance_pair_list_type &cached = (*mp_instance_interaction_cache) [ii_key];
cached.insert (cached.end (), sorted_interactions.begin (), sorted_interactions.end ());
// normalize transformations for cache
db::ICplxTrans i1ti = i1t.inverted (), i2ti = i2t.inverted ();
for (std::vector<std::pair<ClusterInstance, ClusterInstance> >::iterator i = sorted_interactions.begin (); i != sorted_interactions.end (); ++i) {
i->first.transform (i1ti);
i->second.transform (i2ti);
}
cluster_instance_pair_list_type &cached = (*mp_instance_interaction_cache) [ii_key];
cached.insert (cached.end (), sorted_interactions.begin (), sorted_interactions.end ());
}
}
/**
@ -1641,7 +1652,7 @@ private:
box_type common2 = common.transformed (t2i);
InteractionKeyForClustersType ikey (ci1, ci2, t21, common2);
InteractionKeyForClustersType ikey (ci1, ci2, t1i, t21, common2);
typename std::map<InteractionKeyForClustersType, std::vector<std::pair<size_t, size_t> > >::const_iterator ici = m_interaction_cache_for_clusters.find (ikey);
if (ici != m_interaction_cache_for_clusters.end ()) {

View File

@ -762,13 +762,71 @@ inline bool less_array_delegates (const db::ArrayBase *a, const db::ArrayBase *b
*/
struct InstanceToInstanceInteraction
{
InstanceToInstanceInteraction (db::cell_index_type _ci1, const db::ArrayBase *_array1, db::cell_index_type _ci2, const db::ArrayBase *_array2, const db::ICplxTrans &_t21)
: ci1 (_ci1), ci2 (_ci2), array1 (_array1), array2 (_array2), t21 (_t21)
{ }
InstanceToInstanceInteraction (db::cell_index_type _ci1, const db::ArrayBase *_array1, db::cell_index_type _ci2, const db::ArrayBase *_array2, const db::ICplxTrans &_tn, const db::ICplxTrans &_t21)
: ci1 (_ci1), ci2 (_ci2), array1 (0), array2 (0), t21 (_t21)
{
if (_array1) {
array1 = _array1->basic_clone ();
static_cast<db::basic_array<db::Coord> *> (array1)->transform (_tn);
}
if (_array2) {
array2 = _array2->basic_clone ();
static_cast<db::basic_array<db::Coord> *> (array2)->transform (_tn);
}
}
InstanceToInstanceInteraction ()
: ci1 (0), ci2 (0), array1 (0), array2 (0)
{ }
{
// .. nothing yet ..
}
InstanceToInstanceInteraction (const InstanceToInstanceInteraction &other)
: ci1 (other.ci1), ci2 (other.ci2),
array1 (other.array1 ? other.array1->basic_clone () : 0),
array2 (other.array2 ? other.array2->basic_clone () : 0),
t21 (other.t21)
{
// .. nothing yet ..
}
InstanceToInstanceInteraction &operator= (const InstanceToInstanceInteraction &other)
{
if (this != &other) {
ci1 = other.ci1;
ci2 = other.ci2;
if (array1) {
delete array1;
}
array1 = other.array1 ? other.array1->basic_clone () : 0;
if (array2) {
delete array2;
}
array2 = other.array2 ? other.array2->basic_clone () : 0;
t21 = other.t21;
}
return *this;
}
~InstanceToInstanceInteraction ()
{
if (array1) {
delete array1;
}
array1 = 0;
if (array2) {
delete array2;
}
array2 = 0;
}
bool operator== (const InstanceToInstanceInteraction &other) const
{
@ -797,7 +855,7 @@ struct InstanceToInstanceInteraction
}
db::cell_index_type ci1, ci2;
const db::ArrayBase *array1, *array2;
db::ArrayBase *array1, *array2;
db::ICplxTrans t21;
};

View File

@ -31,6 +31,9 @@ namespace db
static HierarchyBuilderShapeInserter def_inserter;
static HierarchyBuilder::cell_map_type null_map;
static HierarchyBuilder::cell_map_type::const_iterator null_iterator = null_map.end ();
// -------------------------------------------------------------------------------------------
int
@ -168,7 +171,7 @@ HierarchyBuilder::reset ()
m_cell_map.clear ();
m_cells_seen.clear ();
m_cell_stack.clear ();
m_cm_entry = cell_map_type::const_iterator ();
m_cm_entry = null_iterator;
m_cm_new_entry = false;
}
@ -263,14 +266,14 @@ HierarchyBuilder::end (const RecursiveShapeIterator *iter)
m_cells_seen.clear ();
mp_initial_cell = m_cell_stack.empty () ? 0 : m_cell_stack.front ().second.front ();
m_cell_stack.clear ();
m_cm_entry = cell_map_type::const_iterator ();
m_cm_entry = null_iterator;
m_cm_new_entry = false;
}
void
HierarchyBuilder::enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/)
{
tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != cell_map_type::const_iterator ());
tl_assert (m_cm_entry != m_cell_map.end () && m_cm_entry != null_iterator);
m_cells_seen.insert (m_cm_entry->first);

View File

@ -241,16 +241,28 @@ Instance::to_string (bool resolve_cell_name) const
r = "cell_index=" + tl::to_string (ci.object ().cell_index ());
}
if (ci.is_complex ()) {
r += " " + ci.complex_trans ().to_string ();
} else {
r += " " + (*ci.begin ()).to_string ();
}
db::vector<coord_type> a, b;
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
if (ci.is_complex ()) {
r += " " + ci.complex_trans ().to_string ();
} else {
r += " " + (*ci.begin ()).to_string ();
}
r += " array=(" + a.to_string () + "," + b.to_string () + " " + tl::to_string (amax) + "x" + tl::to_string (bmax) + ")";
} else {
for (db::CellInstArray::iterator i = ci.begin (); ! i.at_end (); ++i) {
if (ci.is_complex ()) {
r += " " + ci.complex_trans (*i).to_string ();
} else {
r += " " + (*i).to_string ();
}
}
}
if (has_prop_id ()) {
@ -640,13 +652,12 @@ OverlappingInstanceIteratorTraits::init (instance_iterator<OverlappingInstanceIt
// ChildCellIterator implementation
ChildCellIterator::ChildCellIterator ()
: m_iter (), m_end (), mp_insts (0)
: m_iter (), m_end ()
{ }
ChildCellIterator::ChildCellIterator (const instances_type *insts)
: m_iter (insts->begin_sorted_insts ()),
m_end (insts->end_sorted_insts ()),
mp_insts (insts)
m_end (insts->end_sorted_insts ())
{ }
cell_index_type

View File

@ -945,7 +945,6 @@ public:
private:
inst_iterator_type m_iter, m_end;
const instances_type *mp_insts;
};
/**

View File

@ -1189,6 +1189,8 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
enough (tl::info) << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]" << tl::noendl;
} else if (ci.size () > 1) {
enough (tl::info) << " (+" << (ci.size () - 1) << " irregular locations)" << tl::noendl;
} else {
enough (tl::info) << "" << tl::noendl;
}
@ -1208,6 +1210,8 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
enough (tl::info) << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]" << tl::noendl;
} else if (ci.size () > 1) {
enough (tl::info) << " (+" << (ci.size () - 1) << " irregular locations)" << tl::noendl;
} else {
enough (tl::info) << "" << tl::noendl;
}

View File

@ -1041,9 +1041,9 @@ LayoutToNetlist::build_nets (const std::vector<const db::Net *> *nets, const db:
}
}
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point)
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point, std::vector<db::SubCircuit *> *sc_path_out, db::Circuit *initial_circuit)
{
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point);
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point, sc_path_out, initial_circuit);
}
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::NetShape> &test_cluster, std::vector<db::InstElement> &rev_inst_path)
@ -1077,7 +1077,7 @@ size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell
return 0;
}
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point)
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point, std::vector<db::SubCircuit *> *sc_path_out, db::Circuit *initial_circuit)
{
if (! m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
@ -1089,6 +1089,14 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
unsigned int layer = layer_of (of_region);
const db::Cell *top_cell = internal_top_cell ();
if (initial_circuit && internal_layout ()->is_valid_cell_index (initial_circuit->cell_index ())) {
top_cell = &internal_layout ()->cell (initial_circuit->cell_index ());
}
if (! top_cell) {
return 0;
}
// Prepare a test cluster
db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1));
db::GenericRepository sr;
@ -1097,7 +1105,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
std::vector<db::InstElement> inst_path;
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path);
size_t cluster_id = search_net (db::ICplxTrans (), top_cell, test_cluster, inst_path);
if (cluster_id > 0) {
// search_net delivers the path in reverse order
@ -1105,7 +1113,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
std::vector<db::cell_index_type> cell_indexes;
cell_indexes.reserve (inst_path.size () + 1);
cell_indexes.push_back (internal_top_cell ()->cell_index ());
cell_indexes.push_back (top_cell->cell_index ());
for (std::vector<db::InstElement>::const_iterator i = inst_path.begin (); i != inst_path.end (); ++i) {
cell_indexes.push_back (i->inst_ptr.cell_index ());
}
@ -1142,38 +1150,57 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin
}
std::vector<db::SubCircuit *> sc_path;
db::Net *topmost_net = net;
// follow the path up in the net hierarchy using the transformation and the upper cell index as the
// guide line
while (! inst_path.empty () && net->pin_count () > 0) {
while (circuit && ! inst_path.empty ()) {
cell_indexes.pop_back ();
const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ());
tl_assert (pin != 0);
const db::Pin *pin = 0;
if (net && net->pin_count () > 0) {
pin = circuit->pin_by_id (net->begin_pins ()->pin_id ());
tl_assert (pin != 0);
}
db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv;
// try to find a parent circuit which connects to this net
db::Circuit *upper_circuit = 0;
db::SubCircuit *subcircuit = 0;
db::Net *upper_net = 0;
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) {
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_circuit; ++r) {
if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) {
upper_net = r->net_for_pin (pin->id ());
upper_circuit = r->circuit ();
subcircuit = r.operator-> ();
if (pin) {
upper_net = subcircuit->net_for_pin (pin->id ());
}
upper_circuit = subcircuit->circuit ();
}
}
net = upper_net;
if (upper_net) {
circuit = upper_circuit;
net = upper_net;
inst_path.pop_back ();
topmost_net = upper_net;
} else {
break;
sc_path.push_back (subcircuit);
}
circuit = upper_circuit;
inst_path.pop_back ();
}
return net;
if (sc_path_out) {
std::reverse (sc_path.begin (), sc_path.end ());
*sc_path_out = sc_path;
}
return topmost_net;
} else {
return 0;

View File

@ -709,8 +709,11 @@ public:
*
* This variant accepts a micrometer-unit location. The location is given in the
* coordinate space of the initial cell.
*
* The subcircuit path leading to the topmost net is stored in *sc_path_out if this
* pointer is non-null.
*/
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point);
db::Net *probe_net (const db::Region &of_region, const db::DPoint &point, std::vector<SubCircuit *> *sc_path_out = 0, Circuit *initial_circuit = 0);
/**
* @brief Finds the net by probing a specific location on the given layer
@ -718,7 +721,7 @@ public:
* This variant accepts a database-unit location. The location is given in the
* coordinate space of the initial cell.
*/
db::Net *probe_net (const db::Region &of_region, const db::Point &point);
db::Net *probe_net (const db::Region &of_region, const db::Point &point, std::vector<SubCircuit *> *sc_path_out = 0, Circuit *initial_circuit = 0);
/**
* @brief Runs an antenna check on the extracted clusters

View File

@ -238,6 +238,7 @@ namespace tl
*/
template <> struct type_traits <db::Library> : public type_traits<void> {
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_public_destructor;
};
}

View File

@ -265,6 +265,10 @@ void Netlist::unlock ()
const tl::vector<Circuit *> &Netlist::child_circuits (Circuit *circuit)
{
if (circuit->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist")));
}
if (! m_valid_topology) {
validate_topology ();
}
@ -275,6 +279,10 @@ const tl::vector<Circuit *> &Netlist::child_circuits (Circuit *circuit)
const tl::vector<Circuit *> &Netlist::parent_circuits (Circuit *circuit)
{
if (circuit->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist")));
}
if (! m_valid_topology) {
validate_topology ();
}
@ -364,18 +372,39 @@ void Netlist::clear ()
void Netlist::add_circuit (Circuit *circuit)
{
if (! circuit) {
return;
}
if (circuit->netlist ()) {
throw tl::Exception (tl::to_string (tr ("Circuit already contained in a netlist")));
}
m_circuits.push_back (circuit);
circuit->set_netlist (this);
}
void Netlist::remove_circuit (Circuit *circuit)
{
if (! circuit) {
return;
}
if (circuit->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist")));
}
circuit->set_netlist (0);
m_circuits.erase (circuit);
}
void Netlist::purge_circuit (Circuit *circuit)
{
if (! circuit) {
return;
}
if (circuit->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist")));
}
circuit->blank ();
remove_circuit (circuit);
}
@ -406,7 +435,12 @@ void Netlist::flatten_circuits (const std::vector<Circuit *> &circuits)
void Netlist::flatten_circuit (Circuit *circuit)
{
tl_assert (circuit != 0);
if (! circuit) {
return;
}
if (circuit->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Circuit not within given netlist")));
}
std::vector<db::SubCircuit *> refs;
for (db::Circuit::refs_iterator sc = circuit->begin_refs (); sc != circuit->end_refs (); ++sc) {
@ -448,24 +482,52 @@ DeviceClass *Netlist::device_class_by_name (const std::string &name)
void Netlist::add_device_class (DeviceClass *device_class)
{
if (! device_class) {
return;
}
if (device_class->netlist ()) {
throw tl::Exception (tl::to_string (tr ("Device class already contained in a netlist")));
}
m_device_classes.push_back (device_class);
device_class->set_netlist (this);
}
void Netlist::remove_device_class (DeviceClass *device_class)
{
if (! device_class) {
return;
}
if (device_class->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Device class not within given netlist")));
}
device_class->set_netlist (0);
m_device_classes.erase (device_class);
}
void Netlist::add_device_abstract (DeviceAbstract *device_abstract)
{
if (! device_abstract) {
return;
}
if (device_abstract->netlist ()) {
throw tl::Exception (tl::to_string (tr ("Device abstract already contained in a netlist")));
}
m_device_abstracts.push_back (device_abstract);
device_abstract->set_netlist (this);
}
void Netlist::remove_device_abstract (DeviceAbstract *device_abstract)
{
if (! device_abstract) {
return;
}
if (device_abstract->netlist () != this) {
throw tl::Exception (tl::to_string (tr ("Device abstract not within given netlist")));
}
device_abstract->set_netlist (0);
m_device_abstracts.erase (device_abstract);
}

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,8 @@ class DeviceCategorizer;
class CircuitCategorizer;
class CircuitMapper;
class NetGraph;
class SubCircuitEquivalenceTracker;
class DeviceEquivalenceTracker;
/**
* @brief A receiver for netlist compare events
@ -285,6 +287,26 @@ public:
return m_max_n_branch;
}
/**
* @brief Sets a value indicating depth-first traversal
*
* With depth first (the default), the algorithm looks for further identities before moving to another
* node. With breadth first (false), the algorithm will work in "waves" rather than digging deerly
* into the direction of a node.
*/
void set_depth_first (bool df)
{
m_depth_first = df;
}
/**
* @brief Gets a value indicating depth-first traversal
*/
bool depth_first () const
{
return m_depth_first;
}
/**
* @brief Gets the list of circuits without matching circuit in the other netlist
* The result can be used to flatten these circuits prior to compare.
@ -326,8 +348,8 @@ protected:
bool all_subcircuits_verified (const db::Circuit *c, const std::set<const db::Circuit *> &verified_circuits) const;
static void derive_pin_equivalence (const db::Circuit *ca, const db::Circuit *cb, CircuitPinMapper *circuit_pin_mapper);
void do_pin_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, std::map<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &c22_circuit_and_pin_mapping, bool &pin_mismatch, bool &good) const;
void do_device_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, const db::DeviceFilter &device_filter, DeviceCategorizer &device_categorizer, bool &good) const;
void do_subcircuit_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, CircuitCategorizer &circuit_categorizer, const db::CircuitPinMapper &circuit_pin_mapper, std::map<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &c22_circuit_and_pin_mapping, bool &good) const;
void do_device_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, const db::DeviceFilter &device_filter, DeviceCategorizer &device_categorizer, db::DeviceEquivalenceTracker &device_eq, bool &good) const;
void do_subcircuit_assignment (const db::Circuit *c1, const db::NetGraph &g1, const db::Circuit *c2, const db::NetGraph &g2, CircuitCategorizer &circuit_categorizer, const db::CircuitPinMapper &circuit_pin_mapper, std::map<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &c22_circuit_and_pin_mapping, db::SubCircuitEquivalenceTracker &subcircuit_eq, bool &good) const;
bool handle_pin_mismatch (const NetGraph &g1, const db::Circuit *c1, const db::Pin *pin1, const NetGraph &g2, const db::Circuit *c2, const db::Pin *p2) const;
mutable NetlistCompareLogger *mp_logger;
@ -339,6 +361,7 @@ protected:
double m_res_threshold;
size_t m_max_n_branch;
size_t m_max_depth;
bool m_depth_first;
bool m_dont_consider_net_names;
};

View File

@ -55,6 +55,39 @@ NetlistCrossReference::per_circuit_data_for (const std::pair<const db::Circuit *
return 0;
}
const db::Pin *
NetlistCrossReference::other_pin_for (const db::Pin *pin) const
{
std::map<const db::Pin *, const db::Pin *>::const_iterator i = m_other_pin.find (pin);
if (i != m_other_pin.end ()) {
return i->second;
} else {
return 0;
}
}
const db::Device *
NetlistCrossReference::other_device_for (const db::Device *device) const
{
std::map<const db::Device *, const db::Device *>::const_iterator i = m_other_device.find (device);
if (i != m_other_device.end ()) {
return i->second;
} else {
return 0;
}
}
const db::SubCircuit *
NetlistCrossReference::other_subcircuit_for (const db::SubCircuit *subcircuit) const
{
std::map<const db::SubCircuit *, const db::SubCircuit *>::const_iterator i = m_other_subcircuit.find (subcircuit);
if (i != m_other_subcircuit.end ()) {
return i->second;
} else {
return 0;
}
}
const db::Circuit *
NetlistCrossReference::other_circuit_for (const db::Circuit *circuit) const
{
@ -568,6 +601,16 @@ NetlistCrossReference::build_subcircuit_pin_refs (const std::pair<const db::Net
}
// Fallback for swappable pins: match based on the subcircuit alone
if (! pb) {
std::map<std::pair<const db::SubCircuit *, size_t>, const db::NetSubcircuitPinRef *>::iterator b = s2t_b.lower_bound (std::make_pair (sb, 0));
if (b != s2t_b.end () && b->first.first == sb) {
pb = b->second;
// remove the entry so we won't find it again
s2t_b.erase (b);
}
}
}
data.subcircuit_pins.push_back (std::make_pair (a->second, pb));

View File

@ -256,6 +256,9 @@ public:
return m_circuits.end ();
}
const db::Pin *other_pin_for (const db::Pin *pin) const;
const db::Device *other_device_for (const db::Device *device) const;
const db::SubCircuit *other_subcircuit_for (const db::SubCircuit *subcircuit) const;
const db::Circuit *other_circuit_for (const db::Circuit *circuit) const;
const db::Net *other_net_for (const db::Net *net) const;
const PerNetData *per_net_data_for (const std::pair<const db::Net *, const db::Net *> &nets) const;

View File

@ -26,6 +26,7 @@
#include "tlClassRegistry.h"
#include "tlStream.h"
#include "tlExpression.h"
#include "tlInternational.h"
namespace db
{
@ -327,7 +328,7 @@ SaveLayoutOptions::get_valid_layers (const db::Layout &layout, std::vector <std:
}
void
SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers) const
SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers, bool require_unique_names) const
{
if (m_all_cells) {
@ -408,6 +409,26 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set <db::cell_index
}
}
if (require_unique_names) {
std::map<std::string, unsigned int> use_count;
for (std::set <db::cell_index_type>::const_iterator c = cells.begin (); c != cells.end (); ++c) {
use_count.insert (std::make_pair (std::string (layout.cell_name (*c)), 0)).first->second += 1;
}
std::vector<std::string> multi;
for (std::map<std::string, unsigned int>::const_iterator u = use_count.begin (); u != use_count.end (); ++u) {
if (u->second > 1) {
multi.push_back (u->first);
}
}
if (! multi.empty ()) {
throw tl::Exception (tl::to_string (tr ("The following cell name(s) are used for more than one cell - can't write this layout:\n ")) + tl::join (multi, "\n "));
}
}
}
bool

View File

@ -413,7 +413,7 @@ public:
*
* It must be given a list of valid layers which is used to determine empty cells if dont_save_empty_cells is true.
*/
void get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers) const;
void get_cells (const db::Layout &layout, std::set <db::cell_index_type> &cells, const std::vector <std::pair <unsigned int, db::LayerProperties> > &valid_layers, bool require_unique_names = true) const;
/**
* @brief Sets a layout reader option by name

View File

@ -546,9 +546,13 @@ ShapeIterator::advance_aref (int &mode)
if (mode && m_array_iterator_valid) {
if (mode > 0) {
if (mode == 1) {
array_iterator *arr_iter = (array_iterator *) m_ad.iter;
++*arr_iter;
} else if (mode == 2) {
// skip array quad -> skip rest of array quad and move to shape in the next quad or to end
do_skip_array_quad ();
mode = 1;
} else {
// skip quad -> skip rest of array and move to next shape array
skip_array (); // sets m_array_iterator_valid = false
@ -810,9 +814,100 @@ ShapeIterator::quad_box () const
return quad_box_generic<OverlappingRegionTag, db::unstable_layer_tag> ();
}
}
return db::Box ();
}
template <class Iter>
void
ShapeIterator::do_skip_array_quad_iter ()
{
Iter *arr_iter = (Iter *) m_ad.iter;
arr_iter->skip_quad ();
}
void
ShapeIterator::do_skip_array_quad ()
{
if (m_array_iterator_valid) {
if (m_type == PolygonPtrArray) {
do_skip_array_quad_iter<polygon_ptr_array_iterator_type> ();
} else if (m_type == SimplePolygonPtrArray) {
do_skip_array_quad_iter<simple_polygon_ptr_array_iterator_type> ();
} else if (m_type == PathPtrArray) {
do_skip_array_quad_iter<path_ptr_array_iterator_type> ();
} else if (m_type == TextPtrArray) {
do_skip_array_quad_iter<text_ptr_array_iterator_type> ();
} else if (m_type == BoxArray) {
do_skip_array_quad_iter<box_array_iterator_type> ();
} else if (m_type == ShortBoxArray) {
do_skip_array_quad_iter<short_box_array_iterator_type> ();
}
}
}
template <class Iter>
size_t
ShapeIterator::get_array_quad_id () const
{
Iter *arr_iter = (Iter *) m_ad.iter;
return arr_iter->quad_id ();
}
size_t
ShapeIterator::array_quad_id () const
{
if (m_array_iterator_valid) {
if (m_type == PolygonPtrArray) {
return get_array_quad_id<polygon_ptr_array_iterator_type> ();
} else if (m_type == SimplePolygonPtrArray) {
return get_array_quad_id<simple_polygon_ptr_array_iterator_type> ();
} else if (m_type == PathPtrArray) {
return get_array_quad_id<path_ptr_array_iterator_type> ();
} else if (m_type == TextPtrArray) {
return get_array_quad_id<text_ptr_array_iterator_type> ();
} else if (m_type == BoxArray) {
return get_array_quad_id<box_array_iterator_type> ();
} else if (m_type == ShortBoxArray) {
return get_array_quad_id<short_box_array_iterator_type> ();
}
}
return 0;
}
template <class Iter, class Array>
db::Box
ShapeIterator::get_array_quad_box () const
{
const Array *arr = m_array.basic_ptr (typename Array::tag ());
Iter *arr_iter = (Iter *) m_ad.iter;
db::box_convert<typename Array::object_type> bc;
return arr->quad_box (*arr_iter, bc);
}
db::Box
ShapeIterator::array_quad_box () const
{
if (m_array_iterator_valid) {
if (m_type == PolygonPtrArray) {
return get_array_quad_box<polygon_ptr_array_iterator_type, polygon_ptr_array_type> ();
} else if (m_type == SimplePolygonPtrArray) {
return get_array_quad_box<simple_polygon_ptr_array_iterator_type, simple_polygon_ptr_array_type> ();
} else if (m_type == PathPtrArray) {
return get_array_quad_box<path_ptr_array_iterator_type, path_ptr_array_type> ();
} else if (m_type == TextPtrArray) {
return get_array_quad_box<text_ptr_array_iterator_type, text_ptr_array_type> ();
} else if (m_type == BoxArray) {
return get_array_quad_box<box_array_iterator_type, box_array_type> ();
} else if (m_type == ShortBoxArray) {
return get_array_quad_box<short_box_array_iterator_type, short_box_array_type> ();
}
}
return db::Box::world ();
}
void
ShapeIterator::cleanup ()
{

View File

@ -319,6 +319,33 @@ public:
advance (-1);
}
/**
* @brief Gets the arrays quad ID
*
* The arrays quad ID is a unique identifier for the current quad for iterated arrays. This can be used to
* detect whether the iterator entered a new quad and optimize the search in that case.
*/
size_t array_quad_id () const;
/**
* @brief Gets the quad box
*
* Gets the box the current quad uses. This box may be larger than the actual shape containers
* bounding box. Specifically if there is no quad tree at all, this box is the world box.
*/
db::Box array_quad_box () const;
/**
* @brief Skips the current quad
*
* Moves to the next quad. This method can be used to shortcut searching if we are inside
* a quad that is not relevant for the search.
*/
void skip_array_quad ()
{
advance (2);
}
private:
// a helper union for the iter_size union
// (basically computing the size required for all iterators for a certain shape/array type)
@ -418,6 +445,12 @@ private:
template <class Sh, class StableTag> db::Box quad_box_by_shape (OverlappingRegionTag) const;
template <class RegionTag, class StableTag> db::Box quad_box_generic () const;
template <class Iter, class Array> db::Box get_array_quad_box () const;
template <class Iter> size_t get_array_quad_id () const;
template <class Iter> void do_skip_array_quad_iter ();
void do_skip_array_quad ();
template <class Sh, class StableTag, class RegionTag> bool advance_shape (int &mode);
template <class Array> void init_array_iter (NoRegionTag);
template <class Array> void init_array_iter (TouchingRegionTag);

View File

@ -305,7 +305,7 @@ LayerMap::prepare (db::Layout &layout)
// In addition, map other existing layers as well, so merging of layout is somewhat better supported
for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
if (mapped_layers.find ((*l).first) == mapped_layers.end ()) {
if (! (*l).second->is_null () && mapped_layers.find ((*l).first) == mapped_layers.end ()) {
map (*(*l).second, (*l).first);
}
}

View File

@ -119,6 +119,17 @@ const Net *SubCircuit::net_for_pin (size_t pin_id) const
return 0;
}
const NetSubcircuitPinRef *SubCircuit::netref_for_pin (size_t pin_id) const
{
if (pin_id < m_pin_refs.size ()) {
Net::subcircuit_pin_iterator p = m_pin_refs [pin_id];
if (p != Net::subcircuit_pin_iterator ()) {
return p.operator-> ();
}
}
return 0;
}
void SubCircuit::connect_pin (size_t pin_id, Net *net)
{
if (net_for_pin (pin_id) == net) {

View File

@ -171,6 +171,21 @@ public:
return const_cast<Net *> (((const SubCircuit *) this)->net_for_pin (pin_id));
}
/**
* @brief Gets the net attached to a specific pin as a subcircuit pin ref object
* Returns 0 if no net is attached.
*/
const NetSubcircuitPinRef *netref_for_pin (size_t pin_id) const;
/**
* @brief Gets the net attached to a specific pin as a subcircuit pin ref object (non-const version)
* Returns 0 if no net is attached.
*/
NetSubcircuitPinRef *netref_for_pin (size_t pin_id)
{
return const_cast<NetSubcircuitPinRef *> (((const SubCircuit *) this)->netref_for_pin (pin_id));
}
/**
* @brief Connects the given pin to the given net
* If the net is 0 the pin is disconnected.

View File

@ -198,43 +198,52 @@ TextWriter::write (const db::Layout &layout)
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
std::string pfx = "";
if (inst->has_prop_id () && inst->prop_id () != 0) {
pfx = "p $props";
write_props (layout, inst->prop_id ());
}
db::Vector a, b;
unsigned long amax, bmax;
bool is_reg = inst->is_regular_array (a, b, amax, bmax);
*this << (is_reg ? "aref" : "sref") << pfx << " {" << layout.cell_name (inst->cell_index ()) << "}";
for (db::CellInstArray::iterator i = inst->begin (); ! i.at_end (); ++i) {
db::Trans t = inst->front ();
std::string pfx = "";
if (inst->has_prop_id () && inst->prop_id () != 0) {
pfx = "p $props";
write_props (layout, inst->prop_id ());
}
*this << (is_reg ? "aref" : "sref") << pfx << " {" << layout.cell_name (inst->cell_index ()) << "}";
db::Trans t = *i;
if (inst->is_complex ()) {
db::CellInstArray::complex_trans_type ct = inst->complex_trans (t);
*this << " " << ct.angle ();
*this << " " << (ct.is_mirror () ? 1 : 0);
*this << " " << ct.mag ();
} else {
*this << " " << (t.rot () % 4) * 90.0;
*this << " " << (t.is_mirror () ? 1 : 0);
*this << " " << 1.0;
}
if (is_reg) {
*this << " " << int (std::max ((unsigned long) 1, amax));
*this << " " << int (std::max ((unsigned long) 1, bmax));
}
*this << " " << t.disp ();
if (is_reg) {
*this << " " << (t.disp () + a * (long) amax);
*this << " " << (t.disp () + b * (long) bmax);
}
*this << endl ();
if (is_reg) {
break;
}
if (inst->is_complex ()) {
*this << " " << inst->complex_trans ().angle ();
*this << " " << (inst->complex_trans ().is_mirror () ? 1 : 0);
*this << " " << inst->complex_trans ().mag ();
} else {
*this << " " << (t.rot () % 4) * 90.0;
*this << " " << (t.is_mirror () ? 1 : 0);
*this << " " << 1.0;
}
if (is_reg) {
*this << " " << int (std::max ((unsigned long) 1, amax));
*this << " " << int (std::max ((unsigned long) 1, bmax));
}
*this << " " << t.disp ();
if (is_reg) {
*this << " " << (t.disp () + a * (long) amax);
*this << " " << (t.disp () + b * (long) bmax);
}
*this << endl ();
}
end_sorted_section ();

View File

@ -251,6 +251,8 @@ struct cell_inst_array_defs
unsigned long na = 1, nb = 1;
if (arr->is_regular_array (a, b, na, nb)) {
*arr = C (arr->object (), t, a, b, na, nb);
} else if (arr->is_iterated_array ()) {
throw tl::Exception (tl::to_string (tr ("Can't set the transformation on an iterated array (layout not editable?)")));
} else {
*arr = C (arr->object (), t);
}
@ -262,6 +264,8 @@ struct cell_inst_array_defs
unsigned long na = 1, nb = 1;
if (arr->is_regular_array (a, b, na, nb)) {
*arr = C (arr->object (), t, a, b, na, nb);
} else if (arr->is_iterated_array ()) {
throw tl::Exception (tl::to_string (tr ("Can't set the transformation on an iterated array (layout not editable?)")));
} else {
*arr = C (arr->object (), t);
}
@ -292,6 +296,8 @@ struct cell_inst_array_defs
s += "*";
s += tl::to_string (nb);
s += "]";
} else if (arr->size () > 1) {
s += std::string (" (+") + tl::to_string (arr->size () - 1) + " irregular locations)";
}
return s;
@ -303,14 +309,27 @@ struct cell_inst_array_defs
{
typedef db::array<db::CellInst, db::simple_trans<typename T::target_coord_type> > target_array;
std::vector<typename C::vector_type> iterated;
std::vector<typename target_array::vector_type> iterated_transformed;
typename C::vector_type a, b;
unsigned long amax = 0, bmax = 0;
if (arr.is_regular_array (a, b, amax, bmax)) {
if (arr.is_complex ()) {
return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), t * a, t * b, amax, bmax);
} else {
return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), t * a, t * b, amax, bmax);
}
} else if (arr.is_iterated_array (&iterated)) {
iterated_transformed.reserve (iterated.size ());
for (typename std::vector<typename C::vector_type>::const_iterator i = iterated.begin (); i != iterated.end (); ++i) {
iterated_transformed.push_back (t * *i);
}
if (arr.is_complex ()) {
return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), iterated_transformed.begin (), iterated_transformed.end ());
} else {
return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), iterated_transformed.begin (), iterated_transformed.end ());
}
} else if (arr.is_complex ()) {
return target_array (arr.object (), t * arr.complex_trans () * t.inverted ());
} else {
@ -352,93 +371,17 @@ struct cell_inst_array_defs
static bool less (const C *i, const C &other)
{
if (i->object ().cell_index () != other.object ().cell_index ()) {
return i->object ().cell_index () < other.object ().cell_index ();
}
db::vector<coord_type> a, b;
unsigned long na = 1, nb = 1;
bool r = i->is_regular_array (a, b, na, nb);
db::vector<coord_type> aother, bother;
unsigned long naother = 1, nbother = 1;
bool rother = other.is_regular_array (aother, bother, naother, nbother);
if (r != rother) {
return r < rother;
} else if (r) {
if (a.not_equal (aother)) {
return a.less (aother);
}
if (b.not_equal (bother)) {
return b.less (bother);
}
if (na != naother) {
return na < naother;
}
if (nb != nbother) {
return nb < nbother;
}
}
bool c = i->is_complex ();
bool cother = other.is_complex ();
if (c != cother) {
return c < cother;
} else if (c) {
return i->complex_trans ().less (other.complex_trans ());
} else {
return i->front ().less (other.front ());
}
return i->less (other);
}
static bool equal (const C *i, const C &other)
{
if (i->object ().cell_index () != other.object ().cell_index ()) {
return false;
}
db::vector<coord_type> a, b;
unsigned long na = 1, nb = 1;
bool r = i->is_regular_array (a, b, na, nb);
db::vector<coord_type> aother, bother;
unsigned long naother = 1, nbother = 1;
bool rother = other.is_regular_array (aother, bother, naother, nbother);
if (r != rother) {
return false;
} else if (r) {
if (a.not_equal (aother)) {
return false;
}
if (b.not_equal (bother)) {
return false;
}
if (na != naother) {
return false;
}
if (nb != nbother) {
return false;
}
}
bool c = i->is_complex ();
bool cother = other.is_complex ();
if (c != cother) {
return false;
} else if (c) {
return i->complex_trans ().equal (other.complex_trans ());
} else {
return i->front ().equal (other.front ());
}
return i->equal (other);
}
static bool not_equal (const C *i, const C &other)
{
return ! equal (i, other);
return ! i->equal (other);
}
static gsi::Methods methods (bool new_doc)
@ -508,7 +451,9 @@ struct cell_inst_array_defs
) +
gsi::method ("size", &C::size,
"@brief Gets the number of single instances in the array\n"
"If the instance represents a single instance, the count is 1. Otherwise it is na*nb."
"If the instance represents a single instance, the count is 1. Otherwise it is na*nb. "
"Starting with version 0.27, there may be iterated instances for which the size is larger than 1, but \\is_regular_array? will return false. "
"In this case, use \\each_trans or \\each_cplx_trans to retrieve the individual placements of the iterated instance."
) +
gsi::method_ext ("cell_index", &cell_index,
"@brief Gets the cell index of the cell instantiated \n"
@ -1618,6 +1563,28 @@ static db::Instance cell_inst_dtransform_into_cplx (db::Cell *cell, const db::In
return cell->transform_into (inst, dbu_trans.inverted () * t * dbu_trans);
}
static void cell_dtransform_simple (db::Cell *cell, const db::DTrans &t)
{
const db::Layout *layout = cell->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation")));
}
db::CplxTrans dbu_trans (layout->dbu ());
cell->transform (db::Trans (dbu_trans.inverted () * db::DCplxTrans (t) * dbu_trans));
}
static void cell_dtransform_cplx (db::Cell *cell, const db::DCplxTrans &t)
{
const db::Layout *layout = cell->layout ();
if (! layout) {
throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit transformation")));
}
db::CplxTrans dbu_trans (layout->dbu ());
cell->transform (dbu_trans.inverted () * t * dbu_trans);
}
static void cell_dtransform_into_simple (db::Cell *cell, const db::DTrans &t)
{
const db::Layout *layout = cell->layout ();
@ -2402,6 +2369,46 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"This variant has been introduced in version 0.25."
) +
gsi::method ("transform", (void (db::Cell::*)(const db::Trans &)) &db::Cell::transform, gsi::arg ("trans"),
"@brief Transforms the cell by the given integer transformation\n"
"\n"
"This method transforms all instances and all shapes by the given transformation. "
"There is a variant called \\transform_into which applies the transformation to instances "
"in a way such that it can be applied recursively to the child cells.\n"
"\n"
"This method has been introduced in version 0.26.7."
) +
gsi::method ("transform", (void (db::Cell::*)(const db::ICplxTrans &)) &db::Cell::transform, gsi::arg ("trans"),
"@brief Transforms the cell by the given complex integer transformation\n"
"\n"
"This method transforms all instances and all shapes by the given transformation. "
"There is a variant called \\transform_into which applies the transformation to instances "
"in a way such that it can be applied recursively to the child cells. The difference is important in "
"the presence of magnifications: \"transform\" will leave magnified instances while \"transform_into\" "
"will not do so but expect the magnification to be applied inside the called cells too.\n"
"\n"
"This method has been introduced in version 0.26.7."
) +
gsi::method_ext ("transform", &cell_dtransform_simple, gsi::arg ("trans"),
"@brief Transforms the cell by the given, micrometer-unit transformation\n"
"\n"
"This method transforms all instances and all shapes by the given transformation. "
"There is a variant called \\transform_into which applies the transformation to instances "
"in a way such that it can be applied recursively to the child cells.\n"
"\n"
"This method has been introduced in version 0.26.7."
) +
gsi::method_ext ("transform", &cell_dtransform_cplx, gsi::arg ("trans"),
"@brief Transforms the cell by the given, micrometer-unit transformation\n"
"\n"
"This method transforms all instances and all shapes by the given transformation. "
"There is a variant called \\transform_into which applies the transformation to instances "
"in a way such that it can be applied recursively to the child cells. The difference is important in "
"the presence of magnifications: \"transform\" will leave magnified instances while \"transform_into\" "
"will not do so but expect the magnification to be applied inside the called cells too.\n"
"\n"
"This method has been introduced in version 0.26.7."
) +
gsi::method_ext ("transform_into", &cell_dtransform_into_simple, gsi::arg ("trans"),
"@brief Transforms the cell into a new coordinate system with the given transformation where the transformation is in micrometer units\n"
"This method is identical to the corresponding \\transform_into method with a \\Trans argument. For this variant "

View File

@ -543,7 +543,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::LayoutToNetlist::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"),
"@brief Like \\build_all_nets, but with the ability to select some nets."
) +
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"),
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &, std::vector<db::SubCircuit *> *, db::Circuit *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector<db::SubCircuit *> *) 0, "nil"), gsi::arg ("initial_circuit", (db::Circuit *) 0, "nil"),
"@brief Finds the net by probing a specific location on the given layer\n"
"\n"
"This method will find a net looking at the given layer at the specific position.\n"
@ -551,20 +551,30 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"in the specified location. The function will report the topmost net from far above the\n"
"hierarchy of circuits as possible.\n"
"\n"
"If \\initial_circuit is given, the probing will start from this circuit and from the "
"cell this circuit represents. By default, the probing will start from the top circuit.\n"
"\n"
"If no net is found at all, 0 is returned.\n"
"\n"
"It is recommended to use \\probe on the netlist right after extraction.\n"
"It is recommended to use \\probe_net on the netlist right after extraction.\n"
"Optimization functions such as \\Netlist#purge will remove parts of the net which means\n"
"shape to net probing may no longer work for these nets.\n"
"\n"
"If non-null and an array, 'sc_path_out' will receive a list of \\SubCircuits objects which lead to the "
"net from the top circuit of the database.\n"
"\n"
"This variant accepts a micrometer-unit location. The location is given in the\n"
"coordinate space of the initial cell.\n"
"\n"
"The \\sc_path_out and \\initial_circuit parameters have been added in version 0.27.\n"
) +
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"),
gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::Point &, std::vector<db::SubCircuit *> *, db::Circuit *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector<db::SubCircuit *> *) 0, "nil"), gsi::arg ("initial_circuit", (db::Circuit *) 0, "nil"),
"@brief Finds the net by probing a specific location on the given layer\n"
"See the description of the other \\probe_net variant.\n"
"This variant accepts a database-unit location. The location is given in the\n"
"coordinate space of the initial cell.\n"
"\n"
"The \\sc_path_out and \\initial_circuit parameters have been added in version 0.27.\n"
) +
gsi::method ("write|write_l2n", &db::LayoutToNetlist::save, gsi::arg ("path"), gsi::arg ("short_format", false),
"@brief Writes the extracted netlist to a file.\n"

View File

@ -78,7 +78,18 @@ static std::string get_technology (db::Library *lib)
}
}
static void dummy_destroy (db::Library *) { }
Class<db::Library> decl_Library ("db", "Library",
gsi::method_ext ("_destroy", &dummy_destroy,
"@brief An inactive substitute for _destroy (delete the object)\n"
"As libraries need to be kept if cells are using them, library objects must "
"not be deleted. Hence the default '_destroy' implementation must not be called. "
"To keep old code working, this substitute is provided. It just returns without "
"deleting the object.\n"
"\n"
"This method has been introduced in version 0.26.7."
) +
gsi::constructor ("new", &new_lib,
"@brief Creates a new, empty library"
) +

View File

@ -466,6 +466,21 @@ Class<db::SubCircuit> decl_dbSubCircuit (decl_dbNetlistObject, "db", "SubCircuit
gsi::method_ext ("disconnect_pin", &gsi::subcircuit_disconnect_pin1, gsi::arg ("pin"),
"@brief Disconnects the given pin from any net.\n"
"This version takes a \\Pin reference instead of a pin ID."
) +
gsi::method ("trans", &db::SubCircuit::trans,
"@brief Gets the physical transformation for the subcircuit.\n"
"\n"
"This property applies to subcircuits derived from a layout. It specifies the "
"placement of the respective cell.\n"
"\n"
"This property has been introduced in version 0.27."
) +
gsi::method ("trans=", &db::SubCircuit::set_trans, gsi::arg ("trans"),
"@brief Sets the physical transformation for the subcircuit.\n"
"\n"
"See \\trans for details about this property.\n"
"\n"
"This property has been introduced in version 0.27."
),
"@brief A subcircuit inside a circuit.\n"
"Circuits may instantiate other circuits as subcircuits similar to cells "

View File

@ -538,6 +538,18 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"@brief Gets the maximum branch complexity\n"
"See \\max_branch_complexity= for details."
) +
gsi::method ("dont_consider_net_names=", &db::NetlistComparer::set_dont_consider_net_names, gsi::arg ("f"),
"@brief Sets a value indicating whether net names shall not be considered\n"
"If this value is set to true, net names will not be considered when resolving ambiguities.\n"
"Not considering net names usually is more expensive. The default is 'false' indicating that\n"
"net names will be considered for ambiguity resolution.\n"
"\n"
"This property has been introduced in version 0.26.7.\n"
) +
gsi::method ("dont_consider_net_names", &db::NetlistComparer::dont_consider_net_names,
"@brief Gets a value indicating whether net names shall not be considered\n"
"See \\dont_consider_net_names= for details."
) +
gsi::method_ext ("unmatched_circuits_a", &unmatched_circuits_a, gsi::arg ("a"), gsi::arg ("b"),
"@brief Returns a list of circuits in A for which there is not corresponding circuit in B\n"
"This list can be used to flatten these circuits so they do not participate in the compare process.\n"

View File

@ -403,6 +403,34 @@ Class<db::NetlistCrossReference> decl_dbNetlistCrossReference (decl_dbNetlistCom
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' net for nets from the 'a' netlist and vice versa."
) +
gsi::method ("other_circuit_for", &db::NetlistCrossReference::other_circuit_for, gsi::arg ("circuit"),
"@brief Gets the matching other circuit for a given primary circuit.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' circuit for circuits from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("other_device_for", &db::NetlistCrossReference::other_device_for, gsi::arg ("device"),
"@brief Gets the matching other device for a given primary device.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' device for devices from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("other_pin_for", &db::NetlistCrossReference::other_pin_for, gsi::arg ("pin"),
"@brief Gets the matching other pin for a given primary pin.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' pin for pins from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("other_subcircuit_for", &db::NetlistCrossReference::other_subcircuit_for, gsi::arg ("subcircuit"),
"@brief Gets the matching other subcircuit for a given primary subcircuit.\n"
"The return value will be nil if no match is found. "
"Otherwise it is the 'b' subcircuit for subcircuits from the 'a' netlist and vice versa."
"\n\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("clear", &db::NetlistCrossReference::clear,
"@hide\n"
) +

View File

@ -1031,4 +1031,92 @@ TEST(6U)
}
TEST(7)
{
Box2Box conv;
TestTree t;
int n = 200000;
for (int i = n - 1; i >= 0; --i) {
t.insert (db::Box (i * 10, 0, i * 10 + 5, 5));
}
t.sort (conv);
{
tl::SelfTimer timer ("test 7 lookup");
size_t n = 0;
for (unsigned int i = 0; i < 2000; ++i) {
db::Coord sx = 0, sy = 0;
TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv);
while (!it.at_end ()) {
sx += abs (it->left ());
sy += abs (it->bottom ());
++it;
++n;
}
EXPECT_EQ (sx, 252500);
EXPECT_EQ (sy, 0);
}
EXPECT_EQ (n, size_t (101 * 2000));
}
{
tl::SelfTimer timer ("test 7 traverse");
db::Coord m = std::numeric_limits<db::Coord>::max ();
size_t n = 0;
for (unsigned int i = 0; i < 10; ++i) {
TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv);
while (!it.at_end ()) {
++it;
++n;
}
}
EXPECT_EQ (n, t.size () * 10);
}
}
TEST(7U)
{
Box2Box conv;
UnstableTestTree t;
int n = 200000;
for (int i = n - 1; i >= 0; --i) {
t.insert (db::Box (i * 10, 0, i * 10 + 5, 5));
}
t.sort (conv);
{
tl::SelfTimer timer ("test 7U lookup");
size_t n = 0;
for (unsigned int i = 0; i < 2000; ++i) {
db::Coord sx = 0, sy = 0;
UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv);
while (!it.at_end ()) {
sx += abs (it->left ());
sy += abs (it->bottom ());
++it;
++n;
}
EXPECT_EQ (sx, 252500);
EXPECT_EQ (sy, 0);
}
EXPECT_EQ (n, size_t (101 * 2000));
}
{
tl::SelfTimer timer ("test 7U traverse");
db::Coord m = std::numeric_limits<db::Coord>::max ();
size_t n = 0;
for (unsigned int i = 0; i < 10; ++i) {
UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv);
while (!it.at_end ()) {
++it;
++n;
}
}
EXPECT_EQ (n, t.size () * 10);
}
}

View File

@ -722,6 +722,14 @@ TEST(3a)
c0.transform_into (db::ICplxTrans (ti));
inst = *c0.begin ();
EXPECT_EQ (inst.to_string (), "cell_index=1 m90 -334,0");
c0.transform (db::Trans (5));
inst = *c0.begin ();
EXPECT_EQ (inst.to_string (), "cell_index=1 r270 0,-334");
c0.transform (db::ICplxTrans (ti));
inst = *c0.begin ();
EXPECT_EQ (inst.to_string (), "cell_index=1 r315 *2.5 600,-570");
}
TEST(3b)
@ -791,6 +799,73 @@ TEST(3b)
}
}
TEST(3c)
{
::pi = 0;
db::Manager m (true);
db::Layout g (&m);
db::Cell &c0 (g.cell (g.add_cell ()));
db::Cell &c1 (g.cell (g.add_cell ()));
db::Trans t (db::Vector (100, -100));
c0.insert (db::CellInstArrayWithProperties (db::CellInstArray (db::CellInst (c1.cell_index ()), t), 5));
db::Box b (0, 100, 1000, 1200);
c0.shapes (0).insert (db::BoxWithProperties (b, 17));
c1.shapes (1).insert (b);
// Note: this requires editable mode since db::Shapes::erase is permitted in editable mode only
// (erase is triggered by undo)
if (db::default_editable_mode ()) {
m.transaction ("t");
c0.transform (db::ICplxTrans (2.5));
m.commit ();
EXPECT_EQ (c1.cell_instances (), size_t (0));
EXPECT_EQ (c0.cell_instances (), size_t (1));
EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 *2.5 250,-250 prop_id=5");
EXPECT_EQ (c0.shapes (0).size (), size_t (1));
EXPECT_EQ (c0.shapes (1).size (), size_t (0));
EXPECT_EQ (c1.shapes (0).size (), size_t (0));
EXPECT_EQ (c1.shapes (1).size (), size_t (1));
EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,250;2500,3000) prop_id=17");
EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)");
m.undo ();
EXPECT_EQ (c1.cell_instances (), size_t (0));
EXPECT_EQ (c0.cell_instances (), size_t (1));
EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 100,-100 prop_id=5");
EXPECT_EQ (c0.shapes (0).size (), size_t (1));
EXPECT_EQ (c0.shapes (1).size (), size_t (0));
EXPECT_EQ (c1.shapes (0).size (), size_t (0));
EXPECT_EQ (c1.shapes (1).size (), size_t (1));
EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200) prop_id=17");
EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)");
m.redo ();
EXPECT_EQ (c1.cell_instances (), size_t (0));
EXPECT_EQ (c0.cell_instances (), size_t (1));
EXPECT_EQ (c0.begin ()->to_string (), "cell_index=1 r0 *2.5 250,-250 prop_id=5");
EXPECT_EQ (c0.shapes (0).size (), size_t (1));
EXPECT_EQ (c0.shapes (1).size (), size_t (0));
EXPECT_EQ (c1.shapes (0).size (), size_t (0));
EXPECT_EQ (c1.shapes (1).size (), size_t (1));
EXPECT_EQ (c0.shapes (0).begin (db::ShapeIterator::All)->to_string (), "box (0,250;2500,3000) prop_id=17");
EXPECT_EQ (c1.shapes (1).begin (db::ShapeIterator::All)->to_string (), "box (0,100;1000,1200)");
}
}
struct map1
{
db::cell_index_type operator() (db::cell_index_type i) const { return 3-i; }

View File

@ -1287,3 +1287,69 @@ TEST(120_HierClustersCombArrays)
run_hc_test (_this, "comb2.gds", "comb2_au1.gds");
run_hc_test_with_backannotation (_this, "comb2.gds", "comb2_au2.gds");
}
static size_t root_nets (const db::connected_clusters<db::PolygonRef> &cc)
{
size_t n = 0;
for (db::connected_clusters<db::PolygonRef>::all_iterator c = cc.begin_all (); !c.at_end (); ++c) {
if (cc.is_root (*c)) {
++n;
}
}
return n;
}
// issue #609
TEST(200_issue609)
{
db::Layout ly;
unsigned int l1 = 0, l2 = 0;
{
db::LayerProperties p;
db::LayerMap lmap;
p.layer = 1;
p.datatype = 0;
lmap.map (db::LDPair (p.layer, p.datatype), l1 = ly.insert_layer ());
ly.set_properties (l1, p);
p.layer = 2;
p.datatype = 0;
lmap.map (db::LDPair (p.layer, p.datatype), l2 = ly.insert_layer ());
ly.set_properties (l2, p);
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn += "/testdata/algo/issue-609.oas.gz";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
std::vector<std::string> strings;
normalize_layer (ly, strings, l1);
normalize_layer (ly, strings, l2);
// connect 1 to 1, 1 to 2
db::Connectivity conn;
conn.connect (l1, l1);
conn.connect (l2, l2);
conn.connect (l1, l2);
db::hier_clusters<db::PolygonRef> hc;
hc.build (ly, ly.cell (*ly.begin_top_down ()), conn);
db::Layout::top_down_const_iterator td = ly.begin_top_down ();
EXPECT_EQ (td != ly.end_top_down (), true);
EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (1));
++td;
// result needs to be a single net
for ( ; td != ly.end_top_down (); ++td) {
EXPECT_EQ (root_nets (hc.clusters_per_cell (*td)), size_t (0));
}
}

View File

@ -97,6 +97,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties &
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]";
} else if (ci.size () > 1) {
m_os << " (+" << (ci.size () - 1) << " irregular placements)";
}
if (ci.properties_id () != 0) {
m_os << " [" << ci.properties_id () << "]" << std::endl;
@ -112,6 +114,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties &
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]";
} else if (ci.size () > 1) {
m_os << " (+" << (ci.size () - 1) << " irregular placements)";
}
if (ci.properties_id () != 0) {
m_os << " [" << ci.properties_id () << "]" << std::endl;

View File

@ -449,6 +449,7 @@ TEST(1_SimpleInverter)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -470,6 +471,7 @@ TEST(1_SimpleInverter)
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
comp.set_dont_consider_net_names (true);
good = comp_xref.compare (&nl1, &nl2);
@ -519,6 +521,7 @@ TEST(1_SimpleInverterMatchedDeviceClasses)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
comp.same_device_classes (nl1.device_class_by_name ("PMOS"), nl2.device_class_by_name ("PMOSB"));
bool good = comp.compare (&nl1, &nl2);
@ -571,6 +574,7 @@ TEST(1_SimpleInverterSkippedDevices)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -594,6 +598,7 @@ TEST(1_SimpleInverterSkippedDevices)
db::NetlistCrossReference xref;
db::NetlistComparer comp_xref (&xref);
comp.set_dont_consider_net_names (true);
good = comp_xref.compare (&nl1, &nl2);
@ -751,6 +756,8 @@ TEST(2_SimpleInverterWithForcedNetAssignment)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
const db::Circuit *ca = nl1.circuit_by_name ("INV");
const db::Circuit *cb = nl2.circuit_by_name ("INV");
comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD"));
@ -797,6 +804,7 @@ TEST(3_Buffer)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -852,6 +860,7 @@ TEST(4_BufferTwoPaths)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -912,6 +921,7 @@ TEST(5_BufferTwoPathsDifferentParameters)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// Forcing the power nets into equality makes the parameter error harder to detect
const db::Circuit *ca = nl1.circuit_by_name ("BUF");
@ -1134,6 +1144,7 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// NOTE: adding this power hint makes the device class error harder to detect
const db::Circuit *ca = nl1.circuit_by_name ("BUF");
@ -1147,8 +1158,8 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses)
"begin_circuit BUF BUF\n"
"match_nets INT $10\n"
"match_nets IN IN\n"
"net_mismatch OUT OUT\n"
"net_mismatch INT2 $11\n"
"net_mismatch OUT OUT\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1199,6 +1210,7 @@ TEST(6_BufferTwoPathsAdditionalResistor)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// Forcing the power nets into equality makes the resistor error harder to detect
const db::Circuit *ca = nl1.circuit_by_name ("BUF");
@ -1212,8 +1224,35 @@ TEST(6_BufferTwoPathsAdditionalResistor)
"begin_circuit BUF BUF\n"
"net_mismatch INT $10\n"
"match_nets IN IN\n"
"net_mismatch INT2 $11\n"
"match_nets OUT OUT\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices $6 $7\n"
"match_devices $8 $8\n"
"device_mismatch (null) $9\n"
"end_circuit BUF BUF NOMATCH"
);
EXPECT_EQ (good, false);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"net_mismatch INT $10\n"
"match_nets OUT OUT\n"
"net_mismatch INT2 $11\n"
"match_nets IN IN\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1266,6 +1305,7 @@ TEST(6_BufferTwoPathsAdditionalDevices)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1274,9 +1314,9 @@ TEST(6_BufferTwoPathsAdditionalDevices)
"match_nets INT $11\n"
"net_mismatch VDD VDD\n"
"match_nets IN IN\n"
"net_mismatch INT2 $10\n"
"net_mismatch VSS VSS\n"
"net_mismatch OUT OUT\n"
"net_mismatch INT2 $10\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
@ -1318,6 +1358,7 @@ TEST(7_Resistors)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1359,6 +1400,7 @@ TEST(7_ResistorsParameterMismatch)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1401,6 +1443,7 @@ TEST(7_ResistorsPlusOneDevice)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1443,6 +1486,7 @@ TEST(8_Diodes)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1484,6 +1528,7 @@ TEST(8_DiodesDontMatchOnSwappedPins)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1531,6 +1576,7 @@ TEST(10_SimpleSubCircuits)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1593,6 +1639,7 @@ TEST(10_SimpleSubCircuitsMatchedNames)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
EXPECT_EQ (good, false);
@ -1761,22 +1808,22 @@ TEST(11_MismatchingSubcircuits)
// nets are now ambiguous
EXPECT_EQ (xref2s (xref),
"TOP:TOP [Match]:\n"
" pin $0:$0 [Match]\n"
" pin $1:$1 [Match]\n"
" pin $2:$2 [Match]\n"
" pin $0:$2 [Match]\n"
" pin $1:$0 [Match]\n"
" pin $2:$1 [Match]\n"
" pin $3:$3 [Match]\n"
" net IN:OUT [MatchWithWarning]\n"
" pin $0:$0\n"
" subcircuit_pin (null):$1[$3]\n"
" net IN:IN [MatchWithWarning]\n"
" pin $0:$2\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$0]:(null)\n"
" net OUT:VDD [MatchWithWarning]\n"
" pin $1:$1\n"
" net OUT:OUT [MatchWithWarning]\n"
" pin $1:$0\n"
" subcircuit_pin (null):$1[$3]\n"
" subcircuit_pin $2[$1]:(null)\n"
" net VDD:VDD [MatchWithWarning]\n"
" pin $2:$1\n"
" subcircuit_pin (null):$1[$0]\n"
" subcircuit_pin (null):$2[$0]\n"
" subcircuit_pin $2[$1]:(null)\n"
" net VDD:IN [MatchWithWarning]\n"
" pin $2:$2\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$2]:(null)\n"
" subcircuit_pin $2[$2]:(null)\n"
" net VSS:VSS [MatchWithWarning]\n"
@ -1818,6 +1865,7 @@ TEST(12_MismatchingSubcircuitsDuplicates)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1885,6 +1933,7 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -1952,6 +2001,7 @@ TEST(14_Subcircuit2Nand)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1);
bool good = comp.compare (&nl1, &nl2);
@ -2026,6 +2076,7 @@ TEST(14_Subcircuit2NandMismatchNoSwap)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
// intentionally missing: comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1);
bool good = comp.compare (&nl1, &nl2);
@ -2116,17 +2167,14 @@ TEST(14_Subcircuit2NandMismatchNoSwap)
" pin $4:$4 [Match]\n"
" net IN1:INT [Mismatch]\n"
" pin $0:(null)\n"
" subcircuit_pin (null):$2[$2]\n"
" subcircuit_pin $1[$0]:(null)\n"
" subcircuit_pin $1[$0]:$2[$2]\n"
" subcircuit_pin $2[$0]:$1[$0]\n"
" net IN2:IN2 [Mismatch]\n"
" pin $1:$1\n"
" subcircuit_pin (null):$2[$0]\n"
" subcircuit_pin $1[$1]:(null)\n"
" subcircuit_pin $1[$1]:$2[$0]\n"
" net INT:IN1 [Mismatch]\n"
" pin (null):$0\n"
" subcircuit_pin (null):$2[$1]\n"
" subcircuit_pin $1[$2]:(null)\n"
" subcircuit_pin $1[$2]:$2[$1]\n"
" subcircuit_pin $2[$1]:$1[$1]\n"
" net OUT:OUT [Match]\n"
" pin $2:$2\n"
@ -2177,6 +2225,7 @@ TEST(14_Subcircuit2MatchWithSwap)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1);
bool good = comp.compare (&nl1, &nl2);
@ -2256,6 +2305,7 @@ TEST(15_EmptySubCircuitTest)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2331,6 +2381,7 @@ TEST(15_EmptySubCircuitWithoutPinNames)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2413,6 +2464,7 @@ TEST(16_UniqueSubCircuitMatching)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2552,8 +2604,8 @@ TEST(17_InherentlyAmbiguousDecoder)
"match_ambiguous_nets NQ1 NQ1\n"
"match_ambiguous_nets NQ2 NQ2\n"
"match_nets NQ3 NQ3\n"
"match_ambiguous_nets NA NA\n"
"match_ambiguous_nets NB NB\n"
"match_nets NA NA\n"
"match_nets NB NB\n"
"match_nets B B\n"
"match_nets A A\n"
"match_pins $0 $1\n"
@ -2657,10 +2709,61 @@ TEST(17_InherentlyAmbiguousDecoder)
"match_nets B B\n"
"match_nets NB NB\n"
"match_nets NA NA\n"
"match_nets NQ0 NQ0\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ1 NQ1\n"
"match_nets NQ3 NQ3\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ0 NQ0\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
"match_pins $3 $3\n"
"match_pins $4 $4\n"
"match_pins $5 $5\n"
"match_pins $6 $6\n"
"match_pins $7 $7\n"
"match_subcircuits $1 $1\n"
"match_subcircuits $2 $2\n"
"match_subcircuits $4 $3\n"
"match_subcircuits $6 $4\n"
"match_subcircuits $3 $5\n"
"match_subcircuits $5 $6\n"
"end_circuit DECODER DECODER MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit NAND NAND\n"
"match_nets VSS VSS\n"
"match_nets INT INT\n"
"match_nets OUT OUT\n"
"match_nets VDD VDD\n"
"match_nets B B\n"
"match_nets A A\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
"match_pins $3 $3\n"
"match_pins $4 $4\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"match_devices $3 $3\n"
"match_devices $4 $4\n"
"end_circuit NAND NAND MATCH\n"
"begin_circuit DECODER DECODER\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets NA NA\n"
"match_nets NB NB\n"
"match_nets B B\n"
"match_nets NQ1 NQ1\n"
"match_nets NQ3 NQ3\n"
"match_nets NQ2 NQ2\n"
"match_nets NQ0 NQ0\n"
"match_pins $0 $1\n"
"match_pins $1 $0\n"
"match_pins $2 $2\n"
@ -2735,6 +2838,7 @@ TEST(18_ClockTree)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (false);
bool good = comp.compare (&nl1, &nl2);
@ -2744,6 +2848,190 @@ TEST(18_ClockTree)
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_nets SX SX\n"
"match_nets SX SX\n"
"match_nets SXX SXX\n"
"match_nets SXX SXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXX SXX\n"
"match_nets SXX SXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
comp.set_dont_consider_net_names (true);
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit TXEE TXEE\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets S S\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SX SX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXX SXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_ambiguous_nets SXXX SXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits T T\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TX TX\n"
"match_subcircuits TXX TXX\n"
"match_subcircuits TXXX TXXX\n"
"match_subcircuits TXX TXX\n"
"end_circuit TXEE TXEE MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
// because L/R matching is ambiguous, we need to do this to
// establish reproducability on different platforms:
txt = tl::replaced (txt, "L", "X");
txt = tl::replaced (txt, "R", "X");
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
@ -2903,6 +3191,7 @@ TEST(19_SymmetricCircuit)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -2922,24 +3211,123 @@ TEST(19_SymmetricCircuit)
"match_nets g1 G1\n"
"match_nets $44 YI\n"
"match_nets $14 WELL\n"
"match_nets $8 NET215\n"
"match_nets $9 NET175\n"
"match_nets $6 NET181\n"
"match_nets $4 NET200\n"
"match_nets nn1 NN1\n"
"match_ambiguous_nets nn2 NN2\n"
"match_ambiguous_nets nn2_ NN2_\n"
"match_ambiguous_nets q0 Q0\n"
"match_ambiguous_nets q1 Q1\n"
"match_nets $11 CS0\n"
"match_nets $13 CS1\n"
"match_nets nn2 NN2\n"
"match_nets nn2_ NN2_\n"
"match_nets q0 Q0\n"
"match_nets q1 Q1\n"
"match_nets q0_ Q0_\n"
"match_nets $6 NET181\n"
"match_nets nn1 NN1\n"
"match_nets $8 NET215\n"
"match_nets $13 CS1\n"
"match_nets q1_ Q1_\n"
"match_nets a0_ A0_\n"
"match_nets $34 HNET48\n"
"match_nets nn1_ NN1_\n"
"match_nets a0 A0\n"
"match_nets $35 HNET44\n"
"match_nets nn1_ NN1_\n"
"match_nets $9 NET175\n"
"match_nets $4 NET200\n"
"match_nets a0_ A0_\n"
"match_nets $34 HNET48\n"
"match_pins VDD VDD\n"
"match_pins nn1_ NN1_\n"
"match_pins nn1 NN1\n"
"match_pins q0 Q0\n"
"match_pins q0_ Q0_\n"
"match_pins q1_ Q1_\n"
"match_pins q1 Q1\n"
"match_pins nn2 NN2\n"
"match_pins nn2_ NN2_\n"
"match_pins a0 A0\n"
"match_pins a0_ A0_\n"
"match_pins g1 G1\n"
"match_pins g0 G0\n"
"match_pins gtp NN3\n"
"match_pins VSS VSS\n"
"match_pins WELL WELL\n"
"match_devices $30 0\n"
"match_devices $29 1\n"
"match_devices $9 10\n"
"match_devices $10 11\n"
"match_devices $36 12\n"
"match_devices $35 13\n"
"match_devices $34 14\n"
"match_devices $38 15\n"
"match_devices $37 16\n"
"match_devices $33 17\n"
"match_devices $27 18\n"
"match_devices $28 19\n"
"match_devices $17 2\n"
"match_devices $31 20\n"
"match_devices $32 21\n"
"match_devices $22 22\n"
"match_devices $26 23\n"
"match_devices $23 24\n"
"match_devices $43 25\n"
"match_devices $20 26\n"
"match_devices $25 27\n"
"match_devices $15 28\n"
"match_devices $14 29\n"
"match_devices $16 3\n"
"match_devices $18 30\n"
"match_devices $21 31\n"
"match_devices $13 32\n"
"match_devices $19 33\n"
"match_devices $7 34\n"
"match_devices $8 35\n"
"match_devices $24 36\n"
"match_devices $3 37\n"
"match_devices $6 38\n"
"match_devices $4 39\n"
"match_devices $39 4\n"
"match_devices $5 40\n"
"match_devices $2 41\n"
"match_devices $1 42\n"
"match_devices $40 5\n"
"match_devices $11 6\n"
"match_devices $12 7\n"
"match_devices $41 8\n"
"match_devices $42 9\n"
"end_circuit DECODE DECODE MATCH"
);
EXPECT_EQ (good, true);
comp.set_depth_first (false);
logger.clear ();
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit DECODE DECODE\n"
"match_nets $41 WL1_EN_\n"
"match_nets VDD VDD\n"
"match_nets $39 NET194\n"
"match_nets g0 G0\n"
"match_nets $40 HNET52\n"
"match_nets VSS VSS\n"
"match_nets $42 NET189\n"
"match_nets gtp NN3\n"
"match_nets $37 NET193\n"
"match_nets g1 G1\n"
"match_nets $44 YI\n"
"match_nets $14 WELL\n"
"match_ambiguous_nets nn2 NN2\n"
"match_ambiguous_nets nn2_ NN2_\n"
"match_ambiguous_nets q0 Q0\n"
"match_ambiguous_nets q1 Q1\n"
"match_nets $11 CS0\n"
"match_nets q0_ Q0_\n"
"match_nets $4 NET200\n"
"match_nets $13 CS1\n"
"match_nets q1_ Q1_\n"
"match_nets $9 NET175\n"
"match_nets a0 A0\n"
"match_nets a0_ A0_\n"
"match_nets $35 HNET44\n"
"match_nets $34 HNET48\n"
"match_nets $6 NET181\n"
"match_nets $8 NET215\n"
"match_nets nn1 NN1\n"
"match_nets nn1_ NN1_\n"
"match_pins VDD VDD\n"
"match_pins nn1_ NN1_\n"
"match_pins nn1 NN1\n"
@ -3060,6 +3448,7 @@ TEST(20_BusLikeConnections)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3204,6 +3593,153 @@ TEST(20_BusLikeConnections)
"end_circuit TOP TOP MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
comp.set_dont_consider_net_names (false);
good = comp.compare (&nl1, &nl2);
txt = logger.text ();
EXPECT_EQ (txt,
"begin_circuit INV INV\n"
"match_nets VDD VDD\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets VSS VSS\n"
"match_pins IN IN\n"
"match_pins OUT OUT\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_devices $1 $1\n"
"match_devices $2 $2\n"
"end_circuit INV INV MATCH\n"
"begin_circuit INV8 INV8\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_ambiguous_nets IN1 A1\n"
"match_ambiguous_nets IN2 A2\n"
"match_ambiguous_nets IN3 A3\n"
"match_ambiguous_nets IN4 A4\n"
"match_ambiguous_nets IN5 A5\n"
"match_ambiguous_nets IN6 A6\n"
"match_ambiguous_nets IN7 A7\n"
"match_ambiguous_nets IN8 A8\n"
"match_nets OUT1 Q1\n"
"match_nets OUT2 Q2\n"
"match_nets OUT3 Q3\n"
"match_nets OUT4 Q4\n"
"match_nets OUT5 Q5\n"
"match_nets OUT6 Q6\n"
"match_nets OUT7 Q7\n"
"match_nets OUT8 Q8\n"
"match_pins IN1 A1\n"
"match_pins OUT1 Q1\n"
"match_pins IN2 A2\n"
"match_pins OUT2 Q2\n"
"match_pins IN3 A3\n"
"match_pins OUT3 Q3\n"
"match_pins IN4 A4\n"
"match_pins OUT4 Q4\n"
"match_pins IN5 A5\n"
"match_pins OUT5 Q5\n"
"match_pins IN6 A6\n"
"match_pins OUT6 Q6\n"
"match_pins IN7 A7\n"
"match_pins OUT7 Q7\n"
"match_pins IN8 A8\n"
"match_pins OUT8 Q8\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_subcircuits I1 I1\n"
"match_subcircuits I8 I8\n"
"match_subcircuits I3 I3\n"
"match_subcircuits I7 I7\n"
"match_subcircuits I4 I4\n"
"match_subcircuits I2 I2\n"
"match_subcircuits I6 I6\n"
"match_subcircuits I5 I5\n"
"end_circuit INV8 INV8 MATCH\n"
"begin_circuit INV8_WRAP INV8_WRAP\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets IN8 A8\n"
"match_nets OUT8 Q8\n"
"match_nets IN7 A7\n"
"match_nets OUT7 Q7\n"
"match_nets IN6 A6\n"
"match_nets OUT6 Q6\n"
"match_nets IN5 A5\n"
"match_nets OUT5 Q5\n"
"match_nets IN4 A4\n"
"match_nets OUT4 Q4\n"
"match_nets IN3 A3\n"
"match_nets OUT3 Q3\n"
"match_nets IN2 A2\n"
"match_nets OUT2 Q2\n"
"match_nets IN1 A1\n"
"match_nets OUT1 Q1\n"
"match_pins IN1 A1\n"
"match_pins OUT1 Q1\n"
"match_pins IN2 A2\n"
"match_pins OUT2 Q2\n"
"match_pins IN3 A3\n"
"match_pins OUT3 Q3\n"
"match_pins IN4 A4\n"
"match_pins OUT4 Q4\n"
"match_pins IN5 A5\n"
"match_pins OUT5 Q5\n"
"match_pins IN6 A6\n"
"match_pins OUT6 Q6\n"
"match_pins IN7 A7\n"
"match_pins OUT7 Q7\n"
"match_pins IN8 A8\n"
"match_pins OUT8 Q8\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_subcircuits INV8 INV8\n"
"end_circuit INV8_WRAP INV8_WRAP MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets IN8 A8\n"
"match_nets OUT8 Q8\n"
"match_nets IN7 A7\n"
"match_nets OUT7 Q7\n"
"match_nets IN6 A6\n"
"match_nets OUT6 Q6\n"
"match_nets IN5 A5\n"
"match_nets OUT5 Q5\n"
"match_nets IN4 A4\n"
"match_nets OUT4 Q4\n"
"match_nets IN3 A3\n"
"match_nets OUT3 Q3\n"
"match_nets IN2 A2\n"
"match_nets OUT2 Q2\n"
"match_nets IN1 A1\n"
"match_nets OUT1 Q1\n"
"match_pins IN1 A1\n"
"match_pins OUT1 Q1\n"
"match_pins IN2 A2\n"
"match_pins OUT2 Q2\n"
"match_pins IN3 A3\n"
"match_pins OUT3 Q3\n"
"match_pins IN4 A4\n"
"match_pins OUT4 Q4\n"
"match_pins IN5 A5\n"
"match_pins OUT5 Q5\n"
"match_pins IN6 A6\n"
"match_pins OUT6 Q6\n"
"match_pins IN7 A7\n"
"match_pins OUT7 Q7\n"
"match_pins IN8 A8\n"
"match_pins OUT8 Q8\n"
"match_pins VDD VDD\n"
"match_pins VSS VSS\n"
"match_subcircuits INV8 INV8\n"
"end_circuit TOP TOP MATCH"
);
EXPECT_EQ (good, true);
}
TEST(21_BusLikeAmbiguousConnections)
@ -3240,6 +3776,7 @@ TEST(21_BusLikeAmbiguousConnections)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3412,6 +3949,7 @@ TEST(22_NodesRemoved)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3532,6 +4070,7 @@ TEST(23_NodesRemovedWithError)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3631,6 +4170,7 @@ TEST(24_NodesRemovedButConnectedInOther)
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
comp.set_dont_consider_net_names (true);
bool good = comp.compare (&nl1, &nl2);
@ -3722,6 +4262,7 @@ TEST(25_JoinSymmetricNets)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
comp.set_dont_consider_net_names (true);
// NOTE $1 and $2 are joined because they are symmetric
EXPECT_EQ (nl.to_string (),
@ -3760,6 +4301,7 @@ TEST(25b_JoinSymmetricNetsMultiple)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NAND3"));
comp.set_dont_consider_net_names (true);
nl.combine_devices ();
@ -3799,17 +4341,18 @@ TEST(25c_JoinSymmetricNetsMultipleMessedUp)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NOR3"));
comp.set_dont_consider_net_names (true);
nl.combine_devices ();
// NOTE $1 and $2 are joined because they are symmetric
EXPECT_EQ (nl.to_string (),
"circuit NOR3 (A=A,C=C,B=B,OUT=OUT,VSS=VSS,VDD=VDD);\n"
" device PMOS $1 (S=$5,G=B,D=$3) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $1 (S=$6,G=B,D=$3) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $2 (S=OUT,G=A,D=VSS) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $3 (S=$3,G=A,D=OUT) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $4 (S=OUT,G=B,D=VSS) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $8 (S=VDD,G=C,D=$5) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $8 (S=VDD,G=C,D=$6) (L=0.27,W=3.3,AS=0,AD=0,PS=0,PD=0);\n"
" device NMOS $12 (S=VSS,G=C,D=OUT) (L=0.23,W=2.05,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
)
@ -3838,6 +4381,7 @@ TEST(26_JoinSymmetricNets)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("RESCUBE"));
comp.set_dont_consider_net_names (true);
EXPECT_EQ (nl.to_string (),
"circuit RESCUBE (A=A,B=B);\n"
@ -3882,6 +4426,7 @@ TEST(27_DontJoinSymmetricNetsWithPins)
db::NetlistComparer comp;
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
comp.set_dont_consider_net_names (true);
// NOTE $1 and $2 are NOT joined because they have pins
EXPECT_EQ (nl.to_string (),
@ -3914,6 +4459,8 @@ TEST(28_NoSymmetryDetectionCases)
prep_nl (nl, nls);
db::NetlistComparer comp;
comp.set_dont_consider_net_names (true);
std::string sref = nl.to_string ();
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
@ -3936,6 +4483,8 @@ TEST(28_NoSymmetryDetectionCases)
prep_nl (nl, nls);
db::NetlistComparer comp;
comp.set_dont_consider_net_names (true);
std::string sref = nl.to_string ();
comp.join_symmetric_nets (nl.circuit_by_name ("NAND2"));
@ -3965,6 +4514,7 @@ TEST(28_JoinSymmetricNets)
prep_nl (nl, nls);
db::NetlistComparer comp;
comp.set_dont_consider_net_names (true);
comp.join_symmetric_nets (nl.circuit_by_name ("INV2LOAD"));
// NOTE $1 and $2 are joined because they are symmetric

View File

@ -407,3 +407,41 @@ TEST(6)
"layer_map('1/0';'3/10-*';'2/0 : 2/0';'2/42 : 2/42';'2/1-41,43-* : */*')"
);
}
// issue #592
TEST(7)
{
db::Layout ly;
unsigned int l1 = ly.insert_layer (db::LayerProperties (85, 0));
unsigned int l2 = ly.insert_layer (db::LayerProperties (185, 0));
ly.insert_layer ();
ly.insert_layer ();
db::LayerMap lm;
lm.map (db::LayerProperties (10001, 0), l1);
lm.map (db::LayerProperties (10000, 0), l2);
EXPECT_EQ (layers_to_string (ly), "85/0,185/0,,");
lm.prepare (ly);
EXPECT_EQ (layers_to_string (ly), "85/0,185/0,,");
std::pair<bool, unsigned int> p;
p = lm.logical (db::LayerProperties (85, 0));
EXPECT_EQ (p.first, false);
EXPECT_EQ (p.second, (unsigned int) 0);
p = lm.logical (db::LayerProperties (185, 0));
EXPECT_EQ (p.first, false);
EXPECT_EQ (p.second, (unsigned int) 0);
p = lm.logical (db::LayerProperties (10000, 0));
EXPECT_EQ (p.first, true);
EXPECT_EQ (p.second, (unsigned int) 1);
p = lm.logical (db::LayerProperties (10001, 0));
EXPECT_EQ (p.first, true);
EXPECT_EQ (p.second, (unsigned int) 0);
}

View File

@ -51,6 +51,9 @@ namespace {
// we do a initial scan and after this no more write access here.
typedef std::map<const std::type_info *, const ClassBase *, type_info_compare> ti_to_class_map_t;
static ti_to_class_map_t *sp_ti_to_class = 0;
// NOTE: MacOS/clang seems to have some issue with RTTI across shared objects. This map provides a name-based fallback
typedef std::map<std::string, const ClassBase *> tname_to_class_map_t;
static tname_to_class_map_t *sp_tname_to_class = 0;
ClassBase::ClassBase (const std::string &doc, const Methods &mm, bool do_register)
: m_initialized (false), mp_base (0), mp_parent (0), m_doc (doc), m_methods (mm)
@ -68,6 +71,10 @@ ClassBase::ClassBase (const std::string &doc, const Methods &mm, bool do_registe
delete sp_ti_to_class;
sp_ti_to_class = 0;
}
if (sp_tname_to_class) {
delete sp_tname_to_class;
sp_tname_to_class = 0;
}
}
}
@ -753,11 +760,18 @@ static void add_class_to_map (const gsi::ClassBase *c)
if (! sp_ti_to_class) {
sp_ti_to_class = new ti_to_class_map_t ();
}
if (! sp_tname_to_class) {
sp_tname_to_class = new tname_to_class_map_t ();
}
if (ti && c->is_of_type (*ti) && !sp_ti_to_class->insert (std::make_pair (ti, c)).second) {
// Duplicate registration of this class
tl::error << "Duplicate registration of class " << c->name () << " (type " << ti->name () << ")";
tl_assert (false);
if (ti && c->is_of_type (*ti)) {
if (!sp_ti_to_class->insert (std::make_pair (ti, c)).second) {
// Duplicate registration of this class
tl::error << "Duplicate registration of class " << c->name () << " (type " << ti->name () << ")";
tl_assert (false);
} else {
sp_tname_to_class->insert (std::make_pair (std::string (ti->name ()), c));
}
}
}
@ -775,11 +789,19 @@ const ClassBase *class_by_typeinfo_no_assert (const std::type_info &ti)
if (! sp_ti_to_class) {
return 0;
} else {
std::map<const std::type_info *, const ClassBase *, type_info_compare>::const_iterator c = sp_ti_to_class->find (&ti);
ti_to_class_map_t::const_iterator c = sp_ti_to_class->find (&ti);
if (c != sp_ti_to_class->end ()) {
return c->second;
} else {
return 0;
// try name lookup
tname_to_class_map_t::const_iterator cn = sp_tname_to_class->find (std::string (ti.name ()));
if (cn != sp_tname_to_class->end ()) {
// we can use this typeinfo as alias
sp_ti_to_class->insert (std::make_pair (&ti, cn->second));
return cn->second;
} else {
return 0;
}
}
}
}

View File

@ -37,6 +37,15 @@ See <a href="/about/lvs_ref_netter.xml#align">Netter#align</a> for a description
<p>
See <a href="/about/lvs_ref_netter.xml#compare">Netter#compare</a> for a description of that function.
</p>
<a name="consider_net_names"/><h2>"consider_net_names" - Indicates whether the netlist comparer shall use net names</h2>
<keyword name="consider_net_names"/>
<p>Usage:</p>
<ul>
<li><tt>consider_net_names(f)</tt></li>
</ul>
<p>
See <a href="/about/lvs_ref_netter.xml#consider_net_names">Netter#consider_net_names</a> for a description of that function.
</p>
<a name="equivalent_pins"/><h2>"equivalent_pins" - Marks pins as equivalent</h2>
<keyword name="equivalent_pins"/>
<p>Usage:</p>

View File

@ -85,6 +85,18 @@ corresponding circuits: the unpaired circuit will be flattened then.
This method will return true, if the netlists are equivalent and false
otherwise.
</p>
<a name="consider_net_names"/><h2>"consider_net_names" - Indicates whether the netlist comparer shall use net names</h2>
<keyword name="consider_net_names"/>
<p>Usage:</p>
<ul>
<li><tt>consider_net_names(f)</tt></li>
</ul>
<p>
If this value is set to true (the default), the netlist comparer
will employ net names to resolve ambiguities. If set to false,
ambiguities will be resolved based on the topology alone. Topology
resolution is more expensive.
</p>
<a name="equivalent_pins"/><h2>"equivalent_pins" - Marks pins as equivalent</h2>
<keyword name="equivalent_pins"/>
<p>Usage:</p>

View File

@ -285,16 +285,6 @@ static DocumentationParser &cls_documentation (const gsi::ClassBase *cls)
}
}
static const std::string &aliased_name (const gsi::ClassBase *cls)
{
const std::string &alias = cls_documentation (cls).alias;
if (alias.empty ()) {
return cls->name ();
} else {
return alias;
}
}
static std::string make_qualified_name (const gsi::ClassBase *cls)
{
std::string qname;
@ -703,9 +693,9 @@ type_to_s (const gsi::ArgType &a, bool linked, bool for_return)
s += "new ";
}
if (linked) {
s += "<a href=\"" + escape_xml (class_doc_url (aliased_name (a.cls ()))) + "\">" + escape_xml (aliased_name (a.cls ())) + "</a>";
s += "<a href=\"" + escape_xml (class_doc_url (make_qualified_name (a.cls ()))) + "\">" + escape_xml (make_qualified_name (a.cls ())) + "</a>";
} else {
s += aliased_name (a.cls ());
s += make_qualified_name (a.cls ());
}
break;
case gsi::T_vector:

View File

@ -668,18 +668,21 @@ BEGIN_PROTECTED
}
}
if (collection && (force_add || (collection->begin () == collection->end () && collection->begin_children () == collection->end_children ()))) {
bool open_template_dialog = false;
if (! force_add && collection && (collection->begin () == collection->end () && collection->begin_children () == collection->end_children ())) {
TipDialog td (this,
tl::to_string (QObject::tr ("<html><body>To get started with the macro development feature, read the documentation provided: <a href=\"int:/about/macro_editor.xml\">About Macro Development</a>.</body></html>")),
"macro-editor-basic-tips");
open_template_dialog = td.exec_dialog () && td.will_be_shown ();
}
if (collection && (force_add || open_template_dialog)) {
lym::Macro *m = new_macro ();
if (force_add && m) {
set_run_macro (m);
}
}
TipDialog td (this,
tl::to_string (QObject::tr ("<html><body>To get started with the macro development feature, read the documentation provided: <a href=\"int:/about/macro_editor.xml\">About Macro Development</a>.</body></html>")),
"macro-editor-basic-tips");
td.exec_dialog ();
} else {
if (! cat.empty ()) {

View File

@ -161,7 +161,10 @@ void MacroTreeModel::macro_changed ()
void MacroTreeModel::update_data ()
{
emit dataChanged (index (0, 0, QModelIndex ()), index (rowCount (QModelIndex()) - 1, 0, QModelIndex ()));
int rc = rowCount (QModelIndex());
if (rc > 0) {
emit dataChanged (index (0, 0, QModelIndex ()), index (rc - 1, 0, QModelIndex ()));
}
}
void MacroTreeModel::about_to_change ()

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>432</width>
<height>342</height>
<height>349</height>
</rect>
</property>
<property name="windowTitle">
@ -32,7 +32,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Put origin relative to cell's bounding box at ...</string>
<string>Place this cell reference point ...</string>
</property>
</widget>
</item>
@ -302,6 +302,79 @@
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>... at x:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="x_le"/>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>µm, y:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="y_le"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="vis_only_cbx">
<property name="text">
@ -322,7 +395,7 @@
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -403,7 +476,10 @@
<tabstop>lb</tabstop>
<tabstop>cb</tabstop>
<tabstop>rb</tabstop>
<tabstop>x_le</tabstop>
<tabstop>y_le</tabstop>
<tabstop>vis_only_cbx</tabstop>
<tabstop>adjust_calls_cbx</tabstop>
</tabstops>
<resources>
<include location="../../lay/lay/layResources.qrc"/>

View File

@ -0,0 +1,237 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "gsiSignals.h"
#include "layNetlistBrowserDialog.h"
#include "layLayoutView.h"
namespace tl
{
// disable copy and default constructor for NetlistBrowserDialog
template <> struct type_traits<lay::NetlistBrowserDialog> : public type_traits<void>
{
typedef tl::false_tag has_copy_constructor;
typedef tl::false_tag has_default_constructor;
};
}
namespace gsi
{
static void set_root (lay::NetlistObjectPath *path, db::Circuit *r)
{
path->root = r;
}
static db::Circuit *root (const lay::NetlistObjectPath *path)
{
return const_cast<db::Circuit *> (path->root);
}
static void set_device (lay::NetlistObjectPath *path, db::Device *r)
{
path->device = r;
}
static db::Device *device (const lay::NetlistObjectPath *path)
{
return const_cast<db::Device *> (path->device);
}
static void set_net (lay::NetlistObjectPath *path, db::Net *r)
{
path->net = r;
}
static db::Net *net (const lay::NetlistObjectPath *path)
{
return const_cast<db::Net *> (path->net);
}
static std::vector<db::SubCircuit *> path (const lay::NetlistObjectPath *p)
{
std::vector<db::SubCircuit *> pp;
pp.reserve (p->path.size ());
for (lay::NetlistObjectPath::path_iterator i = p->path.begin (); i != p->path.end (); ++i) {
pp.push_back (const_cast<db::SubCircuit *> (*i));
}
return pp;
}
static void set_path (lay::NetlistObjectPath *p, const std::vector<db::SubCircuit *> &path)
{
p->path = lay::NetlistObjectPath::path_type (path.begin (), path.end ());
}
Class<lay::NetlistObjectPath> decl_NetlistObjectPath ("lay", "NetlistObjectPath",
gsi::method_ext ("root=", &set_root, gsi::arg ("root"),
"@brief Sets the root circuit of the path.\n"
"The root circuit is the circuit from which the path starts.\n"
) +
gsi::method_ext ("root", &root,
"@brief Gets the root circuit of the path.\n"
) +
gsi::method_ext ("path=", &set_path, gsi::arg ("path"),
"@brief Sets the path.\n"
"The path is a list of subcircuits leading from the root to the final object. "
"The final (net, device) object is located in the circuit called by the last subcircuit "
"of the subcircuit chain. If the subcircuit list is empty, the final object is located inside "
"the root object."
) +
gsi::method_ext ("path", &path,
"@brief Gets the path.\n"
) +
gsi::method_ext ("net=", &set_net, gsi::arg ("net"),
"@brief Sets the net the path points to.\n"
"If the path describes the location of a net, this member will indicate it.\n"
"The other way to describe a final object is \\device=. If neither a device nor "
"net is given, the path describes a circuit and how it is referenced from the root."
) +
gsi::method_ext ("net", &net,
"@brief Gets the net the path points to.\n"
) +
gsi::method_ext ("device=", &set_device, gsi::arg ("device"),
"@brief Sets the device the path points to.\n"
"If the path describes the location of a device, this member will indicate it.\n"
"The other way to describe a final object is \\net=. If neither a device nor "
"net is given, the path describes a circuit and how it is referenced from the root."
) +
gsi::method_ext ("device", &device,
"@brief Gets the device the path points to.\n"
) +
gsi::method ("is_null?", &lay::NetlistObjectPath::is_null,
"@brief Returns a value indicating whether the path is an empty one.\n"
),
"@brief An object describing the instantiation of a netlist object.\n"
"This class describes the instantiation of a net or a device or a circuit in terms of "
"a root circuit and a subcircuit chain leading to the indicated object.\n"
"\n"
"See \\net= or \\device= for the indicated object, \\path= for the subcircuit chain.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistObjectPath first (const lay::NetlistObjectsPath *pp)
{
return pp->first ();
}
static lay::NetlistObjectPath second (const lay::NetlistObjectsPath *pp)
{
return pp->second ();
}
Class<lay::NetlistObjectsPath> decl_NetlistObjectsPath ("lay", "NetlistObjectsPath",
gsi::method_ext ("first", &first,
"@brief Gets the first object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the layout netlist object.\n"
"For the single netlist, the first path is the only path supplied."
) +
gsi::method_ext ("second", &second,
"@brief Gets the second object's path.\n"
"In cases of paired netlists (LVS database), the first path points to the schematic netlist object.\n"
"For the single netlist, the scecond path is always a null path."
),
"@brief An object describing the instantiation of a single netlist object or a pair of those.\n"
"This class is basically a pair of netlist object paths (see \\NetlistObjectPath). When derived from a single netlist view, "
"only the first path is valid and will point to the selected object (a net, a device or a circuit). The second path is null.\n"
"\n"
"If the path is derived from a paired netlist view (a LVS report view), the first path corresponds to the object in the layout netlist, "
"the second one to the object in the schematic netlist.\n"
"If the selected object isn't a matched one, either the first or second path may be a null or a partial path without a final net or device object "
"or a partial path.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistObjectPath current_path_first (lay::NetlistBrowserDialog *dialog)
{
return dialog->current_path ().first ();
}
static lay::NetlistObjectPath current_path_second (lay::NetlistBrowserDialog *dialog)
{
return dialog->current_path ().second ();
}
Class<lay::NetlistBrowserDialog> decl_NetlistBrowserDialog ("lay", "NetlistBrowserDialog",
gsi::event ("on_current_db_changed", &lay::NetlistBrowserDialog::current_db_changed_event,
"@brief This event is triggered when the current database is changed.\n"
"The current database can be obtained with \\db."
) +
gsi::event ("on_selection_changed", &lay::NetlistBrowserDialog::selection_changed_event,
"@brief This event is triggered when the selection changed.\n"
"The selection can be obtained with \\current_path_first, \\current_path_second, \\selected_nets, \\selected_devices, \\selected_subcircuits and \\selected_circuits."
) +
gsi::event ("on_probe", &lay::NetlistBrowserDialog::probe_event, gsi::arg ("first_path"), gsi::arg ("second_path"),
"@brief This event is triggered when a net is probed.\n"
"The first path will indicate the location of the probed net in terms of two paths: one describing the instantiation of the "
"net in layout space and one in schematic space. Both objects are \\NetlistObjectPath objects which hold the root circuit, the "
"chain of subcircuits leading to the circuit containing the net and the net itself."
) +
gsi::method ("db", &lay::NetlistBrowserDialog::db,
"@brief Gets the database the browser is connected to.\n"
) +
gsi::method_ext ("current_path_first", &current_path_first,
"@brief Gets the path of the current object on the first (layout in case of LVS database) side.\n"
) +
gsi::method_ext ("current_path_second", &current_path_second,
"@brief Gets the path of the current object on the second (schematic in case of LVS database) side.\n"
) +
gsi::method ("current_path", &lay::NetlistBrowserDialog::current_path,
"@brief Gets the path of the current object as a path pair (combines layout and schematic object paths in case of a LVS database view).\n"
) +
gsi::method ("selected_paths", &lay::NetlistBrowserDialog::selected_paths,
"@brief Gets the nets currently selected objects (paths) in the netlist database browser.\n"
"The result is an array of path pairs. See \\NetlistObjectsPath for details about these pairs."
),
"@brief Represents the netlist browser dialog.\n"
"This dialog is a part of the \\LayoutView class and can be obtained through \\LayoutView#netlist_browser.\n"
"This interface allows to interact with the browser - mainly to get information about state changes.\n"
"\n"
"This class has been introduced in version 0.27.\n"
);
static lay::NetlistBrowserDialog *netlist_browser (lay::LayoutView *lv)
{
return lv->get_plugin<lay::NetlistBrowserDialog> ();
}
// extend lay::LayoutView with the getter for the netlist browser
static
gsi::ClassExt<lay::LayoutView> decl_ext_layout_view (
gsi::method_ext ("netlist_browser", &netlist_browser,
"@brief Gets the netlist browser object for the given layout view\n"
"\n"
"\nThis method has been added in version 0.27.\n"
)
);
}

View File

@ -702,7 +702,15 @@ BrowseInstancesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Lay
std::string aref;
if (r > 1 || c > 1) {
aref = tl::sprintf ("[%ld,%ld]", c, r);
aref = "[";
aref += tl::to_string (c);
aref += ",";
aref += tl::to_string (r);
aref += "]";
} else if (parent_inst.size () > 1) {
aref = "(+";
aref += tl::to_string (parent_inst.size () - 1);
aref += "x)";
}
std::string new_path;

View File

@ -868,7 +868,15 @@ BrowseShapesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Layout
std::string aref;
if (r > 1 || c > 1) {
aref = tl::sprintf ("[%ld,%ld]", c, r);
aref = "[";
aref += tl::to_string (c);
aref += ",";
aref += tl::to_string (r);
aref += "]";
} else if (parent_inst.size () > 1) {
aref = "(+";
aref += tl::to_string (parent_inst.size () - 1);
aref += "x)";
}
std::string new_path;

View File

@ -900,33 +900,39 @@ AlignCellOptionsDialog::~AlignCellOptionsDialog ()
}
bool
AlignCellOptionsDialog::exec_dialog (int &mode_x, int &mode_y, bool &visible_only, bool &adjust_calls)
AlignCellOptionsDialog::exec_dialog (AlignCellOptions &data)
{
mp_ui->vis_only_cbx->setChecked (visible_only);
mp_ui->adjust_calls_cbx->setChecked (adjust_calls);
mp_ui->vis_only_cbx->setChecked (data.visible_only);
mp_ui->adjust_calls_cbx->setChecked (data.adjust_parents);
QToolButton *buttons[3][3] = { { mp_ui->lb, mp_ui->cb, mp_ui->rb }, { mp_ui->lc, mp_ui->cc, mp_ui->rc }, { mp_ui->lt, mp_ui->ct, mp_ui->rt } };
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
buttons[i][j]->setChecked (j - 1 == mode_x && i - 1 == mode_y);
buttons[i][j]->setChecked (j - 1 == data.mode_x && i - 1 == data.mode_y);
}
}
mp_ui->x_le->setText (tl::to_qstring (tl::micron_to_string (data.xpos)));
mp_ui->y_le->setText (tl::to_qstring (tl::micron_to_string (data.ypos)));
if (QDialog::exec ()) {
visible_only = mp_ui->vis_only_cbx->isChecked ();
adjust_calls = mp_ui->adjust_calls_cbx->isChecked ();
data.visible_only = mp_ui->vis_only_cbx->isChecked ();
data.adjust_parents = mp_ui->adjust_calls_cbx->isChecked ();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (buttons[i][j]->isChecked ()) {
mode_x = j - 1;
mode_y = i - 1;
data.mode_x = j - 1;
data.mode_y = i - 1;
}
}
}
tl::from_string (tl::to_string (mp_ui->x_le->text ()), data.xpos);
tl::from_string (tl::to_string (mp_ui->y_le->text ()), data.ypos);
return true;
} else {
@ -934,7 +940,21 @@ AlignCellOptionsDialog::exec_dialog (int &mode_x, int &mode_y, bool &visible_onl
}
}
void
void
AlignCellOptionsDialog::accept ()
{
BEGIN_PROTECTED;
double x = 0.0;
tl::from_string (tl::to_string (mp_ui->x_le->text ()), x);
tl::from_string (tl::to_string (mp_ui->y_le->text ()), x);
QDialog::accept ();
END_PROTECTED;
}
void
AlignCellOptionsDialog::button_clicked ()
{
QToolButton *buttons[3][3] = { { mp_ui->lb, mp_ui->cb, mp_ui->rb }, { mp_ui->lc, mp_ui->cc, mp_ui->rc }, { mp_ui->lt, mp_ui->ct, mp_ui->rt } };

View File

@ -363,6 +363,21 @@ private:
lay::LayoutView *mp_view;
};
/**
* @brief A data structure holding the options for the "align cell" dialog
*/
struct LAYBASIC_PUBLIC AlignCellOptions
{
AlignCellOptions ()
: mode_x (-1), mode_y (-1), xpos (0.0), ypos (0.0), visible_only (false), adjust_parents (true)
{ }
int mode_x, mode_y;
double xpos, ypos;
bool visible_only;
bool adjust_parents;
};
/**
* @brief The merge operation options
*/
@ -375,10 +390,11 @@ public:
AlignCellOptionsDialog (QWidget *parent);
virtual ~AlignCellOptionsDialog ();
bool exec_dialog (int &mode_x, int &mode_y, bool &visible_only, bool &adjust_calls);
bool exec_dialog (AlignCellOptions &data);
private slots:
void button_clicked ();
void accept ();
private:
Ui::AlignCellOptionsDialog *mp_ui;

View File

@ -236,67 +236,73 @@ SingleIndexedNetlistModel::top_circuit_count () const
size_t
SingleIndexedNetlistModel::net_count (const circuit_pair &circuits) const
{
return circuits.first->net_count ();
return circuits.first ? circuits.first->net_count () : 0;
}
size_t
SingleIndexedNetlistModel::net_terminal_count (const net_pair &nets) const
{
return nets.first->terminal_count ();
return nets.first ? nets.first->terminal_count () : 0;
}
size_t
SingleIndexedNetlistModel::net_subcircuit_pin_count (const net_pair &nets) const
{
return nets.first->subcircuit_pin_count ();
return nets.first ? nets.first->subcircuit_pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::net_pin_count (const net_pair &nets) const
{
return nets.first->pin_count ();
return nets.first ? nets.first->pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::device_count (const circuit_pair &circuits) const
{
return circuits.first->device_count ();
return circuits.first ? circuits.first->device_count () : 0;
}
size_t
SingleIndexedNetlistModel::subcircuit_pin_count (const subcircuit_pair &subcircuits) const
{
return subcircuits.first ? subcircuits.first->circuit_ref ()->pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::pin_count (const circuit_pair &circuits) const
{
return circuits.first->pin_count ();
return circuits.first ? circuits.first->pin_count () : 0;
}
size_t
SingleIndexedNetlistModel::subcircuit_count (const circuit_pair &circuits) const
{
return circuits.first->subcircuit_count ();
return circuits.first ? circuits.first->subcircuit_count () : 0;
}
size_t
SingleIndexedNetlistModel::child_circuit_count (const circuit_pair &circuits) const
{
return circuits.first->end_children () - circuits.first->begin_children ();
return circuits.first ? (circuits.first->end_children () - circuits.first->begin_children ()) : 0;
}
IndexedNetlistModel::circuit_pair
SingleIndexedNetlistModel::parent_of (const net_pair &nets) const
{
return std::make_pair (nets.first->circuit (), (const db::Circuit *) 0);
return std::make_pair (nets.first ? nets.first->circuit () : 0, (const db::Circuit *) 0);
}
IndexedNetlistModel::circuit_pair
SingleIndexedNetlistModel::parent_of (const device_pair &devices) const
{
return std::make_pair (devices.first->circuit (), (const db::Circuit *) 0);
return std::make_pair (devices.first ? devices.first->circuit () : 0, (const db::Circuit *) 0);
}
IndexedNetlistModel::circuit_pair
SingleIndexedNetlistModel::parent_of (const subcircuit_pair &subcircuits) const
{
return std::make_pair (subcircuits.first->circuit (), (const db::Circuit *) 0);
return std::make_pair (subcircuits.first ? subcircuits.first->circuit () : 0, (const db::Circuit *) 0);
}
std::pair<IndexedNetlistModel::circuit_pair, IndexedNetlistModel::Status>
@ -346,6 +352,34 @@ SingleIndexedNetlistModel::net_subcircuit_pinref_from_index (const net_pair &net
return attr_by_object_and_index (nets, index, nets.first->begin_subcircuit_pins (), nets.first->end_subcircuit_pins (), none, none, m_subcircuit_pinref_by_net_and_index, sort_by_pin_name<db::NetSubcircuitPinRef> ());
}
IndexedNetlistModel::net_subcircuit_pin_pair
SingleIndexedNetlistModel::subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const
{
if (! subcircuits.first) {
return IndexedNetlistModel::net_subcircuit_pin_pair (0, 0);
}
std::map<subcircuit_pair, std::vector<net_subcircuit_pin_pair> >::iterator i = m_subcircuit_pins_by_index.find (subcircuits);
if (i == m_subcircuit_pins_by_index.end ()) {
i = m_subcircuit_pins_by_index.insert (std::make_pair (subcircuits, std::vector<net_subcircuit_pin_pair> ())).first;
std::vector<net_subcircuit_pin_pair> &refs = i->second;
const db::Circuit *circuit = subcircuits.first->circuit_ref ();
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
const db::NetSubcircuitPinRef *ref = subcircuits.first->netref_for_pin (p->id ());
if (! ref) {
m_synthetic_pinrefs.push_back (db::NetSubcircuitPinRef (const_cast<db::SubCircuit *> (subcircuits.first), p->id ()));
ref = & m_synthetic_pinrefs.back ();
}
refs.push_back (net_subcircuit_pin_pair (ref, 0));
}
}
return index < i->second.size () ? i->second [index] : IndexedNetlistModel::net_subcircuit_pin_pair (0, 0);
}
IndexedNetlistModel::net_terminal_pair
SingleIndexedNetlistModel::net_terminalref_from_index (const net_pair &nets, size_t index) const
{

View File

@ -80,6 +80,7 @@ public:
virtual size_t net_subcircuit_pin_count (const net_pair &nets) const = 0;
virtual size_t net_pin_count (const net_pair &nets) const = 0;
virtual size_t device_count (const circuit_pair &circuits) const = 0;
virtual size_t subcircuit_pin_count (const subcircuit_pair &subcircuits) const = 0;
virtual size_t pin_count (const circuit_pair &circuits) const = 0;
virtual size_t subcircuit_count (const circuit_pair &circuits) const = 0;
virtual size_t child_circuit_count (const circuit_pair &circuits) const = 0;
@ -95,6 +96,7 @@ public:
virtual const db::Net *second_net_for (const db::Net *first) const = 0;
virtual const db::Circuit *second_circuit_for (const db::Circuit *first) const = 0;
virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const = 0;
virtual net_subcircuit_pin_pair subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const = 0;
virtual net_terminal_pair net_terminalref_from_index (const net_pair &nets, size_t index) const = 0;
virtual net_pin_pair net_pinref_from_index (const net_pair &nets, size_t index) const = 0;
virtual std::pair<device_pair, Status> device_from_index (const circuit_pair &circuits, size_t index) const = 0;
@ -149,6 +151,7 @@ public:
virtual size_t net_pin_count (const net_pair &nets) const;
virtual size_t device_count (const circuit_pair &circuits) const;
virtual size_t pin_count (const circuit_pair &circuits) const;
virtual size_t subcircuit_pin_count (const subcircuit_pair &subcircuits) const;
virtual size_t subcircuit_count (const circuit_pair &circuits) const;
virtual size_t child_circuit_count (const circuit_pair &circuits) const;
@ -163,6 +166,7 @@ public:
virtual const db::Net *second_net_for (const db::Net *first) const;
virtual const db::Circuit *second_circuit_for (const db::Circuit *first) const;
virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const;
virtual net_subcircuit_pin_pair subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const;
virtual net_terminal_pair net_terminalref_from_index (const net_pair &nets, size_t index) const;
virtual net_pin_pair net_pinref_from_index (const net_pair &nets, size_t index) const;
virtual std::pair<device_pair, Status> device_from_index (const circuit_pair &circuits, size_t index) const;
@ -194,6 +198,8 @@ private:
mutable std::map<pin_pair, size_t> m_pin_index_by_object;
mutable std::map<subcircuit_pair, size_t> m_subcircuit_index_by_object;
mutable std::map<device_pair, size_t> m_device_index_by_object;
mutable std::map<subcircuit_pair, std::vector<net_subcircuit_pin_pair> > m_subcircuit_pins_by_index;
mutable std::list<db::NetSubcircuitPinRef> m_synthetic_pinrefs;
};
}

View File

@ -51,7 +51,6 @@
#include "layConverters.h"
#include "layGridNet.h"
#include "layMove.h"
#include "layDialogs.h"
#include "layZoomBox.h"
#include "layMouseTracker.h"
#include "layTipDialog.h"
@ -463,10 +462,6 @@ LayoutView::init (db::Manager *mgr, QWidget * /*parent*/)
m_sel_inside_pcells = false;
m_move_to_origin_mode_x = 0;
m_move_to_origin_mode_y = 0;
m_align_cell_origin_mode_x = -1;
m_align_cell_origin_mode_y = -1;
m_align_cell_origin_visible_layers = false;
m_align_cell_adjust_parents = true;
m_del_cell_mode = 0;
m_layer_hier_mode = 0;
m_add_other_layers = false;
@ -5292,7 +5287,7 @@ LayoutView::cm_align_cell_origin ()
}
lay::AlignCellOptionsDialog dialog (this);
if (dialog.exec_dialog (m_align_cell_origin_mode_x, m_align_cell_origin_mode_y, m_align_cell_origin_visible_layers, m_align_cell_adjust_parents)) {
if (dialog.exec_dialog (m_align_cell_options)) {
clear_selection ();
@ -5300,7 +5295,7 @@ LayoutView::cm_align_cell_origin ()
db::Box bbox;
if (m_align_cell_origin_visible_layers) {
if (m_align_cell_options.visible_only) {
for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) {
bbox += cell->bbox (l->layer_index ());
@ -5311,7 +5306,7 @@ LayoutView::cm_align_cell_origin ()
}
db::Coord refx, refy;
switch (m_align_cell_origin_mode_x) {
switch (m_align_cell_options.mode_x) {
case -1:
refx = bbox.left ();
break;
@ -5322,7 +5317,7 @@ LayoutView::cm_align_cell_origin ()
refx = bbox.center ().x ();
break;
}
switch (m_align_cell_origin_mode_y) {
switch (m_align_cell_options.mode_y) {
case -1:
refy = bbox.bottom ();
break;
@ -5334,10 +5329,11 @@ LayoutView::cm_align_cell_origin ()
break;
}
db::Trans t (db::Vector (-refx, -refy));
db::Layout &layout = cellview (cv_index)->layout ();
db::Cell &nc_cell = layout.cell (cell->cell_index ());
db::Trans t (db::Vector (-refx + db::coord_traits<db::Coord>::rounded (m_align_cell_options.xpos / layout.dbu ()), -refy + db::coord_traits<db::Coord>::rounded (m_align_cell_options.ypos / layout.dbu ())));
for (unsigned int i = 0; i < layout.layers (); ++i) {
if (layout.is_valid_layer (i)) {
db::Shapes &shapes = nc_cell.shapes (i);
@ -5351,7 +5347,7 @@ LayoutView::cm_align_cell_origin ()
nc_cell.transform (*inst, t);
}
if (m_align_cell_adjust_parents) {
if (m_align_cell_options.adjust_parents) {
std::vector<std::pair<db::Cell *, db::Instance> > insts_to_modify;
for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {

View File

@ -50,6 +50,7 @@
#include "layPlugin.h"
#include "layDisplayState.h"
#include "layBookmarkList.h"
#include "layDialogs.h"
#include "gsi.h"
#include "tlException.h"
#include "tlEvents.h"
@ -2871,9 +2872,7 @@ private:
db::LayerProperties m_new_layer_props;
db::DVector m_move_dist;
int m_move_to_origin_mode_x, m_move_to_origin_mode_y;
int m_align_cell_origin_mode_x, m_align_cell_origin_mode_y;
bool m_align_cell_origin_visible_layers;
bool m_align_cell_adjust_parents;
lay::AlignCellOptions m_align_cell_options;
int m_del_cell_mode;
int m_layer_hier_mode;
int m_duplicate_hier_mode;

View File

@ -120,6 +120,8 @@ NetlistBrowserDialog::NetlistBrowserDialog (lay::Dispatcher *root, lay::LayoutVi
connect (sticky_cbx, SIGNAL (clicked ()), this, SLOT (sticky_mode_clicked ()));
cellviews_changed ();
browser_page->selection_changed_event.add (this, &NetlistBrowserDialog::selection_changed);
}
NetlistBrowserDialog::~NetlistBrowserDialog ()
@ -127,6 +129,34 @@ NetlistBrowserDialog::~NetlistBrowserDialog ()
tl::Object::detach_from_all_events ();
}
db::LayoutToNetlist *
NetlistBrowserDialog::db ()
{
return browser_page->db ();
}
const lay::NetlistObjectsPath &
NetlistBrowserDialog::current_path () const
{
if (browser_page) {
return browser_page->current_path ();
} else {
static lay::NetlistObjectsPath empty;
return empty;
}
}
const std::vector<lay::NetlistObjectsPath> &
NetlistBrowserDialog::selected_paths () const
{
if (browser_page) {
return browser_page->selected_paths ();
} else {
static std::vector<lay::NetlistObjectsPath> empty;
return empty;
}
}
void
NetlistBrowserDialog::configure_clicked ()
{
@ -254,40 +284,60 @@ NetlistBrowserDialog::probe_net (const db::DPoint &p, bool trace_path)
}
const db::Net *net = 0;
db::Net *net = 0;
db::Circuit *root = 0;
std::vector<db::SubCircuit *> sc_path;
db::LayoutToNetlist *l2ndb = view ()->get_l2ndb (m_l2n_index);
if (l2ndb) {
// determines the corresponding layer inside the database and probe the net from this region and the
// start point.
root = l2ndb->netlist ()->circuit_by_name (cv->layout ().cell_name (cv.cell_index ()));
if (root) {
std::vector<db::Region *> regions;
// determines the corresponding layer inside the database and probe the net from this region and the
// start point.
const db::Connectivity &conn = l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::LayerProperties lp = l2ndb->internal_layout ()->get_properties (*layer);
if (! lp.is_null ()) {
db::Region *region = l2ndb->layer_by_index (*layer);
if (lp == cv->layout ().get_properties (start_layer)) {
// a matching original layer is looked up with higher prio
regions.insert (regions.begin (), region);
} else {
regions.push_back (region);
std::vector<db::Region *> regions;
const db::Connectivity &conn = l2ndb->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::LayerProperties lp = l2ndb->internal_layout ()->get_properties (*layer);
if (! lp.is_null ()) {
db::Region *region = l2ndb->layer_by_index (*layer);
if (lp == cv->layout ().get_properties (start_layer)) {
// a matching original layer is looked up with higher prio
regions.insert (regions.begin (), region);
} else {
regions.push_back (region);
}
}
}
}
// probe the net
// probe the net
for (std::vector<db::Region *>::const_iterator r = regions.begin (); r != regions.end () && !net; ++r) {
sc_path.clear ();
net = l2ndb->probe_net (**r, start_point, &sc_path, root);
}
for (std::vector<db::Region *>::const_iterator r = regions.begin (); r != regions.end () && !net; ++r) {
net = l2ndb->probe_net (**r, start_point);
}
}
// select the net if one was found
browser_page->select_net (net);
lay::NetlistObjectPath path;
if (net) {
path.root = root;
path.net = net;
path.path = lay::NetlistObjectPath::path_type (sc_path.begin (), sc_path.end ());
}
browser_page->select_path (path);
// emits the probe event
// NOTE: browser_page->current_path () will hold the paired path with the schematic side being
// expanded.
probe_event (browser_page->current_path ().first (), browser_page->current_path ().second ());
}
void
@ -421,9 +471,15 @@ BEGIN_PROTECTED
tl::log << tl::to_string (QObject::tr ("Loading file: ")) << l2ndb->filename ();
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading")));
browser_page->set_l2ndb (0);
l2ndb->load (l2ndb->filename ());
browser_page->set_l2ndb (l2ndb);
browser_page->set_db (0);
try {
l2ndb->load (l2ndb->filename ());
browser_page->set_db (l2ndb);
current_db_changed_event ();
} catch (...) {
current_db_changed_event ();
throw;
}
}
@ -707,6 +763,8 @@ NetlistBrowserDialog::update_content ()
central_stack->setCurrentIndex (1);
}
bool db_changed = false;
m_saveas_action->setEnabled (l2ndb != 0);
m_export_action->setEnabled (l2ndb != 0);
m_unload_action->setEnabled (l2ndb != 0);
@ -714,7 +772,10 @@ NetlistBrowserDialog::update_content ()
m_reload_action->setEnabled (l2ndb != 0);
browser_page->enable_updates (false); // Avoid building the internal lists several times ...
browser_page->set_l2ndb (l2ndb);
if (browser_page->db () != l2ndb) {
db_changed = true;
browser_page->set_db (l2ndb);
}
browser_page->set_max_shape_count (m_max_shape_count);
browser_page->set_highlight_style (m_marker_color, m_marker_line_width, m_marker_vertex_size, m_marker_halo, m_marker_dither_pattern, m_marker_intensity, m_use_original_colors, m_auto_color_enabled ? &m_auto_colors : 0);
browser_page->set_window (m_window, m_window_dim);
@ -740,6 +801,10 @@ NetlistBrowserDialog::update_content ()
if (l2ndb_cb->currentIndex () != m_l2n_index) {
l2ndb_cb->setCurrentIndex (m_l2n_index);
}
if (db_changed) {
current_db_changed_event ();
}
}
void
@ -751,8 +816,16 @@ NetlistBrowserDialog::deactivated ()
lay::Dispatcher::instance ()->config_set (cfg_l2ndb_window_state, lay::save_dialog_state (this, false /*don't store the section sizes*/).c_str ());
}
browser_page->set_l2ndb (0);
bool db_changed = false;
if (browser_page->db () != 0) {
db_changed = true;
browser_page->set_db (0);
}
browser_page->set_view (0, 0);
if (db_changed) {
current_db_changed_event ();
}
}
void

View File

@ -29,6 +29,7 @@
#include "layNetlistBrowser.h"
#include "layViewObject.h"
#include "layColorPalette.h"
#include "tlEvents.h"
namespace lay
{
@ -46,6 +47,38 @@ public:
void load (int lay_index, int cv_index);
/**
* @brief This event is emitted after the current database changed
*/
tl::Event current_db_changed_event;
/**
* @brief This event is emitted when a shape is probed
* The first path is that of the layout, the second that of the schematic in case of a
* LVS database.
*/
tl::event<lay::NetlistObjectPath, lay::NetlistObjectPath> probe_event;
/**
* @brief Gets the current database
*/
db::LayoutToNetlist *db ();
/**
* @brief Gets the current object's path
*/
const lay::NetlistObjectsPath &current_path () const;
/**
* @brief Gets the selected nets
*/
const std::vector<lay::NetlistObjectsPath> &selected_paths () const;
/**
* @brief An event indicating that the selection has changed
*/
tl::Event selection_changed_event;
private:
// implementation of the lay::Browser interface
virtual void activated ();
@ -64,6 +97,11 @@ private:
void cellview_changed (int index);
void l2ndbs_changed ();
void selection_changed ()
{
selection_changed_event ();
}
public slots:
void cv_index_changed (int);
void l2ndb_index_changed (int);

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,9 @@
#include "dbLayoutToNetlist.h"
#include "dbLayoutVsSchematic.h"
#include "tlList.h"
#include "tlTypeTraits.h"
#include <QAbstractItemModel>
#include <QColor>
@ -88,6 +91,167 @@ private:
// ----------------------------------------------------------------------------------
// NetlistBrowserModel definition
class NetlistBrowserModel;
class NetlistModelItemData;
class RootItemData;
class CircuitItemData;
class CircuitNetItemData;
class CircuitDeviceItemData;
class CircuitSubCircuitItemData;
}
namespace tl {
// disable copying for NetlistModelItemData
template<> struct type_traits<lay::NetlistModelItemData>
{
typedef false_tag has_copy_constructor;
};
}
namespace lay
{
/**
* @brief A base class for the item data object
*/
class NetlistModelItemData
: public tl::list_node<NetlistModelItemData>
{
public:
typedef tl::list<NetlistModelItemData>::iterator iterator;
NetlistModelItemData ();
NetlistModelItemData (NetlistModelItemData *parent);
virtual ~NetlistModelItemData ();
virtual NetlistModelItemData *parent () { return mp_parent; }
virtual QIcon icon (NetlistBrowserModel *model) = 0;
virtual QString text (int column, NetlistBrowserModel *model) = 0;
virtual QString search_text () = 0;
virtual std::string tooltip (NetlistBrowserModel *model) = 0;
virtual db::NetlistCrossReference::Status status (NetlistBrowserModel *model) = 0;
void ensure_children (NetlistBrowserModel *model);
void push_back (NetlistModelItemData *child);
iterator begin () { return m_children.begin (); }
iterator end () { return m_children.end (); }
size_t child_count () { return m_children_per_index.size (); }
size_t index () { return m_index; }
NetlistModelItemData *child (size_t n);
virtual std::pair<const db::Circuit *, const db::Circuit *> circuits_of_this ();
std::pair<const db::Circuit *, const db::Circuit *> circuits ();
bool derived_from_circuits (const std::pair<const db::Circuit *, const db::Circuit *> &sp);
virtual std::pair<const db::Device *, const db::Device *> devices_of_this ();
std::pair<const db::Device *, const db::Device *> devices ();
bool derived_from_devices (const std::pair<const db::Device *, const db::Device *> &sp);
virtual std::pair<const db::Pin *, const db::Pin *> pins_of_this ();
std::pair<const db::Pin *, const db::Pin *> pins ();
bool derived_from_pins (const std::pair<const db::Pin *, const db::Pin *> &sp);
virtual std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuits_of_this ();
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuits ();
bool derived_from_subcircuits (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sp);
virtual std::pair<const db::Net *, const db::Net *> nets_of_this ();
std::pair<const db::Net *, const db::Net *> nets ();
bool derived_from_nets (const std::pair<const db::Net *, const db::Net *> &np);
private:
NetlistModelItemData *mp_parent;
tl::list<NetlistModelItemData> m_children;
std::vector<NetlistModelItemData *> m_children_per_index;
bool m_children_made;
size_t m_index;
void set_index (size_t index) { m_index = index; }
virtual void do_ensure_children (NetlistBrowserModel *model) = 0;
};
/**
* @brief An object describing the instantiation path of a net, a device or a (sub)circuit pair
*
* This object applies to pairs of these objects. A class providing a path for a single
* object is NetlistObjectPath
*/
struct LAYBASIC_PUBLIC NetlistObjectPath
{
typedef std::list<const db::SubCircuit *> path_type;
typedef path_type::const_iterator path_iterator;
NetlistObjectPath () : root (0), net (0), device (0) { }
bool is_null () const
{
return ! root;
}
bool operator== (const NetlistObjectPath &other) const
{
return root == other.root && path == other.path && net == other.net && device == other.device;
}
bool operator!= (const NetlistObjectPath &other) const
{
return ! operator== (other);
}
const db::Circuit *root;
std::list<const db::SubCircuit *> path;
const db::Net *net;
const db::Device *device;
};
/**
* @brief An object describing the instantiation path of a net, a device or a (sub)circuit pair
*
* This object applies to pairs of these objects. A class providing a path for a single
* object is NetlistObjectPath
*/
struct LAYBASIC_PUBLIC NetlistObjectsPath
{
typedef std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> > path_type;
typedef path_type::const_iterator path_iterator;
NetlistObjectsPath () { }
bool is_null () const
{
return ! root.first && ! root.second;
}
static NetlistObjectsPath from_first(const NetlistObjectPath &p);
static NetlistObjectsPath from_second (const NetlistObjectPath &p);
NetlistObjectPath first () const;
NetlistObjectPath second () const;
bool operator== (const NetlistObjectsPath &other) const
{
return root == other.root && path == other.path && net == other.net && device == other.device;
}
bool operator!= (const NetlistObjectsPath &other) const
{
return ! operator== (other);
}
std::pair<const db::Circuit *, const db::Circuit *> root;
std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> > path;
std::pair<const db::Net *, const db::Net *> net;
std::pair<const db::Device *, const db::Device *> device;
};
/**
* @brief The NetlistBrowserModel
*
@ -125,31 +289,70 @@ public:
virtual QModelIndex parent (const QModelIndex &index) const;
virtual int rowCount (const QModelIndex &parent) const;
QModelIndex index_from_id (void *id, int column) const;
int status_column () const
{
return m_status_column;
}
std::pair<const db::Net *, const db::Net *> net_from_index (const QModelIndex &index) const;
QModelIndex index_from_net (const std::pair<const db::Net *, const db::Net *> &net) const;
QModelIndex index_from_net (const db::Net *net) const;
std::pair<const db::Circuit *, const db::Circuit *> circuit_from_index (const QModelIndex &index) const;
QModelIndex index_from_circuit (const std::pair<const db::Circuit *, const db::Circuit *> &circuit) const;
QModelIndex index_from_circuit (const db::Circuit *circuit) const;
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuit_from_index (const QModelIndex &index) const;
std::pair<const db::Device *, const db::Device *> device_from_index (const QModelIndex &index) const;
bool is_circuit_index (const QModelIndex &index) const
int object_column () const
{
return is_id_circuit (index.internalPointer ());
return m_object_column;
}
int first_column () const
{
return m_first_column;
}
int second_column () const
{
return m_second_column;
}
IndexedNetlistModel *indexer ()
{
return mp_indexer.get ();
}
std::pair<const db::Net *, const db::Net *> net_from_index (const QModelIndex &index, bool include_parents = true) const;
QModelIndex index_from_net (const std::pair<const db::Net *, const db::Net *> &net) const;
QModelIndex index_from_net (const db::Net *net) const;
std::pair<const db::Circuit *, const db::Circuit *> circuit_from_index (const QModelIndex &index, bool include_parents = true) const;
QModelIndex index_from_circuit (const std::pair<const db::Circuit *, const db::Circuit *> &circuit) const;
QModelIndex index_from_circuit (const db::Circuit *circuit) const;
QModelIndex index_from_subcircuit (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &subcircuits) const;
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuit_from_index (const QModelIndex &index, bool include_parents = true) const;
std::pair<const db::Device *, const db::Device *> device_from_index (const QModelIndex &index, bool include_parents = true) const;
void set_item_visibility (QTreeView *view, bool show_all, bool with_warnings);
QString make_link_to (const std::pair<const db::Net *, const db::Net *> &nets, int column = 0) const;
QString make_link_to (const std::pair<const db::Device *, const db::Device *> &devices, int column = 0) const;
QString make_link_to (const std::pair<const db::Pin *, const db::Pin *> &pins, const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sub_circuits, int column = 0) const;
bool is_valid_net_pair (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_nets (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_connection (const std::pair<const db::Net *, const db::Net *> &net) const;
QModelIndex index_from_url (const QString &url) const;
NetlistObjectsPath path_from_index (const QModelIndex &index) const;
NetlistObjectPath spath_from_index (const QModelIndex &index) const
{
return path_from_index (index).first ();
}
QModelIndex index_from_path (const NetlistObjectsPath &path);
QModelIndex index_from_path (const NetlistObjectPath &path)
{
return index_from_path (NetlistObjectsPath::from_first (path));
}
private slots:
void colors_changed ();
@ -157,73 +360,18 @@ private:
NetlistBrowserModel (const NetlistBrowserModel &);
NetlistBrowserModel &operator= (const NetlistBrowserModel &);
void *make_id_circuit (size_t circuit_index) const;
void *make_id_circuit_pin (size_t circuit_index, size_t pin_index) const;
void *make_id_circuit_pin_net (size_t circuit_index, size_t pin_index, size_t net_index) const;
void *make_id_circuit_net (size_t circuit_index, size_t net_index) const;
void *make_id_circuit_net_device_terminal (size_t circuit_index, size_t net_index, size_t terminal_ref_index) const;
void *make_id_circuit_net_device_terminal_others (size_t circuit_index, size_t net_index, size_t terminal_ref_index, size_t other_index) const;
void *make_id_circuit_net_pin (size_t circuit_index, size_t net_index, size_t pin_index) const;
void *make_id_circuit_net_subcircuit_pin (size_t circuit_index, size_t net_index, size_t pin_ref_index) const;
void *make_id_circuit_net_subcircuit_pin_others (size_t circuit_index, size_t net_index, size_t pin_ref_index, size_t other_index) const;
void *make_id_circuit_subcircuit (size_t circuit_index, size_t subcircuit_index) const;
void *make_id_circuit_subcircuit_pin (size_t circuit_index, size_t subcircuit_index, size_t pin_index) const;
void *make_id_circuit_device (size_t circuit_index, size_t device_index) const;
void *make_id_circuit_device_terminal (size_t circuit_index, size_t device_index, size_t terminal_index) const;
bool is_id_circuit (void *id) const;
bool is_id_circuit_pin (void *id) const;
bool is_id_circuit_pin_net (void *id) const;
bool is_id_circuit_net (void *id) const;
bool is_id_circuit_net_device_terminal (void *id) const;
bool is_id_circuit_net_device_terminal_others (void *id) const;
bool is_id_circuit_net_pin (void *id) const;
bool is_id_circuit_net_subcircuit_pin (void *id) const;
bool is_id_circuit_net_subcircuit_pin_others (void *id) const;
bool is_id_circuit_subcircuit (void *id) const;
bool is_id_circuit_subcircuit_pin (void *id) const;
bool is_id_circuit_device (void *id) const;
bool is_id_circuit_device_terminal (void *id) const;
size_t circuit_index_from_id (void *id) const;
size_t circuit_pin_index_from_id (void *id) const;
size_t circuit_device_index_from_id (void *id) const;
size_t circuit_device_terminal_index_from_id (void *id) const;
size_t circuit_subcircuit_index_from_id (void *id) const;
size_t circuit_subcircuit_pin_index_from_id (void *id) const;
size_t circuit_net_index_from_id (void *id) const;
size_t circuit_net_pin_index_from_id (void *id) const;
size_t circuit_net_subcircuit_pin_index_from_id (void *id) const;
size_t circuit_net_subcircuit_pin_other_index_from_id (void *id) const;
size_t circuit_net_device_terminal_index_from_id (void *id) const;
size_t circuit_net_device_terminal_other_index_from_id (void *id) const;
std::pair<const db::Circuit *, const db::Circuit *> circuits_from_id (void *id) const;
std::pair<const db::Net *, const db::Net *> nets_from_id (void *id) const;
std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> net_subcircuit_pinrefs_from_id (void *id) const;
std::pair<const db::NetTerminalRef *, const db::NetTerminalRef *> net_terminalrefs_from_id (void *id) const;
std::pair<const db::NetPinRef *, const db::NetPinRef *> net_pinrefs_from_id (void *id) const;
std::pair<const db::Device *, const db::Device *> devices_from_id (void *id) const;
std::pair<const db::Pin *, const db::Pin *> pins_from_id (void *id) const;
std::pair<const db::SubCircuit *, const db::SubCircuit *> subcircuits_from_id (void *id) const;
QString text (const QModelIndex &index) const;
QVariant tooltip (const QModelIndex &index) const;
QString search_text (const QModelIndex &index) const;
db::NetlistCrossReference::Status status (const QModelIndex &index) const;
QIcon icon (const QModelIndex &index) const;
QString make_link_to (const std::pair<const db::Net *, const db::Net *> &nets, int column = 0) const;
QString make_link_to (const std::pair<const db::Device *, const db::Device *> &devices, int column = 0) const;
QString make_link_to (const std::pair<const db::Pin *, const db::Pin *> &pins, const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::Circuit *, const db::Circuit *> &circuits, int column = 0) const;
QString make_link_to (const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sub_circuits, int column = 0) const;
QString build_url (const QModelIndex &index, const std::string &title) const;
std::pair<const db::Netlist *, const db::Netlist *> netlists () const
{
return std::pair<const db::Netlist *, const db::Netlist *> (mp_l2ndb->netlist (), (const db::Netlist *)0);
}
bool is_valid_net_pair (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_nets (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_connection (const std::pair<const db::Net *, const db::Net *> &net) const;
void show_or_hide_items (QTreeView *view, const QModelIndex &parent, bool show_all, bool with_warnings, bool with_children);
db::LayoutToNetlist *mp_l2ndb;
@ -236,6 +384,9 @@ private:
int m_status_column;
int m_first_column;
int m_second_column;
std::auto_ptr<NetlistModelItemData> mp_root;
RootItemData *root () const;
};
} // namespace lay

View File

@ -69,7 +69,7 @@ inline const db::Circuit *deref_circuit (const db::Circuit *obj)
}
template <class Obj>
static db::ICplxTrans
static db::DCplxTrans
trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::ContextCache &cc, const db::DCplxTrans &initial = db::DCplxTrans ())
{
db::DCplxTrans t = initial;
@ -89,7 +89,6 @@ trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::Cont
}
db::CplxTrans dbu_trans (ly.dbu ());
db::ICplxTrans it = dbu_trans.inverted () * t * dbu_trans;
// The circuit may not be instantiated and still not be the top cell.
// This happens if the subcell does not have connections. In this case
@ -98,11 +97,11 @@ trans_for (const Obj *objs, const db::Layout &ly, const db::Cell &cell, db::Cont
if (circuit && ly.is_valid_cell_index (circuit->cell_index ())) {
std::pair<bool, db::ICplxTrans> tc = cc.find_layout_context (circuit->cell_index (), cell.cell_index ());
if (tc.first) {
it = tc.second * it;
t = dbu_trans * tc.second * dbu_trans.inverted () * t;
}
}
return it;
return t;
}
NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
@ -142,7 +141,8 @@ NetlistBrowserPage::NetlistBrowserPage (QWidget * /*parent*/)
QAction *sep;
directory_tree->addAction (m_show_all_action);
directory_tree->addAction (actionCollapseAll);
directory_tree->addAction (actionExpandAll);
// TODO: this gives a too big tree - confine to single branches?
// directory_tree->addAction (actionExpandAll);
sep = new QAction (directory_tree);
sep->setSeparator (true);
directory_tree->addAction (sep);
@ -306,21 +306,10 @@ NetlistBrowserPage::layer_list_changed (int)
void
NetlistBrowserPage::anchor_clicked (const QString &a)
{
QUrl url (a);
QString ids;
#if QT_VERSION >= 0x050000
ids = QUrlQuery (url.query ()).queryItemValue (QString::fromUtf8 ("id"));
#else
ids = url.queryItemValue (QString::fromUtf8 ("id"));
#endif
if (ids.isEmpty ()) {
return;
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
if (netlist_model) {
navigate_to (netlist_model->index_from_url (a), true);
}
void *id = reinterpret_cast<void *> (ids.toULongLong ());
navigate_to (id, true);
}
void
@ -334,6 +323,7 @@ NetlistBrowserPage::current_tree_index_changed (const QModelIndex &index)
return;
}
// TODO: this could give a path ...
std::pair<const db::Circuit *, const db::Circuit *> circuits = tree_model->circuits_from_index (index);
QModelIndex circuit_index = netlist_model->index_from_circuit (circuits);
@ -355,11 +345,10 @@ NetlistBrowserPage::current_index_changed (const QModelIndex &index)
return;
}
void *id = index.internalPointer ();
add_to_history (id, true);
add_to_history (index, true);
std::pair<const db::Circuit *, const db::Circuit *> circuits = netlist_model->circuit_from_index (index);
QModelIndex circuit_index = tree_model->index_from_circuits (circuits);
NetlistObjectsPath path = netlist_model->path_from_index (index);
QModelIndex circuit_index = tree_model->index_from_netpath (path);
m_signals_enabled = false;
hierarchy_tree->setCurrentIndex (circuit_index);
@ -380,6 +369,18 @@ NetlistBrowserPage::select_net (const db::Net *net)
}
}
void
NetlistBrowserPage::select_path (const lay::NetlistObjectsPath &path)
{
if (path.is_null ()) {
directory_tree->clearSelection ();
} else {
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
tl_assert (model != 0);
directory_tree->setCurrentIndex (model->index_from_path (path));
}
}
std::vector<const db::Net *>
NetlistBrowserPage::selected_nets ()
{
@ -411,7 +412,7 @@ NetlistBrowserPage::selected_circuits ()
QModelIndexList selection = directory_tree->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
if (i->column () == 0 && model->is_circuit_index (*i)) {
if (i->column () == 0) {
const db::Circuit *circuit = model->circuit_from_index (*i).first;
if (circuit) {
circuits.push_back (circuit);
@ -467,18 +468,23 @@ NetlistBrowserPage::selected_devices ()
void
NetlistBrowserPage::selection_changed ()
{
std::vector<const db::Net *> nets = selected_nets ();
if (mp_info_dialog) {
mp_info_dialog->set_nets (mp_database.get (), nets);
NetlistBrowserModel *model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
tl_assert (model != 0);
QModelIndexList selected = directory_tree->selectionModel ()->selectedIndexes ();
std::vector<lay::NetlistObjectsPath> selected_paths;
selected_paths.reserve (selected.size ());
for (QModelIndexList::const_iterator i = selected.begin (); i != selected.end (); ++i) {
if (i->column () == 0) {
selected_paths.push_back (model->path_from_index (*i));
}
}
std::vector<const db::Device *> devices = selected_devices ();
QModelIndex current = directory_tree->selectionModel ()->currentIndex ();
highlight (model->path_from_index (current), selected_paths);
std::vector<const db::SubCircuit *> subcircuits = selected_subcircuits ();
std::vector<const db::Circuit *> circuits = selected_circuits ();
highlight (nets, devices, subcircuits, circuits);
selection_changed_event ();
}
void
@ -518,15 +524,8 @@ NetlistBrowserPage::select_color_for_net ()
}
void
NetlistBrowserPage::navigate_to (void *id, bool fwd)
NetlistBrowserPage::navigate_to (const QModelIndex &index, bool fwd)
{
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
if (! tree_model || ! netlist_model) {
return;
}
QModelIndex index = netlist_model->index_from_id (id, 0);
if (! index.isValid ()) {
return;
}
@ -536,35 +535,42 @@ NetlistBrowserPage::navigate_to (void *id, bool fwd)
directory_tree->setCurrentIndex (index);
std::pair<const db::Circuit *, const db::Circuit *> circuits = netlist_model->circuit_from_index (index);
QModelIndex circuit_index = tree_model->index_from_circuits (circuits);
NetlistBrowserTreeModel *tree_model = dynamic_cast<NetlistBrowserTreeModel *> (hierarchy_tree->model ());
NetlistBrowserModel *netlist_model = dynamic_cast<NetlistBrowserModel *> (directory_tree->model ());
if (! tree_model || ! netlist_model) {
return;
}
lay::NetlistObjectsPath path = netlist_model->path_from_index (index);
QModelIndex circuit_index = tree_model->index_from_netpath (path);
hierarchy_tree->setCurrentIndex (circuit_index);
} catch (...) {
}
m_signals_enabled = true;
add_to_history (id, fwd);
add_to_history (index, fwd);
selection_changed ();
}
void
NetlistBrowserPage::add_to_history (void *id, bool fwd)
NetlistBrowserPage::add_to_history (const QModelIndex &index, bool fwd)
{
if (! fwd) {
if (m_history_ptr > 1) {
--m_history_ptr;
m_history [m_history_ptr - 1] = id;
m_history [m_history_ptr - 1] = index;
}
} else if (m_history_ptr >= m_history.size ()) {
m_history.push_back (id);
m_history.push_back (index);
m_history_ptr = m_history.size ();
} else {
if (m_history [m_history_ptr] != id) {
if (m_history [m_history_ptr] != index) {
m_history.erase (m_history.begin () + m_history_ptr + 1, m_history.end ());
}
m_history [m_history_ptr] = id;
m_history [m_history_ptr] = index;
++m_history_ptr;
}
@ -730,7 +736,7 @@ NetlistBrowserPage::find_button_pressed ()
QModelIndex next = find_next (directory_tree, directory_tree->model (), re, directory_tree->currentIndex ());
if (next.isValid ()) {
navigate_to (next.internalPointer ());
navigate_to (next);
}
}
@ -795,11 +801,12 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
m_signals_enabled = se;
clear_markers ();
highlight (std::vector<const db::Net *> (), std::vector<const db::Device *> (), std::vector<const db::SubCircuit *> (), std::vector<const db::Circuit *> ());
m_cell_context_cache = db::ContextCache (mp_database.get () ? mp_database->internal_layout () : 0);
setup_trees ();
selection_changed_event ();
}
void
@ -886,21 +893,16 @@ NetlistBrowserPage::setup_trees ()
}
void
NetlistBrowserPage::highlight (const std::vector<const db::Net *> &nets, const std::vector<const db::Device *> &devices, const std::vector<const db::SubCircuit *> &subcircuits, const std::vector<const db::Circuit *> &circuits)
NetlistBrowserPage::highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths)
{
if (nets != m_current_nets || devices != m_current_devices || subcircuits != m_current_subcircuits || circuits != m_current_circuits) {
if (current_path != m_current_path && selected_paths != m_selected_paths) {
m_current_nets = nets;
m_current_devices = devices;
m_current_subcircuits = subcircuits;
m_current_circuits = circuits;
m_current_path = current_path;
m_selected_paths = selected_paths;
clear_markers ();
if (! nets.empty () || ! devices.empty () || ! subcircuits.empty () || ! circuits.empty ()) {
adjust_view ();
update_highlights ();
}
adjust_view ();
update_highlights ();
}
}
@ -928,7 +930,8 @@ bbox_for_device_abstract (const db::Layout *layout, const db::DeviceAbstract *de
return db::Box ();
}
return layout->cell (device_abstract->cell_index ()).bbox ().transformed (db::CplxTrans (layout->dbu ()).inverted () * trans * db::CplxTrans (layout->dbu ()));}
return layout->cell (device_abstract->cell_index ()).bbox ().transformed (db::CplxTrans (layout->dbu ()).inverted () * trans * db::CplxTrans (layout->dbu ()));
}
static db::Box
bbox_for_circuit (const db::Layout *layout, const db::Circuit *circuit)
@ -945,12 +948,6 @@ bbox_for_circuit (const db::Layout *layout, const db::Circuit *circuit)
return layout->cell (circuit->cell_index ()).bbox ();
}
static db::Box
bbox_for_subcircuit (const db::Layout *layout, const db::SubCircuit *subcircuit)
{
return bbox_for_circuit (layout, subcircuit->circuit_ref ());
}
void
NetlistBrowserPage::adjust_view ()
{
@ -967,57 +964,80 @@ NetlistBrowserPage::adjust_view ()
return;
}
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
const db::Circuit *top_circuit = mp_database->netlist ()->circuit_by_name (original_layout.cell_name (mp_view->cellview (m_cv_index).cell_index ()));
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
const db::Cell *cell = (top_circuit && layout->is_valid_cell_index (top_circuit->cell_index ()) ? &layout->cell (top_circuit->cell_index ()) : mp_database->internal_top_cell ());
if (! layout || ! cell) {
return;
}
db::DBox bbox;
db::Box bbox;
for (std::vector<lay::NetlistObjectsPath>::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) {
for (std::vector<const db::Net *>::const_iterator net = m_current_nets.begin (); net != m_current_nets.end (); ++net) {
const db::Circuit *circuit = path->root.first;
if (! circuit) {
continue;
}
db::ICplxTrans net_trans = trans_for (*net, *layout, *cell, m_cell_context_cache);
db::DCplxTrans trans;
db::cell_index_type cell_index = (*net)->circuit ()->cell_index ();
size_t cluster_id = (*net)->cluster_id ();
trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = path->path.begin (); p != path->path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
} else {
circuit = 0;
}
}
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ();
++shapes;
if (! circuit) {
continue;
}
db::Box ebox;
const db::Device *device = path->device.first;
const db::Net *net = path->net.first;
if (device) {
ebox += bbox_for_device_abstract (layout, device->device_abstract (), db::DCplxTrans ());
const std::vector<db::DeviceAbstractRef> &oda = device->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
ebox += bbox_for_device_abstract (layout, a->device_abstract, a->trans);
}
bbox += net_trans * layer_bbox;
} else if (net) {
db::cell_index_type cell_index = net->circuit ()->cell_index ();
size_t cluster_id = net->cluster_id ();
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ().transformed (shapes.trans ());
++shapes;
}
ebox += layer_bbox;
}
} else if (circuit) {
ebox += bbox_for_circuit (layout, circuit);
}
}
bbox += trans * db::CplxTrans (layout->dbu ()) * ebox;
for (std::vector<const db::Device *>::const_iterator device = m_current_devices.begin (); device != m_current_devices.end (); ++device) {
db::ICplxTrans trans = trans_for (*device, *layout, *cell, m_cell_context_cache, (*device)->trans ());
bbox += trans * bbox_for_device_abstract (layout, (*device)->device_abstract (), db::DCplxTrans ());
const std::vector<db::DeviceAbstractRef> &oda = (*device)->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
bbox += trans * bbox_for_device_abstract (layout, a->device_abstract, a->trans);
}
}
for (std::vector<const db::SubCircuit *>::const_iterator subcircuit = m_current_subcircuits.begin (); subcircuit != m_current_subcircuits.end (); ++subcircuit) {
bbox += trans_for (*subcircuit, *layout, *cell, m_cell_context_cache, (*subcircuit)->trans ()) * bbox_for_subcircuit (layout, *subcircuit);
}
for (std::vector<const db::Circuit *>::const_iterator circuit = m_current_circuits.begin (); circuit != m_current_circuits.end (); ++circuit) {
bbox += trans_for (*circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ()) * bbox_for_circuit (layout, *circuit);
}
if (! bbox.empty ()) {
@ -1025,9 +1045,8 @@ NetlistBrowserPage::adjust_view ()
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
db::DBox tv_bbox;
db::DBox dbu_bbox = db::CplxTrans (layout->dbu ()) * bbox;
for (std::vector<db::DCplxTrans>::const_iterator t = tv.begin (); t != tv.end (); ++t) {
tv_bbox += *t * dbu_bbox;
tv_bbox += *t * bbox;
}
if (m_window == lay::NetlistBrowserConfig::FitNet) {
@ -1065,12 +1084,10 @@ bool
NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans device_trans = trans_for (device, *layout, *cell, m_cell_context_cache, device->trans ());
QColor color = make_valid_color (m_colorizer.marker_color ());
db::Box device_bbox = bbox_for_device_abstract (layout, device->device_abstract (), db::DCplxTrans ());
db::Box device_bbox = bbox_for_device_abstract (layout, device->device_abstract (), device->trans ());
if (! device_bbox.empty ()) {
if (n_markers == m_max_shape_count) {
@ -1080,7 +1097,7 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (device_bbox, device_trans, tv);
mp_markers.back ()->set (device_bbox, db::ICplxTrans (), tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
@ -1090,7 +1107,7 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
const std::vector<db::DeviceAbstractRef> &oda = device->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
db::Box da_box = bbox_for_device_abstract (layout, a->device_abstract, a->trans);
db::Box da_box = bbox_for_device_abstract (layout, a->device_abstract, device->trans () * a->trans);
if (! da_box.empty ()) {
if (n_markers == m_max_shape_count) {
@ -1100,7 +1117,7 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (da_box, device_trans, tv);
mp_markers.back ()->set (da_box, db::ICplxTrans (), tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
@ -1112,40 +1129,10 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
return false;
}
bool
NetlistBrowserPage::produce_highlights_for_subcircuit (const db::SubCircuit *subcircuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans subcircuit_trans = trans_for (subcircuit, *layout, *cell, m_cell_context_cache, subcircuit->trans ());
QColor color = make_valid_color (m_colorizer.marker_color ());
db::Box circuit_bbox = bbox_for_subcircuit (layout, subcircuit);
if (circuit_bbox.empty ()) {
return false;
}
if (n_markers == m_max_shape_count) {
return true;
}
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (circuit_bbox, subcircuit_trans, tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
return false;
}
bool
NetlistBrowserPage::produce_highlights_for_circuit (const db::Circuit *circuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans circuit_trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
QColor color = make_valid_color (m_colorizer.marker_color ());
db::Box circuit_bbox = bbox_for_circuit (layout, circuit);
@ -1160,7 +1147,7 @@ NetlistBrowserPage::produce_highlights_for_circuit (const db::Circuit *circuit,
++n_markers;
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (circuit_bbox, circuit_trans, tv);
mp_markers.back ()->set (circuit_bbox, db::ICplxTrans (), tv);
mp_markers.back ()->set_color (color);
mp_markers.back ()->set_frame_color (color);
configure_marker (mp_markers.back (), false);
@ -1172,8 +1159,6 @@ bool
NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_markers, const std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> &display_by_lp, const std::vector<db::DCplxTrans> &tv)
{
const db::Layout *layout = mp_database->internal_layout ();
const db::Cell *cell = mp_database->internal_top_cell ();
db::ICplxTrans net_trans = trans_for (net, *layout, *cell, m_cell_context_cache);
db::cell_index_type cell_index = net->circuit ()->cell_index ();
size_t cluster_id = net->cluster_id ();
@ -1199,7 +1184,7 @@ NetlistBrowserPage::produce_highlights_for_net (const db::Net *net, size_t &n_ma
}
mp_markers.push_back (new lay::Marker (mp_view, m_cv_index));
mp_markers.back ()->set (shapes->polygon_ref (), net_trans * shapes.trans (), tv);
mp_markers.back ()->set (shapes->polygon_ref (), shapes.trans (), tv);
if (net_color.isValid ()) {
@ -1272,14 +1257,14 @@ NetlistBrowserPage::update_highlights ()
}
const db::Layout &original_layout = mp_view->cellview (m_cv_index)->layout ();
const db::Circuit *top_circuit = mp_database->netlist ()->circuit_by_name (original_layout.cell_name (mp_view->cellview (m_cv_index).cell_index ()));
const db::Layout *layout = mp_database->internal_layout ();
if (! layout) {
const db::Cell *cell = (top_circuit && layout->is_valid_cell_index (top_circuit->cell_index ()) ? &layout->cell (top_circuit->cell_index ()) : mp_database->internal_top_cell ());
if (! layout || ! cell) {
return;
}
// a map of display properties vs. layer properties
std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> display_by_lp;
for (lay::LayerPropertiesConstIterator lp = mp_view->begin_layers (); ! lp.at_end (); ++lp) {
if (! lp->has_children () && lp->cellview_index () == int (m_cv_index) && lp->layer_index () >= 0 && (unsigned int) lp->layer_index () < original_layout.layers ()) {
@ -1287,36 +1272,55 @@ NetlistBrowserPage::update_highlights ()
}
}
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
// correct DBU differences between the storage layout and the original layout
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
*t = *t * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
size_t n_markers = 0;
bool not_all_shapes_are_shown = false;
for (std::vector<const db::Net *>::const_iterator net = m_current_nets.begin (); net != m_current_nets.end () && ! not_all_shapes_are_shown; ++net) {
if ((*net)->circuit ()) {
not_all_shapes_are_shown = produce_highlights_for_net (*net, n_markers, display_by_lp, tv);
}
}
for (std::vector<lay::NetlistObjectsPath>::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) {
for (std::vector<const db::Device *>::const_iterator device = m_current_devices.begin (); device != m_current_devices.end () && ! not_all_shapes_are_shown; ++device) {
if ((*device)->circuit ()) {
not_all_shapes_are_shown = produce_highlights_for_device (*device, n_markers, tv);
const db::Circuit *circuit = path->root.first;
if (! circuit) {
continue;
}
}
for (std::vector<const db::SubCircuit *>::const_iterator subcircuit = m_current_subcircuits.begin (); subcircuit != m_current_subcircuits.end () && ! not_all_shapes_are_shown; ++subcircuit) {
if ((*subcircuit)->circuit ()) {
not_all_shapes_are_shown = produce_highlights_for_subcircuit (*subcircuit, n_markers, tv);
// computes the transformation supplied by the path
db::DCplxTrans trans = trans_for (circuit, *layout, *cell, m_cell_context_cache, db::DCplxTrans ());
for (std::list<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator p = path->path.begin (); p != path->path.end () && circuit; ++p) {
if (p->first) {
circuit = p->first->circuit_ref ();
trans = trans * p->first->trans ();
} else {
circuit = 0;
}
}
if (! circuit) {
continue;
}
// a map of display properties vs. layer properties
// correct DBU differences between the storage layout and the original layout
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
*t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
if (path->net.first) {
if (produce_highlights_for_net (path->net.first, n_markers, display_by_lp, tv)) {
not_all_shapes_are_shown = true;
}
} else if (path->device.first) {
if (produce_highlights_for_device (path->device.first, n_markers, tv)) {
not_all_shapes_are_shown = true;
}
} else if (circuit) {
if (produce_highlights_for_circuit (circuit, n_markers, tv)) {
not_all_shapes_are_shown = true;
}
}
}
for (std::vector<const db::Circuit *>::const_iterator circuit = m_current_circuits.begin (); circuit != m_current_circuits.end () && ! not_all_shapes_are_shown; ++circuit) {
not_all_shapes_are_shown = produce_highlights_for_circuit (*circuit, n_markers, tv);
}
if (not_all_shapes_are_shown) {
@ -1327,7 +1331,6 @@ NetlistBrowserPage::update_highlights ()
} else {
info_label->hide ();
}
}
void

View File

@ -33,6 +33,7 @@
#include "dbLayoutUtils.h"
#include "tlObject.h"
#include "tlEvents.h"
#include <QFrame>
@ -90,25 +91,14 @@ public:
/**
* @brief Attaches the page to a L2N DB
*/
void set_l2ndb (db::LayoutToNetlist *database)
{
set_db (database);
}
void set_db (db::LayoutToNetlist *database);
/**
* @brief Attaches the page to a LVS DB
* @brief Gets the database the page is connected to
*/
void set_lvsdb (db::LayoutVsSchematic *database)
db::LayoutToNetlist *db ()
{
set_db (database);
}
/**
* @brief Detaches the page from any DB
*/
void reset_db ()
{
set_db (0);
return mp_database.get ();
}
/**
@ -116,6 +106,22 @@ public:
*/
void select_net (const db::Net *net);
/**
* @brief Selects a netlist object (a circuit, a subcircuit, a net or a device)
*/
void select_path (const lay::NetlistObjectPath &path)
{
select_path (lay::NetlistObjectsPath::from_first (path));
}
/**
* @brief Selects a netlist object (a circuit, a subcircuit, a net or a device)
*
* This variant allows specifying a paired path using either an object from the first,
* the second netlist of both.
*/
void select_path (const lay::NetlistObjectsPath &path);
/**
* @brief Set the window type and window dimensions
*/
@ -162,6 +168,27 @@ public:
*/
void update_highlights ();
/**
* @brief Gets the current object's path
*/
const lay::NetlistObjectsPath &current_path () const
{
return m_current_path;
}
/**
* @brief Gets the selected nets
*/
const std::vector<lay::NetlistObjectsPath> &selected_paths () const
{
return m_selected_paths;
}
/**
* @brief An event indicating that the selection has changed
*/
tl::Event selection_changed_event;
public slots:
void export_all ();
void export_selected ();
@ -201,28 +228,25 @@ private:
unsigned int m_cv_index;
lay::Dispatcher *mp_plugin_root;
tl::weak_ptr<db::LayoutToNetlist> mp_database;
std::vector<void *> m_history;
std::vector<QModelIndex> m_history;
size_t m_history_ptr;
bool m_signals_enabled;
std::vector <lay::Marker *> mp_markers;
bool m_enable_updates;
bool m_update_needed;
std::vector<const db::Net *> m_current_nets;
std::vector<const db::Device *> m_current_devices;
std::vector<const db::SubCircuit *> m_current_subcircuits;
std::vector<const db::Circuit *> m_current_circuits;
lay::NetlistObjectsPath m_current_path;
std::vector<lay::NetlistObjectsPath> m_selected_paths;
lay::NetInfoDialog *mp_info_dialog;
tl::DeferredMethod<NetlistBrowserPage> dm_update_highlights;
tl::DeferredMethod<NetlistBrowserPage> dm_rerun_macro;
db::ContextCache m_cell_context_cache;
void set_db (db::LayoutToNetlist *l2ndb);
void setup_trees ();
void add_to_history (void *id, bool fwd);
void navigate_to (void *id, bool forward = true);
void add_to_history (const QModelIndex &index, bool fwd);
void navigate_to (const QModelIndex &index, bool forward = true);
void adjust_view ();
void clear_markers ();
void highlight (const std::vector<const db::Net *> &nets, const std::vector<const db::Device *> &devices, const std::vector<const db::SubCircuit *> &subcircuits, const std::vector<const db::Circuit *> &circuits);
void highlight (const NetlistObjectsPath &current_path, const std::vector<NetlistObjectsPath> &selected_paths);
std::vector<const db::Net *> selected_nets ();
std::vector<const db::Device *> selected_devices ();
std::vector<const db::SubCircuit *> selected_subcircuits ();
@ -232,7 +256,6 @@ private:
QColor make_valid_color (const QColor &color);
bool produce_highlights_for_net(const db::Net *net, size_t &n_markers, const std::map<db::LayerProperties, lay::LayerPropertiesConstIterator> &display_by_lp, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_device (const db::Device *device, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_subcircuit (const db::SubCircuit *subcircuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
bool produce_highlights_for_circuit (const db::Circuit *circuit, size_t &n_markers, const std::vector<db::DCplxTrans> &tv);
void configure_marker (lay::Marker *marker, bool with_fill);
void rerun_macro ();

View File

@ -24,6 +24,7 @@
#include "layNetlistBrowserTreeModel.h"
#include "layIndexedNetlistModel.h"
#include "layNetlistCrossReferenceModel.h"
#include "layNetlistBrowserModel.h"
#include <QPainter>
#include <QIcon>
@ -257,6 +258,45 @@ NetlistBrowserTreeModel::build_circuits_to_index (size_t nprod, const std::pair<
}
}
static bool is_compatible (const std::pair<const db::Circuit *, const db::Circuit *> &a, const std::pair<const db::Circuit *, const db::Circuit *> &b)
{
if (a.first && b.first && a.first == b.first) {
return true;
} else if (a.second && b.second && a.second == b.second) {
return true;
} else {
return false;
}
}
QModelIndex
NetlistBrowserTreeModel::index_from_netpath (const NetlistObjectsPath &path) const
{
QModelIndex idx;
idx = index_from_circuits (path.root);
for (NetlistObjectsPath::path_iterator p = path.path.begin (); p != path.path.end () && idx.isValid (); ++p) {
std::pair<const db::Circuit *, const db::Circuit *> sc (p->first ? p->first->circuit_ref () : 0, p->second ? p->second->circuit_ref (): 0);
std::pair<const db::Circuit *, const db::Circuit *> circuit = circuits_from_index (idx);
size_t count = mp_indexer->child_circuit_count (circuit);
for (size_t n = count; n > 0; ) {
--n;
std::pair<const db::Circuit *, const db::Circuit *> cc = mp_indexer->child_circuit_from_index (circuit, n).first;
if (is_compatible (sc, cc)) {
circuit = cc;
idx = index (n, 0, idx);
break;
}
}
}
return idx;
}
QModelIndex
NetlistBrowserTreeModel::index_from_circuits (const std::pair<const db::Circuit *, const db::Circuit *> &circuits) const
{
@ -382,7 +422,7 @@ NetlistBrowserTreeModel::parent (const QModelIndex &index) const
nprod /= nnlast;
return createIndex (int (ids / nprod - 1), index.column (), reinterpret_cast<void *> (ids));
return createIndex (int (ids / nprod - 1), 0, reinterpret_cast<void *> (ids));
}

View File

@ -42,6 +42,7 @@ namespace lay
{
class IndexedNetlistModel;
struct NetlistObjectsPath;
// ----------------------------------------------------------------------------------
// NetlistBrowserTreeModel definition
@ -77,6 +78,7 @@ public:
std::pair<const db::Circuit *, const db::Circuit *> circuits_from_index (const QModelIndex &index) const;
QModelIndex index_from_circuits (const std::pair<const db::Circuit *, const db::Circuit *> &circuits) const;
QModelIndex index_from_netpath (const NetlistObjectsPath &path) const;
private:
NetlistBrowserTreeModel (const NetlistBrowserTreeModel &);

View File

@ -308,6 +308,161 @@ const db::Circuit *NetlistCrossReferenceModel::second_circuit_for (const db::Cir
return mp_cross_ref->other_circuit_for (first);
}
namespace {
// TODO: borrowed from dbNetlistCrossReference.cc
static int string_value_compare (const std::string &a, const std::string &b)
{
return a == b ? 0 : (a < b ? -1 : 1);
}
template <class Obj>
struct by_expanded_name_value_compare
{
int operator() (const Obj &a, const Obj &b) const
{
return string_value_compare (a.expanded_name (), b.expanded_name ());
}
};
template <class Obj> struct net_object_compare;
template <>
struct net_object_compare<db::NetSubcircuitPinRef>
{
int operator() (const db::NetSubcircuitPinRef &a, const db::NetSubcircuitPinRef &b) const
{
int ct = by_expanded_name_value_compare<db::SubCircuit> () (*a.subcircuit (), *b.subcircuit ());
if (ct == 0) {
return by_expanded_name_value_compare<db::Pin> () (*a.pin (), *b.pin ());
} else {
return ct;
}
}
};
template <class Obj, class ValueCompare>
struct two_pointer_compare
{
int operator() (const Obj *a, const Obj *b) const
{
if ((a == 0) != (b == 0)) {
return (a == 0) > (b == 0) ? -1 : 1;
}
if (a != 0) {
return ValueCompare () (*a, *b);
} else {
return 0;
}
}
};
template <class Obj, class ValueCompare>
struct two_pair_compare
{
bool operator() (const std::pair<const Obj *, const Obj *> &a, const std::pair<const Obj *, const Obj *> &b)
{
int ct = two_pointer_compare<Obj, ValueCompare> () (a.first, b.first);
if (ct != 0) {
return ct < 0;
}
return two_pointer_compare<Obj, ValueCompare> () (a.second, b.second) < 0;
}
};
struct SortNetSubCircuitPins
: public two_pair_compare<db::NetSubcircuitPinRef, net_object_compare<db::NetSubcircuitPinRef> >
{
// .. nothing yet ..
};
}
void NetlistCrossReferenceModel::ensure_subcircuit_data_built () const
{
if (! m_per_subcircuit_data.empty ()) {
return;
}
// Build the net to subcircuit ref table
for (db::NetlistCrossReference::circuits_iterator c = mp_cross_ref->begin_circuits (); c != mp_cross_ref->end_circuits (); ++c) {
const db::NetlistCrossReference::PerCircuitData *data = mp_cross_ref->per_circuit_data_for (*c);
if (! data) {
continue;
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_type::const_iterator sc = data->subcircuits.begin (); sc != data->subcircuits.end (); ++sc) {
const std::pair<const db::SubCircuit *, const db::SubCircuit *> &sc_pair = sc->pair;
if (sc_pair.first && sc_pair.second) {
PerSubCircuitCacheData &sc_data = m_per_subcircuit_data [sc_pair];
std::multimap<const db::Net *, const db::NetSubcircuitPinRef *> first_net_to_other_netref;
for (size_t i = 0; i < sc_pair.second->circuit_ref ()->pin_count (); ++i) {
const db::NetSubcircuitPinRef *n2 = sc_pair.second->netref_for_pin (i);
if (n2) {
const db::Net *n1 = mp_cross_ref->other_net_for (n2->net ());
if (n1) {
first_net_to_other_netref.insert (std::make_pair (n1, n2));
} else {
sc_data.nets_per_pins.push_back (std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> (0, n2));
}
}
}
for (size_t i = 0; i < sc_pair.first->circuit_ref ()->pin_count (); ++i) {
const db::NetSubcircuitPinRef *n1 = sc_pair.first->netref_for_pin (i);
if (n1) {
const db::NetSubcircuitPinRef *n2 = 0;
std::multimap<const db::Net *, const db::NetSubcircuitPinRef *>::iterator m = first_net_to_other_netref.find (n1->net ());
if (m != first_net_to_other_netref.end () && m->first == n1->net ()) {
n2 = m->second;
first_net_to_other_netref.erase (m);
}
sc_data.nets_per_pins.push_back (std::make_pair (n1, n2));
}
}
std::sort (sc_data.nets_per_pins.begin (), sc_data.nets_per_pins.end (), SortNetSubCircuitPins ());
}
}
}
}
size_t NetlistCrossReferenceModel::subcircuit_pin_count (const subcircuit_pair &subcircuits) const
{
ensure_subcircuit_data_built ();
std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, PerSubCircuitCacheData>::const_iterator sc = m_per_subcircuit_data.find (subcircuits);
if (sc != m_per_subcircuit_data.end ()) {
return sc->second.nets_per_pins.size ();
} else {
return std::max (subcircuits.first ? subcircuits.first->circuit_ref ()->pin_count () : 0, subcircuits.second ? subcircuits.second->circuit_ref ()->pin_count () : 0);
}
}
IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::subcircuit_pinref_from_index (const subcircuit_pair &subcircuits, size_t index) const
{
ensure_subcircuit_data_built ();
std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, PerSubCircuitCacheData>::const_iterator sc = m_per_subcircuit_data.find (subcircuits);
if (sc != m_per_subcircuit_data.end ()) {
if (index < sc->second.nets_per_pins.size ()) {
return sc->second.nets_per_pins [index];
} else {
return IndexedNetlistModel::net_subcircuit_pin_pair (0, 0);
}
} else {
return IndexedNetlistModel::net_subcircuit_pin_pair (subcircuits.first ? subcircuits.first->netref_for_pin (index) : 0, subcircuits.second ? subcircuits.second->netref_for_pin (index) : 0);
}
}
IndexedNetlistModel::net_subcircuit_pin_pair NetlistCrossReferenceModel::net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const
{
const db::NetlistCrossReference::PerNetData *data = mp_cross_ref->per_net_data_for (nets);

View File

@ -48,6 +48,7 @@ public:
virtual size_t net_count (const circuit_pair &circuits) const;
virtual size_t net_terminal_count (const net_pair &nets) const;
virtual size_t net_subcircuit_pin_count (const net_pair &nets) const;
virtual size_t subcircuit_pin_count (const subcircuit_pair &subcircuits) const;
virtual size_t net_pin_count (const net_pair &nets) const;
virtual size_t device_count (const circuit_pair &circuits) const;
virtual size_t pin_count (const circuit_pair &circuits) const;
@ -65,6 +66,7 @@ public:
virtual const db::Net *second_net_for (const db::Net *first) const;
virtual const db::Circuit *second_circuit_for (const db::Circuit *first) const;
virtual net_subcircuit_pin_pair net_subcircuit_pinref_from_index (const net_pair &nets, size_t index) const;
virtual net_subcircuit_pin_pair subcircuit_pinref_from_index (const subcircuit_pair &nets, size_t index) const;
virtual net_terminal_pair net_terminalref_from_index (const net_pair &nets, size_t index) const;
virtual net_pin_pair net_pinref_from_index (const net_pair &nets, size_t index) const;
virtual std::pair<device_pair, Status> device_from_index (const circuit_pair &circuits, size_t index) const;
@ -95,6 +97,11 @@ public:
std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, size_t> index_of_subcircuits;
};
struct PerSubCircuitCacheData
{
std::vector<std::pair<const db::NetSubcircuitPinRef *, const db::NetSubcircuitPinRef *> > nets_per_pins;
};
tl::weak_ptr<db::NetlistCrossReference> mp_cross_ref;
mutable std::map<net_pair, circuit_pair> m_parents_of_nets;
mutable std::map<device_pair, circuit_pair> m_parents_of_devices;
@ -104,6 +111,9 @@ public:
mutable std::vector<circuit_pair> m_top_level_circuits;
mutable std::map<std::pair<const db::Circuit *, const db::Circuit *>, PerCircuitCacheData> m_per_circuit_data;
mutable std::map<std::pair<const db::Circuit *, const db::Circuit *>, size_t> m_index_of_circuits;
mutable std::map<std::pair<const db::SubCircuit *, const db::SubCircuit *>, PerSubCircuitCacheData> m_per_subcircuit_data;
void ensure_subcircuit_data_built () const;
};
}

View File

@ -809,15 +809,31 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co
} else {
// The array (or single instance) must be iterated instance
// by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
size_t qid = 0;
// The array (or single instance) must be iterated instance by instance
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
test_snapshot (0);
db::ICplxTrans t (cell_inst.complex_trans (*p));
db::Box new_vp = db::Box (t.inverted () * *v);
draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1);
if (p.quad_id () > 0 && p.quad_id () != qid) {
qid = p.quad_id ();
// if the quad is very small we don't gain anything from looking further into the quad - skip this one
db::DBox qb = trans * cell_inst.quad_box (p, bc);
if (qb.width () < 1.0 && qb.height () < 1.0) {
p.skip_quad ();
continue;
}
}
++p;
}
}
@ -986,7 +1002,6 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty
++inst;
}
}
}
@ -1582,6 +1597,8 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
db::Shape last_array;
size_t current_quad_id = 0;
size_t current_array_quad_id = 0;
db::ShapeIterator shape (shapes.begin_touching (*v, db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths, mp_prop_sel, m_inv_prop_sel));
while (! shape.at_end ()) {
@ -1596,16 +1613,18 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
}
if (skip) {
shape.skip_quad ();
continue;
}
} else {
if (shape.in_array ()) {
bool simplified = false;
if (shape.in_array () && last_array != shape.array ()) {
if (last_array != shape.array ()) {
last_array = shape.array ();
current_array_quad_id = 0;
bool simplified = false;
if (last_array.type () == db::Shape::PolygonPtrArray) {
simplified = draw_array_simplified<db::Shape::polygon_ptr_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
@ -1619,17 +1638,42 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
simplified = draw_array_simplified<db::Shape::short_box_array_type> (mp_renderer.get (), last_array, frame, vertex, trans);
}
if (simplified) {
shape.finish_array ();
// continue with the next shape, array or quad
continue;
}
}
if (simplified) {
shape.finish_array ();
} else {
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
++shape;
} else {
current_array_quad_id = 0;
}
// try whether the array quad can be simplified
size_t aqid = shape.array_quad_id ();
if (aqid != 0 && aqid != current_array_quad_id) {
current_array_quad_id = aqid;
db::DBox qbbox = trans * shape.array_quad_box ();
if (qbbox.width () < 1.5 && qbbox.height () < 1.5) {
// draw a single box instead of the quad
mp_renderer->draw (qbbox, fill, frame, vertex, text);
shape.skip_array_quad ();
// continue with the next shape, array or quad
continue;
}
}
mp_renderer->draw (*shape, trans, fill, frame, vertex, text);
++shape;
}
}
@ -1727,7 +1771,9 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
} else if (anything) {
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) {
size_t qid = 0;
for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) {
if (! m_draw_array_border_instances ||
p.index_a () <= 0 || (unsigned long)p.index_a () == amax - 1 || p.index_b () <= 0 || (unsigned long)p.index_b () == bmax - 1) {
@ -1736,6 +1782,21 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_
db::Box new_vp = db::Box (t.inverted () * *v);
draw_layer (from_level, to_level, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, update_snapshot);
if (p.quad_id () > 0 && p.quad_id () != qid) {
qid = p.quad_id ();
// if the quad is very small we don't gain anything from looking further into the quad - skip this one
db::DBox qb = trans * cell_inst.quad_box (p, bc);
if (qb.width () < 1.0 && qb.height () < 1.0) {
p.skip_quad ();
continue;
}
}
++p;
}
}

View File

@ -128,12 +128,9 @@ TipDialog::no_pressed ()
accept ();
}
bool
TipDialog::do_exec_dialog (button_type *button)
static std::pair<bool, int>
tip_dialog_status (const std::string &key)
{
bool must_show = true;
mp_res = button;
std::string th;
if (lay::Dispatcher::instance ()) {
lay::Dispatcher::instance ()->config_get (cfg_tip_window_hidden, th);
@ -148,20 +145,39 @@ TipDialog::do_exec_dialog (button_type *button)
}
int r = -1;
ex.test ("=") && ex.try_read (r);
if (k == m_key) {
if (r >= 0) {
*mp_res = button_type (r);
}
must_show = false;
break;
if (k == key) {
return std::make_pair (false, r);
}
ex.test (",");
}
if (must_show) {
return std::make_pair (true, -1);
}
bool
TipDialog::will_be_shown ()
{
return tip_dialog_status (m_key).first;
}
bool
TipDialog::do_exec_dialog (button_type *button)
{
mp_res = button;
std::string th;
if (lay::Dispatcher::instance ()) {
lay::Dispatcher::instance ()->config_get (cfg_tip_window_hidden, th);
}
std::pair<bool, int> td_status = tip_dialog_status (m_key);
if (td_status.first) {
exec ();
return true;
} else {
if (td_status.second >= 0) {
*mp_res = button_type (td_status.second);
}
return false;
}
}

View File

@ -65,6 +65,11 @@ public:
*/
~TipDialog ();
/**
* @brief Returns true, if the tip dialog will be shown
*/
bool will_be_shown ();
/**
* @brief Show the dialog
*

View File

@ -183,7 +183,8 @@ SOURCES = \
layGenericSyntaxHighlighter.cc \
layDispatcher.cc \
laySelectCellViewForm.cc \
layLayoutStatisticsForm.cc
layLayoutStatisticsForm.cc \
gsiDeclLayNetlistBrowserDialog.cc
HEADERS = \
gtf.h \

View File

@ -44,90 +44,98 @@ TEST (1)
QModelIndex inv2Index = model->index (0, 0, QModelIndex ());
EXPECT_EQ (model->hasChildren (inv2Index), true);
// 4 subnodes
EXPECT_EQ (model->rowCount (inv2Index), 3);
// 5 pins, 5 nets, 0 subcircuits, 4 devices
EXPECT_EQ (model->rowCount (inv2Index), 14);
QModelIndex sn_pins = model->index (0, 0, inv2Index);
QModelIndex sn_nets = model->index (1, 0, inv2Index);
QModelIndex sn_devices = model->index (2, 0, inv2Index);
// Pins
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::UserRole).toString ()), "IN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::DisplayRole).toString ()), "IN");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Index), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2Index), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, inv2Index), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, inv2Index), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "IN|NIN");
EXPECT_EQ (model->parent (model->index (0, 0, sn_pins)) == model->parent (model->index (0, 3, sn_pins)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "IN");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_pins), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, sn_pins), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, sn_pins), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, sn_pins), Qt::DisplayRole).toString ()), "$4");
// Nets
EXPECT_EQ (tl::to_string (model->data (model->index (5, 0, inv2Index), Qt::UserRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 0, inv2Index), Qt::DisplayRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 2, inv2Index), Qt::DisplayRole).toString ()), "NIN (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 0, inv2Index), Qt::DisplayRole).toString ()), "NOUT");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, inv2Index), Qt::DisplayRole).toString ()), "NOUT (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 0, inv2Index), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, inv2Index), Qt::DisplayRole).toString ()), "$2 (5)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 0, inv2Index), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, inv2Index), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 0, inv2Index), Qt::DisplayRole).toString ()), "$5");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, inv2Index), Qt::DisplayRole).toString ()), "$5 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "NIN");
EXPECT_EQ (model->parent (model->index (0, 0, sn_nets)) == model->parent (model->index (0, 3, sn_nets)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::DisplayRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "NIN (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_nets), Qt::DisplayRole).toString ()), "NOUT");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_nets), Qt::DisplayRole).toString ()), "NOUT (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, sn_nets), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, sn_nets), Qt::DisplayRole).toString ()), "$2 (5)");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, sn_nets), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, sn_nets), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, sn_nets), Qt::DisplayRole).toString ()), "$5");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, sn_nets), Qt::DisplayRole).toString ()), "$5 (3)");
// No Subcircuits
// Devices
EXPECT_EQ (tl::to_string (model->data (model->index (10, 0, inv2Index), Qt::UserRole).toString ()), "$1|PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 0, inv2Index), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 2, inv2Index), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 0, inv2Index), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 2, inv2Index), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, inv2Index), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 2, inv2Index), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (13, 0, inv2Index), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (13, 2, inv2Index), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::UserRole).toString ()), "$1|PMOS");
EXPECT_EQ (model->parent (model->index (0, 0, sn_devices)) == model->parent (model->index (0, 3, sn_devices)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_devices), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_devices), Qt::DisplayRole).toString ()), "PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_devices), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, sn_devices), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, sn_devices), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, sn_devices), Qt::DisplayRole).toString ()), "NMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, sn_devices), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (model->hasChildren (ringoIndex), true);
// 0 pins, 12 nets, 10 subcircuits, 0 devices
EXPECT_EQ (model->rowCount (ringoIndex), 22);
EXPECT_EQ (model->rowCount (ringoIndex), 2);
sn_nets = model->index (0, 0, ringoIndex);
QModelIndex sn_subcircuits = model->index (1, 0, ringoIndex);
// Pins
// Nets
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoIndex), Qt::UserRole).toString ()), "FB");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoIndex), Qt::DisplayRole).toString ()), "FB (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoIndex), Qt::DisplayRole).toString ()), "VDD (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoIndex), Qt::DisplayRole).toString ()), "VSS (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, ringoIndex), Qt::DisplayRole).toString ()), "$4 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, ringoIndex), Qt::DisplayRole).toString ()), "$5 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 2, ringoIndex), Qt::DisplayRole).toString ()), "$6 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, ringoIndex), Qt::DisplayRole).toString ()), "$7 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, ringoIndex), Qt::DisplayRole).toString ()), "$8 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, ringoIndex), Qt::DisplayRole).toString ()), "$9 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, ringoIndex), Qt::DisplayRole).toString ()), "$10 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 2, ringoIndex), Qt::DisplayRole).toString ()), "$11 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 2, ringoIndex), Qt::DisplayRole).toString ()), "$12 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "FB");
EXPECT_EQ (model->parent (model->index (0, 0, sn_nets)) == model->parent (model->index (0, 3, sn_nets)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "FB (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_nets), Qt::DisplayRole).toString ()), "VDD (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, sn_nets), Qt::DisplayRole).toString ()), "VSS (10)");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, sn_nets), Qt::DisplayRole).toString ()), "$4 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, sn_nets), Qt::DisplayRole).toString ()), "$5 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (5, 2, sn_nets), Qt::DisplayRole).toString ()), "$6 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, sn_nets), Qt::DisplayRole).toString ()), "$7 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, sn_nets), Qt::DisplayRole).toString ()), "$8 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, sn_nets), Qt::DisplayRole).toString ()), "$9 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, sn_nets), Qt::DisplayRole).toString ()), "$10 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (10, 2, sn_nets), Qt::DisplayRole).toString ()), "$11 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (11, 2, sn_nets), Qt::DisplayRole).toString ()), "$12 (2)");
// Subcircuits
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, ringoIndex), Qt::UserRole).toString ()), "INV2|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, ringoIndex), Qt::DisplayRole).toString ()), "<a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 2, ringoIndex), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (21, 0, ringoIndex), Qt::DisplayRole).toString ()), "<a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (21, 2, ringoIndex), Qt::DisplayRole).toString ()), "$10");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_subcircuits), Qt::UserRole).toString ()), "INV2|$1");
EXPECT_EQ (model->parent (model->index (0, 0, sn_subcircuits)) == model->parent (model->index (0, 3, sn_subcircuits)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_subcircuits), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_subcircuits), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 0, sn_subcircuits), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (9, 2, sn_subcircuits), Qt::DisplayRole).toString ()), "$10");
// Devices
// OUT pin of INV2 has a single child node which is the "NOUT" net
QModelIndex inv2PinOutIndex = model->index (2, 0, inv2Index);
EXPECT_EQ (model->parent (inv2PinOutIndex) == inv2Index, true);
// OUT pin of INV2 is identical with the "NOUT" net
QModelIndex inv2PinOutIndex = model->index (2, 0, model->index (0, 0, inv2Index));
EXPECT_EQ (model->parent (inv2PinOutIndex) == model->index (0, 0, inv2Index), true);
EXPECT_EQ (model->hasChildren (inv2PinOutIndex), true);
EXPECT_EQ (model->rowCount (inv2PinOutIndex), 1);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PinOutIndex), Qt::DisplayRole).toString ()), "NOUT");
QModelIndex inv2PinOutIndexNet = model->index (0, 0, inv2PinOutIndex);
EXPECT_EQ (model->parent (inv2PinOutIndexNet) == inv2PinOutIndex, true);
EXPECT_EQ (model->hasChildren (inv2PinOutIndexNet), false);
EXPECT_EQ (model->rowCount (inv2PinOutIndexNet), 0);
EXPECT_EQ (model->rowCount (inv2PinOutIndex), 3);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PinOutIndex), Qt::DisplayRole).toString ()), "D / PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
// NOUT net has 1 pin, 2 devices, 0 subcircuits
QModelIndex inv2NOutIndex = model->index (6, 0, inv2Index);
EXPECT_EQ (model->parent (inv2NOutIndex) == inv2Index, true);
QModelIndex inv2NOutIndex = model->index (1, 0, model->index (1, 0, inv2Index));
EXPECT_EQ (model->parent (inv2NOutIndex) == model->index (1, 0, inv2Index), true);
EXPECT_EQ (model->hasChildren (inv2NOutIndex), true);
EXPECT_EQ (model->rowCount (inv2NOutIndex), 3);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutIndex), Qt::UserRole).toString ()), "D|PMOS|$2");
EXPECT_EQ (model->parent (model->index (0, 0, inv2NOutIndex)) == model->parent (model->index (0, 3, inv2NOutIndex)), true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "D / PMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "<a href='int:device?id=24'>$2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "$2");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "D / NMOS [L=0.25, W=0.95, AS=0.26125, AD=0.49875, PS=1.5, PD=2.95]");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "<a href='int:device?id=56'>$4</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "<a href='int:pin?id=18'>OUT</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutIndex), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, inv2NOutIndex), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (model->parent (model->index (2, 0, inv2NOutIndex)) == model->parent (model->index (2, 3, inv2NOutIndex)), true);
// no children for pins on nets
QModelIndex inv2NOutPinOutIndex = model->index (2, 0, inv2NOutIndex);
@ -137,6 +145,9 @@ TEST (1)
// a MOS3 transistor has three other terminals
QModelIndex inv2NOutDeviceIndex = model->index (0, 0, inv2NOutIndex);
QModelIndex b = model->index (0, 0, inv2NOutIndex);
EXPECT_EQ (b.parent () == inv2NOutDeviceIndex.parent (), true);
EXPECT_EQ (b.model () == inv2NOutDeviceIndex.model (), true);
EXPECT_EQ (model->parent (inv2NOutDeviceIndex) == inv2NOutIndex, true);
EXPECT_EQ (model->hasChildren (inv2NOutDeviceIndex), true);
EXPECT_EQ (model->rowCount (inv2NOutDeviceIndex), 3);
@ -144,67 +155,76 @@ TEST (1)
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutDeviceIndex), Qt::UserRole).toString ()), "S|$5");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "S");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "G");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "D");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, inv2NOutDeviceIndex), Qt::DisplayRole).toString ()), "D (already seen)");
QModelIndex inv2NOutDeviceGateIndex = model->index (1, 0, inv2NOutDeviceIndex);
EXPECT_EQ (model->parent (inv2NOutDeviceGateIndex) == inv2NOutDeviceIndex, true);
EXPECT_EQ (model->hasChildren (inv2NOutDeviceGateIndex), false);
EXPECT_EQ (model->rowCount (inv2NOutDeviceGateIndex), 0);
EXPECT_EQ (model->hasChildren (inv2NOutDeviceGateIndex), true);
EXPECT_EQ (model->rowCount (inv2NOutDeviceGateIndex), 5);
// FB net has 0 pin, 0 devices, 2 subcircuits
QModelIndex ringoFbIndex = model->index (0, 0, ringoIndex);
EXPECT_EQ (model->parent (ringoFbIndex) == ringoIndex, true);
QModelIndex ringoFbIndex = model->index (0, 0, sn_nets);
EXPECT_EQ (model->parent (ringoFbIndex) == sn_nets, true);
EXPECT_EQ (model->hasChildren (ringoFbIndex), true);
EXPECT_EQ (model->rowCount (ringoFbIndex), 2);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbIndex), Qt::UserRole).toString ()), "IN|INV2|$2");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:pin?id=2'>IN</a> / <a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:subcircuit?id=23'>$2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:pin?id=34'>$1</a> / <a href='int:circuit?id=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:subcircuit?id=7'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "IN / <a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=1,1,1'>$2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoFbIndex), Qt::DisplayRole).toString ()), "$1 / <a href='int:netlist?path=0'>INV2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoFbIndex), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=1,1,0'>$1</a>");
QModelIndex ringoFbSubcircuit2Index = model->index (0, 0, ringoFbIndex);
EXPECT_EQ (model->parent (ringoFbSubcircuit2Index) == ringoFbIndex, true);
EXPECT_EQ (model->hasChildren (ringoFbSubcircuit2Index), true);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2Index), 5);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2Index), 1);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::UserRole).toString ()), "IN|NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=2'>IN</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=5'>FB</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=34'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=18'>OUT</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=53'>$4</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=50'>$3</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (3, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=37'>VSS</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=66'>$4</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (4, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=21'>VDD</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::UserRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, ringoFbSubcircuit2Index), Qt::DisplayRole).toString ()), "NIN (3)");
QModelIndex ringoFbSubcircuit2InPinIndex = model->index (1, 0, ringoFbSubcircuit2Index);
EXPECT_EQ (model->parent (ringoFbSubcircuit2InPinIndex) == ringoFbSubcircuit2Index, true);
EXPECT_EQ (model->hasChildren (ringoFbSubcircuit2InPinIndex), false);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2InPinIndex), 0);
QModelIndex ringoFbSubcircuit2InsideNetIndex = model->index (0, 0, ringoFbSubcircuit2Index);
EXPECT_EQ (model->parent (ringoFbSubcircuit2InsideNetIndex) == ringoFbSubcircuit2Index, true);
EXPECT_EQ (model->hasChildren (ringoFbSubcircuit2InsideNetIndex), true);
EXPECT_EQ (model->rowCount (ringoFbSubcircuit2InsideNetIndex), 3);
// Subcircuit 1 of RINGO has 5 pins
QModelIndex ringoSubcircuit1Index = model->index (12, 0, ringoIndex);
EXPECT_EQ (model->parent (ringoSubcircuit1Index) == ringoIndex, true);
QModelIndex ringoSubcircuit1Index = model->index (0, 0, sn_subcircuits);
EXPECT_EQ (model->parent (ringoSubcircuit1Index) == sn_subcircuits, true);
EXPECT_EQ (model->hasChildren (ringoSubcircuit1Index), true);
EXPECT_EQ (model->rowCount (ringoSubcircuit1Index), 5);
EXPECT_EQ (model->rowCount (ringoSubcircuit1Index), 2);
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1Index), Qt::UserRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=18'>OUT</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoSubcircuit1Index), Qt::DisplayRole).toString ()), "");
QModelIndex ringoSubcircuit1PinsIndex = model->index (0, 0, ringoSubcircuit1Index);
EXPECT_EQ (model->rowCount (ringoSubcircuit1PinsIndex), 5);
QModelIndex ringoSubcircuit1OutPinIndex = model->index (2, 0, ringoSubcircuit1Index);
EXPECT_EQ (model->parent (ringoSubcircuit1OutPinIndex) == ringoSubcircuit1Index, true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoSubcircuit1PinsIndex), Qt::UserRole).toString ()), "IN|$5");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoSubcircuit1PinsIndex), Qt::UserRole).toString ()), "FB");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1PinsIndex), Qt::UserRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1PinsIndex), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoSubcircuit1PinsIndex), Qt::DisplayRole).toString ()), "");
QModelIndex ringoSubcircuit1NodeIndex = model->index (1, 0, ringoSubcircuit1Index);
EXPECT_EQ (model->rowCount (ringoSubcircuit1NodeIndex), 3);
QModelIndex ringoSubcircuit1InsidePinsIndex = model->index (0, 0, ringoSubcircuit1NodeIndex);
EXPECT_EQ (model->rowCount (ringoSubcircuit1InsidePinsIndex), 5);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, ringoSubcircuit1InsidePinsIndex), Qt::UserRole).toString ()), "IN|NIN");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, ringoSubcircuit1InsidePinsIndex), Qt::UserRole).toString ()), "$1|$2");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1InsidePinsIndex), Qt::UserRole).toString ()), "OUT|NOUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 0, ringoSubcircuit1InsidePinsIndex), Qt::DisplayRole).toString ()), "OUT");
EXPECT_EQ (tl::to_string (model->data (model->index (2, 2, ringoSubcircuit1InsidePinsIndex), Qt::DisplayRole).toString ()), "NOUT (3)");
QModelIndex ringoSubcircuit1OutPinIndex = model->index (2, 0, ringoSubcircuit1PinsIndex);
EXPECT_EQ (model->parent (ringoSubcircuit1OutPinIndex) == ringoSubcircuit1PinsIndex, true);
EXPECT_EQ (model->hasChildren (ringoSubcircuit1OutPinIndex), false);
EXPECT_EQ (model->rowCount (ringoSubcircuit1OutPinIndex), 0);
// Device 1 of INV2 has 3 pins
// Device 1 of INV2 has 3 terminals
QModelIndex inv2Device1Index = model->index (10, 0, inv2Index);
EXPECT_EQ (model->parent (inv2Device1Index) == inv2Index, true);
QModelIndex inv2Device1Index = model->index (0, 0, sn_devices);
EXPECT_EQ (model->parent (inv2Device1Index) == sn_devices, true);
EXPECT_EQ (model->hasChildren (inv2Device1Index), true);
EXPECT_EQ (model->rowCount (inv2Device1Index), 3);
@ -213,8 +233,11 @@ TEST (1)
QModelIndex inv2Device1GateIndex = model->index (1, 0, inv2Device1Index);
EXPECT_EQ (model->parent (inv2Device1GateIndex) == inv2Device1Index, true);
EXPECT_EQ (model->hasChildren (inv2Device1GateIndex), false);
EXPECT_EQ (model->rowCount (inv2Device1GateIndex), 0);
EXPECT_EQ (model->hasChildren (inv2Device1GateIndex), true);
EXPECT_EQ (model->rowCount (inv2Device1GateIndex), 3);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Device1GateIndex), Qt::UserRole).toString ()), "G|PMOS|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Device1GateIndex), Qt::DisplayRole).toString ()), "G / PMOS [L=0.25, W=0.95, AS=0.49875, AD=0.26125, PS=2.95, PD=1.5] (already seen)");
}
TEST (2)
@ -228,6 +251,7 @@ TEST (2)
EXPECT_EQ (model->hasChildren (QModelIndex ()), true);
// two circuits
EXPECT_EQ (model->rowCount (QModelIndex ()), 4);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, QModelIndex ()), Qt::UserRole).toString ()), "INV2PAIRX");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, QModelIndex ()), Qt::DisplayRole).toString ()), "- ⇔ INV2PAIRX");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, QModelIndex ()), Qt::DisplayRole).toString ()), "");
@ -245,26 +269,33 @@ TEST (2)
// INV2 circuit node
EXPECT_EQ (model->hasChildren (inv2Index), true);
EXPECT_EQ (model->rowCount (inv2Index), 14);
EXPECT_EQ (model->rowCount (inv2Index), 3);
EXPECT_EQ (model->parent (inv2Index).isValid (), false);
QModelIndex sn_pins = model->index (0, 0, inv2Index);
QModelIndex sn_nets = model->index (1, 0, inv2Index);
QModelIndex sn_devices = model->index (2, 0, inv2Index);
EXPECT_EQ (model->rowCount (sn_pins), 6);
EXPECT_EQ (model->rowCount (sn_nets), 6);
EXPECT_EQ (model->rowCount (sn_devices), 2);
// first of pins in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::UserRole).toString ()), "$0|$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "$0|$0|$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_pins), Qt::DisplayRole).toString ()), "$1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_pins), Qt::DisplayRole).toString ()), "1 (2)");
// INV2, pin 0 node
QModelIndex inv2Pin0Index = model->index (0, 0, inv2Index);
QModelIndex inv2Pin0Index = model->index (0, 0, sn_pins);
EXPECT_EQ (model->hasChildren (inv2Pin0Index), true);
EXPECT_EQ (model->rowCount (inv2Pin0Index), 1);
EXPECT_EQ (model->parent (inv2Pin0Index) == inv2Index, true);
EXPECT_EQ (model->rowCount (inv2Pin0Index), 2);
EXPECT_EQ (model->parent (inv2Pin0Index) == sn_pins, true);
// INV2, pin 0 has one net node
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Pin0Index), Qt::UserRole).toString ()), "$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Pin0Index), Qt::DisplayRole).toString ()), "$1 ⇔ 1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Pin0Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=9'>$1</a>");
std::pair<const db::Net *, const db::Net *> nets = model->net_from_index (model->index_from_id ((void *) 9, 0));
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "$0|$0|$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_pins), Qt::DisplayRole).toString ()), "$1 (2)");
std::pair<const db::Net *, const db::Net *> nets = model->net_from_index (model->index (0, 0, sn_pins));
EXPECT_EQ (nets.first != 0, true);
if (nets.first != 0) {
EXPECT_EQ (nets.first->expanded_name (), "$1");
@ -273,25 +304,25 @@ TEST (2)
if (nets.second != 0) {
EXPECT_EQ (nets.second->expanded_name (), "1");
}
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Pin0Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=9'>1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_pins), Qt::DisplayRole).toString ()), "1 (2)");
// first of nets in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (6, 0, inv2Index), Qt::UserRole).toString ()), "$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 0, inv2Index), Qt::DisplayRole).toString ()), "$1 ⇔ 1");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 2, inv2Index), Qt::DisplayRole).toString ()), "$1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (6, 3, inv2Index), Qt::DisplayRole).toString ()), "1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "$1|1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::DisplayRole).toString ()), "$1 ⇔ 1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "$1 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_nets), Qt::DisplayRole).toString ()), "1 (2)");
// INV2, net 1 node
QModelIndex inv2Net0Index = model->index (6, 0, inv2Index);
QModelIndex inv2Net0Index = model->index (0, 0, sn_nets);
EXPECT_EQ (model->hasChildren (inv2Net0Index), true);
EXPECT_EQ (model->rowCount (inv2Net0Index), 2);
EXPECT_EQ (model->parent (inv2Net0Index) == inv2Index, true);
EXPECT_EQ (model->parent (inv2Net0Index) == sn_nets, true);
// INV2, net 1 has one pin and one terminal at BULK
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Net0Index), Qt::UserRole).toString ()), "B|B|PMOS|PMOS|$1|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2Net0Index), Qt::DisplayRole).toString ()), "B / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:device?id=17'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:device?id=17'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "$1");
// This terminal connects to a device with four other terminals ..
QModelIndex inv2Net0TerminalIndex = model->index (0, 0, inv2Net0Index);
@ -301,14 +332,14 @@ TEST (2)
// .. whose second terminal is gate
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::UserRole).toString ()), "G|G|IN|2");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "G");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "<a href='int:net?id=73'>IN</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "<a href='int:net?id=73'>2</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "IN (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0TerminalIndex), Qt::DisplayRole).toString ()), "2 (3)");
// The Pin
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0Index), Qt::UserRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2Net0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=5'>$0</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=5'>$0</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2Net0Index), Qt::DisplayRole).toString ()), "$0");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2Net0Index), Qt::DisplayRole).toString ()), "$0");
// This pin does not have children
QModelIndex inv2Net0PinIndex = model->index (1, 0, inv2Net0Index);
@ -317,59 +348,49 @@ TEST (2)
EXPECT_EQ (model->parent (inv2Net0PinIndex) == inv2Net0Index, true);
// second of nets in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (7, 0, inv2Index), Qt::UserRole).toString ()), "BULK|6");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 0, inv2Index), Qt::DisplayRole).toString ()), "BULK ⇔ 6");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 2, inv2Index), Qt::DisplayRole).toString ()), "BULK (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (7, 3, inv2Index), Qt::DisplayRole).toString ()), "6 (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_nets), Qt::UserRole).toString ()), "BULK|6");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, sn_nets), Qt::DisplayRole).toString ()), "BULK ⇔ 6");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, sn_nets), Qt::DisplayRole).toString ()), "BULK (2)");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, sn_nets), Qt::DisplayRole).toString ()), "6 (2)");
// first of devices in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, inv2Index), Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 0, inv2Index), Qt::DisplayRole).toString ()), "PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 2, inv2Index), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (12, 3, inv2Index), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_devices), Qt::DisplayRole).toString ()), "PMOS");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_devices), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_devices), Qt::DisplayRole).toString ()), "$1 / PMOS [L=0.25, W=3.5]");
QModelIndex inv2PairIndex = model->index (2, 0, QModelIndex ());
EXPECT_EQ (model->parent (inv2PairIndex).isValid (), false);
// INV2PAIR circuit node
EXPECT_EQ (model->hasChildren (inv2PairIndex), true);
EXPECT_EQ (model->rowCount (inv2PairIndex), 18);
EXPECT_EQ (model->rowCount (inv2PairIndex), 3);
sn_pins = model->index (0, 0, inv2PairIndex);
sn_nets = model->index (1, 0, inv2PairIndex);
// first of pins in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairIndex), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairIndex), Qt::DisplayRole).toString ()), "- ⇔ $4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairIndex), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairIndex), Qt::DisplayRole).toString ()), "$4");
// INV2, pin 0 node
QModelIndex inv2PairPin0Index = model->index (0, 0, inv2PairIndex);
EXPECT_EQ (model->hasChildren (inv2PairPin0Index), true);
EXPECT_EQ (model->rowCount (inv2PairPin0Index), 1);
EXPECT_EQ (model->parent (inv2PairPin0Index) == inv2PairIndex, true);
// INV2, pin 0 has one net node
// The pin isnt't connected to any net, left side because there is no match, right side because the pin isn't connected
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairPin0Index), Qt::UserRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairPin0Index), Qt::DisplayRole).toString ()), "-");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairPin0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairPin0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_pins), Qt::DisplayRole).toString ()), "- ⇔ $4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_pins), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_pins), Qt::DisplayRole).toString ()), "");
// first of nets in INV2 circuit
EXPECT_EQ (tl::to_string (model->data (model->index (8, 0, inv2PairIndex), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 0, inv2PairIndex), Qt::DisplayRole).toString ()), "$4 ⇔ -");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 2, inv2PairIndex), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (8, 3, inv2PairIndex), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::UserRole).toString ()), "$4");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, sn_nets), Qt::DisplayRole).toString ()), "$4 ⇔ -");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, sn_nets), Qt::DisplayRole).toString ()), "$4 (3)");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, sn_nets), Qt::DisplayRole).toString ()), "");
// This net has only left side which has one pin and two subcircuits
QModelIndex inv2PairNet0Index = model->index (8, 0, inv2PairIndex);
QModelIndex inv2PairNet0Index = model->index (0, 0, sn_nets);
EXPECT_EQ (model->hasChildren (inv2PairNet0Index), true);
EXPECT_EQ (model->rowCount (inv2PairNet0Index), 3);
EXPECT_EQ (model->parent (inv2PairNet0Index) == inv2PairIndex, true);
EXPECT_EQ (model->parent (inv2PairNet0Index) == sn_nets, true);
// The pin
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0Index), Qt::UserRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0Index), Qt::DisplayRole).toString ()), "");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=38'>$3</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "$3");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairNet0Index), Qt::DisplayRole).toString ()), "");
// This pin does not have children
@ -380,18 +401,179 @@ TEST (2)
// The first subcircuit
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairNet0Index), Qt::UserRole).toString ()), "OUT|INV2|$1");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=101'>OUT ⇔ -</a> / <a href='int:circuit?id=1'>INV2 ⇔ -</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:subcircuit?id=46'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 0, inv2PairNet0Index), Qt::DisplayRole).toString ()), "OUT ⇔ - / <a href='int:netlist?path=1'>INV2 ⇔ -</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 2, inv2PairNet0Index), Qt::DisplayRole).toString ()), "<a href='int:netlist?path=2,2,1'>$1</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (1, 3, inv2PairNet0Index), Qt::DisplayRole).toString ()), "");
// This subcircuit has 6 other pins
QModelIndex inv2PairNet0SubCircuit0Index = model->index (1, 0, inv2PairNet0Index);
EXPECT_EQ (model->hasChildren (inv2PairNet0SubCircuit0Index), true);
EXPECT_EQ (model->rowCount (inv2PairNet0SubCircuit0Index), 6);
EXPECT_EQ (model->parent (inv2PairNet0SubCircuit0Index) == inv2PairNet0Index, true);
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0SubCircuit0Index), Qt::UserRole).toString ()), "$1");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 0, inv2PairNet0SubCircuit0Index), Qt::DisplayRole).toString ()), "<a href='int:pin?id=5'>$0</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 2, inv2PairNet0SubCircuit0Index), Qt::DisplayRole).toString ()), "<a href='int:net?id=170'>$7</a>");
EXPECT_EQ (tl::to_string (model->data (model->index (0, 3, inv2PairNet0SubCircuit0Index), Qt::DisplayRole).toString ()), "");
}
TEST (3)
{
db::LayoutToNetlist l2n;
l2n.load (tl::testsrc () + "/testdata/lay/l2n_browser.l2n");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &l2n, &colorizer));
db::Circuit *root = l2n.netlist ()->circuit_by_name ("RINGO");
EXPECT_EQ (root != 0, true);
lay::NetlistObjectsPath path;
EXPECT_EQ (model->index_from_path (path).isValid (), false);
path.root.first = root;
db::Net *net = root->net_by_name ("FB");
EXPECT_EQ (net != 0, true);
path.net.first = net;
QModelIndex index = model->index_from_path (path);
EXPECT_EQ (index.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (index, Qt::UserRole).toString ()), "FB");
}
TEST (4)
{
db::LayoutToNetlist l2n;
l2n.load (tl::testsrc () + "/testdata/lay/l2n_browser.l2n");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &l2n, &colorizer));
db::Circuit *root = l2n.netlist ()->circuit_by_name ("RINGO");
EXPECT_EQ (root != 0, true);
lay::NetlistObjectsPath path;
path.root.first = root;
db::SubCircuit *sc1 = root->begin_subcircuits ().operator-> ();
EXPECT_EQ (sc1 != 0, true);
path.path.push_back (std::make_pair (sc1, (db::SubCircuit *) 0));
db::Net *net = sc1->circuit_ref ()->net_by_name ("NOUT");
EXPECT_EQ (net != 0, true);
path.net.first = net;
QModelIndex index = model->index_from_path (path);
EXPECT_EQ (index.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (index, Qt::UserRole).toString ()), "NOUT");
}
// Netlist object path: single vs. pairs - first
TEST (5)
{
db::LayoutVsSchematic lvs;
lvs.load (tl::testsrc () + "/testdata/lay/lvsdb_browser.lvsdb");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &lvs, &colorizer));
QModelIndex idx;
db::Circuit *root = lvs.netlist ()->circuit_by_name ("INV2PAIR");
EXPECT_EQ (root != 0, true);
db::Circuit *sc = lvs.netlist ()->circuit_by_name ("INV2");
EXPECT_EQ (sc != 0, true);
lay::NetlistObjectPath path;
EXPECT_EQ (path.is_null (), true);
path.root = root;
EXPECT_EQ (path.is_null (), false);
idx = model->index_from_path (path);
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "INV2PAIR|INV2PAIR");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.net = root->net_by_cluster_id (5);
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$5|4");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.path.push_back (root->subcircuit_by_id (1));
EXPECT_EQ (path.path.back () != 0, true);
EXPECT_EQ (path.path.back ()->expanded_name (), "$1");
EXPECT_EQ (path.path.back ()->circuit_ref ()->name (), "INV2");
path.net = 0;
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
// A pure subcircuit path addresses the "Circuit" representative node of the subcircuit
EXPECT_EQ (tl::to_string (model->data (idx, Qt::DisplayRole).toString ()), "Circuit");
EXPECT_EQ (tl::to_string (model->data (model->parent (idx), Qt::UserRole).toString ()), "INV2|$1");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.net = sc->net_by_cluster_id (2);
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "IN|2");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
path.net = 0;
path.device = sc->device_by_id (1);
idx = model->index_from_path (lay::NetlistObjectsPath::from_first (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (path == model->path_from_index (idx).first (), true);
}
// Netlist object path: single vs. pairs - second
TEST (6)
{
db::LayoutVsSchematic lvs;
lvs.load (tl::testsrc () + "/testdata/lay/lvsdb_browser.lvsdb");
lay::NetColorizer colorizer;
std::auto_ptr<lay::NetlistBrowserModel> model (new lay::NetlistBrowserModel (0, &lvs, &colorizer));
QModelIndex idx;
db::Circuit *root = lvs.reference_netlist ()->circuit_by_name ("INV2PAIR");
EXPECT_EQ (root != 0, true);
db::Circuit *sc = lvs.reference_netlist ()->circuit_by_name ("INV2");
EXPECT_EQ (sc != 0, true);
lay::NetlistObjectPath path;
EXPECT_EQ (path.is_null (), true);
path.root = root;
EXPECT_EQ (path.is_null (), false);
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "INV2PAIR|INV2PAIR");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.net = root->net_by_name ("4");
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$5|4");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.path.push_back (root->subcircuit_by_name ("$2"));
EXPECT_EQ (path.path.back () != 0, true);
EXPECT_EQ (path.path.back ()->expanded_name (), "$2");
EXPECT_EQ (path.path.back ()->circuit_ref ()->name (), "INV2");
path.net = 0;
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
// A pure subcircuit path addresses the "Circuit" representative node of the subcircuit
EXPECT_EQ (tl::to_string (model->data (idx, Qt::DisplayRole).toString ()), "Circuit");
EXPECT_EQ (tl::to_string (model->data (model->parent (idx), Qt::UserRole).toString ()), "INV2|$2");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.net = sc->net_by_name ("2");
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "IN|2");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
path.net = 0;
path.device = sc->device_by_id (1);
idx = model->index_from_path (lay::NetlistObjectsPath::from_second (path));
EXPECT_EQ (idx.isValid (), true);
EXPECT_EQ (tl::to_string (model->data (idx, Qt::UserRole).toString ()), "$1|$1|PMOS|PMOS");
EXPECT_EQ (path == model->path_from_index (idx).second (), true);
}

View File

@ -155,6 +155,12 @@ module LVS
# @synopsis max_depth(n)
# See \Netter#max_depth for a description of that function.
# %LVS%
# @name consider_net_names
# @brief Indicates whether the netlist comparer shall use net names
# @synopsis consider_net_names(f)
# See \Netter#consider_net_names for a description of that function.
# %LVS%
# @name tolerance
# @brief Specifies compare tolerances for certain device parameters
@ -162,7 +168,7 @@ module LVS
# @synopsis tolerance(device_class_name, parameter_name [, :absolute => absolute_tolerance] [, :relative => relative_tolerance])
# See \Netter#tolerance for a description of that function.
%w(schematic compare join_symmetric_nets tolerance align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f|
%w(schematic compare join_symmetric_nets tolerance align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity consider_net_names).each do |f|
eval <<"CODE"
def #{f}(*args)
_netter.#{f}(*args)

View File

@ -631,6 +631,20 @@ module LVS
@comparer_config << lambda { |comparer| comparer.max_branch_complexity = v }
end
# %LVS%
# @name consider_net_names
# @brief Indicates whether the netlist comparer shall use net names
# @synopsis consider_net_names(f)
# If this value is set to true (the default), the netlist comparer
# will employ net names to resolve ambiguities. If set to false,
# ambiguities will be resolved based on the topology alone. Topology
# resolution is more expensive.
def consider_net_names(value)
v = ! value
@comparer_config << lambda { |comparer| comparer.dont_consider_net_names = v }
end
end
end

View File

@ -251,6 +251,7 @@ void Macro::load_from (const std::string &fn)
}
m_modified = true;
m_is_file = true;
on_changed ();
}
@ -293,9 +294,6 @@ void Macro::load_from_string (const std::string &text, const std::string &url)
void Macro::load ()
{
load_from (path ());
m_modified = false;
m_is_file = true;
on_changed ();
}
bool
@ -449,13 +447,13 @@ void Macro::reset_modified ()
bool Macro::rename (const std::string &n)
{
if (m_is_file) {
if (m_is_file && parent ()) {
std::string suffix = suffix_for_format (m_interpreter, m_dsl_interpreter, m_format);
if (tl::verbosity () >= 20) {
tl::log << "Renaming macro " << path () << " to " << n;
}
QFile f (tl::to_qstring (path ()));
if (! f.rename (QFileInfo (QDir (tl::to_qstring (mp_parent->path ())), tl::to_qstring (n + suffix)).filePath ())) {
if (! f.rename (QFileInfo (QDir (tl::to_qstring (parent ()->path ())), tl::to_qstring (n + suffix)).filePath ())) {
return false;
}
}
@ -1294,14 +1292,15 @@ MacroCollection::add_folder (const std::string &description, const std::string &
begin_changes ();
MacroCollection *mc = m_folders.insert (std::make_pair (path, new MacroCollection ())).first->second;
mc->set_parent (this);
mc->set_name (path);
mc->set_description (description);
mc->set_category (cat);
mc->set_readonly (readonly);
mc->scan (path);
mc->set_parent (this);
on_changed ();
on_macro_changed (0);
return mc;
}
@ -1376,7 +1375,6 @@ void MacroCollection::scan (const std::string &path)
}
if (! found) {
Macro *m = m_macros.insert (std::make_pair (n, new Macro ()))->second;
m->set_parent (this);
m->set_interpreter (interpreter);
m->set_autorun_default (autorun);
m->set_autorun (autorun);
@ -1387,6 +1385,7 @@ void MacroCollection::scan (const std::string &path)
m->set_readonly (m_readonly);
m->reset_modified ();
m->set_is_file ();
m->set_parent (this);
}
}
@ -1425,6 +1424,7 @@ void MacroCollection::scan (const std::string &path)
try {
std::string n = tl::to_string (QFileInfo (*f).completeBaseName ());
std::string mp = tl::to_string (dir.absoluteFilePath (*f));
Macro::Format format = Macro::NoFormat;
Macro::Interpreter interpreter = Macro::None;
@ -1451,10 +1451,11 @@ void MacroCollection::scan (const std::string &path)
m->set_autorun (autorun);
m->set_interpreter (interpreter);
m->set_dsl_interpreter (dsl_name);
m->set_parent (this);
m->set_name (n);
m->load ();
m->load_from (mp);
m->reset_modified ();
m->set_readonly (m_readonly);
m->set_parent (this);
}
}
@ -1478,12 +1479,12 @@ void MacroCollection::scan (const std::string &path)
MacroCollection *&mc = m_folders.insert (std::make_pair (n, (MacroCollection *) 0)).first->second;
if (! mc) {
mc = new MacroCollection ();
mc->set_parent (this);
mc->set_name (n);
mc->set_virtual_mode (NotVirtual);
bool ro = (m_readonly || ! QFileInfo (dir.filePath (*f)).isWritable ());
mc->set_readonly (ro);
mc->scan (tl::to_string (dir.filePath (*f)));
mc->set_parent (this);
}
} catch (tl::Exception &ex) {

View File

@ -354,120 +354,130 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal
bool is_reg = instance.is_regular_array (a, b, amax, bmax);
db::Trans t = instance.front ();
for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) {
if (normalize) {
db::Trans t = *ii;
// try to normalize orthogonal arrays into "Cadence notation", that is
// column and row vectors are positive in the coordinate system of the
// rotated array.
if (is_reg) {
if (normalize) {
if (amax < 2) {
a = db::Vector ();
}
if (bmax < 2) {
b = db::Vector ();
}
// try to normalize orthogonal arrays into "Cadence notation", that is
// column and row vectors are positive in the coordinate system of the
// rotated array.
// normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel
if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) {
db::FTrans fp = db::FTrans(t.rot ()).inverted ();
a.transform (fp);
b.transform (fp);
if (is_reg) {
db::Vector p;
for (int i = 0; i < 2; ++i) {
if (amax < 2) {
a = db::Vector ();
}
if (bmax < 2) {
b = db::Vector ();
}
db::Vector *q = (i == 0) ? &a : &b;
unsigned long n = (i == 0) ? amax : bmax;
// normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel
if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) {
db::FTrans fp = db::FTrans(t.rot ()).inverted ();
a.transform (fp);
b.transform (fp);
db::Vector p;
for (int i = 0; i < 2; ++i) {
db::Vector *q = (i == 0) ? &a : &b;
unsigned long n = (i == 0) ? amax : bmax;
if (n == 0) {
*q = db::Vector ();
} else {
if (q->x () < 0) {
p += db::Vector ((n - 1) * q->x (), 0);
q->set_x (-q->x ());
}
if (q->y () < 0) {
p += db::Vector (0, (n - 1) * q->y ());
q->set_y (-q->y ());
}
}
if (n == 0) {
*q = db::Vector ();
} else {
if (q->x () < 0) {
p += db::Vector ((n - 1) * q->x (), 0);
q->set_x (-q->x ());
}
if (q->y () < 0) {
p += db::Vector (0, (n - 1) * q->y ());
q->set_y (-q->y ());
}
}
if (a.x () != 0 || b.y () != 0) {
std::swap (a, b);
std::swap (amax, bmax);
}
fp = db::FTrans (t.rot ());
a.transform (fp);
b.transform (fp);
t = t * db::Trans (p);
}
if (a.x () != 0 || b.y () != 0) {
std::swap (a, b);
std::swap (amax, bmax);
}
fp = db::FTrans (t.rot ());
a.transform (fp);
b.transform (fp);
t = t * db::Trans (p);
}
}
}
write_record_size (4);
write_record (is_reg ? sAREF : sSREF);
write_record_size (4);
write_record (is_reg ? sAREF : sSREF);
write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ()));
write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ()));
if (t.rot () != 0 || instance.is_complex ()) {
if (t.rot () != 0 || instance.is_complex ()) {
write_record_size (6);
write_record (sSTRANS);
write_short (t.is_mirror () ? 0x8000 : 0);
write_record_size (6);
write_record (sSTRANS);
write_short (t.is_mirror () ? 0x8000 : 0);
if (instance.is_complex ()) {
write_record_size (4 + 8);
write_record (sMAG);
write_double (instance.complex_trans ().mag ());
write_record_size (4 + 8);
write_record (sANGLE);
write_double (instance.complex_trans ().angle ());
} else {
if ((t.rot () % 4) != 0) {
if (instance.is_complex ()) {
db::CellInstArray::complex_trans_type ct = instance.complex_trans (t);
write_record_size (4 + 8);
write_record (sMAG);
write_double (ct.mag ());
write_record_size (4 + 8);
write_record (sANGLE);
write_double ((t.rot () % 4) * 90.0);
write_double (ct.angle ());
} else {
if ((t.rot () % 4) != 0) {
write_record_size (4 + 8);
write_record (sANGLE);
write_double ((t.rot () % 4) * 90.0);
}
}
}
if (is_reg) {
write_record_size (4 + 2 * 2);
write_record (sCOLROW);
if (amax > 32767 || bmax > 32767) {
throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams")));
}
write_short (std::max ((unsigned long) 1, bmax));
write_short (std::max ((unsigned long) 1, amax));
}
write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4);
write_record (sXY);
write_int (scale (sf, t.disp ().x ()));
write_int (scale (sf, t.disp ().y ()));
if (is_reg) {
write_int (scale (sf, t.disp ().x () + b.x () * bmax));
write_int (scale (sf, t.disp ().y () + b.y () * bmax));
write_int (scale (sf, t.disp ().x () + a.x () * amax));
write_int (scale (sf, t.disp ().y () + a.y () * amax));
}
finish (layout, prop_id);
if (is_reg) {
// we have already written all instances
break;
}
}
if (is_reg) {
write_record_size (4 + 2 * 2);
write_record (sCOLROW);
if (amax > 32767 || bmax > 32767) {
throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams")));
}
write_short (std::max ((unsigned long) 1, bmax));
write_short (std::max ((unsigned long) 1, amax));
}
write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4);
write_record (sXY);
write_int (scale (sf, t.disp ().x ()));
write_int (scale (sf, t.disp ().y ()));
if (is_reg) {
write_int (scale (sf, t.disp ().x () + b.x () * bmax));
write_int (scale (sf, t.disp ().y () + b.y () * bmax));
write_int (scale (sf, t.disp ().x () + a.x () * amax));
write_int (scale (sf, t.disp ().y () + a.y () * amax));
}
finish (layout, prop_id);
}
void

View File

@ -1129,6 +1129,40 @@ TEST(117)
EXPECT_EQ (pp == poly, true);
}
// error on duplicate cell name
TEST(118)
{
db::Manager m (false);
db::Layout layout_org (&m);
db::cell_index_type cid1 = layout_org.add_cell ("A");
db::cell_index_type cid2 = layout_org.add_cell ("B");
layout_org.rename_cell (cid2, "A"); // creates a duplicate cell
db::LayerProperties lp;
lp.layer = 1;
lp.datatype = 0;
unsigned int lid = layout_org.insert_layer (lp);
layout_org.cell (cid1).shapes (lid).insert (db::Box (0, 0, 1000, 2000));
layout_org.cell (cid2).shapes (lid).insert (db::Box (0, 0, 1000, 2000));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_117.gds");
bool error = false;
try {
tl::OutputStream stream (tmp_file);
db::SaveLayoutOptions options;
db::Writer writer (options);
writer.write (layout_org, stream);
} catch (tl::Exception &ex) {
tl::warn << ex.msg ();
error = true;
}
EXPECT_EQ (error, true);
}
// Extreme fracturing by max. points
TEST(120)
{
@ -1153,3 +1187,4 @@ TEST(166)
opt.max_vertex_count = 4;
run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt);
}

View File

@ -2016,6 +2016,8 @@ OASISReader::do_read_placement (unsigned char r,
db::Vector pos (mm_placement_x.get (), mm_placement_y.get ());
const std::vector<db::Vector> *points = 0;
if ((m & 0x8) && read_repetition ()) {
std::pair<bool, db::properties_id_type> pp = read_element_properties (layout.properties_repository (), false);
@ -2040,6 +2042,42 @@ OASISReader::do_read_placement (unsigned char r,
instances.push_back (inst);
}
} else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) {
db::CellInstArray inst;
if (mag_set || angle < 0) {
db::ICplxTrans ct (mag, angle_deg, mirror, pos);
db::CellInstArray::iterated_complex_array_type array (ct.rcos (), ct.mag ());
array.reserve (points->size () + 1);
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()),
db::Trans (ct), layout.array_repository ().insert (array));
} else {
db::CellInstArray::iterated_array_type array;
array.reserve (points->size () + 1);
array.insert (db::Vector ());
array.insert (points->begin (), points->end ());
array.sort ();
inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()),
db::Trans (angle, mirror, pos), layout.array_repository ().insert (array));
}
if (pp.first) {
instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second));
} else {
instances.push_back (inst);
}
} else {
RepetitionIterator p = mm_repetition.get ().begin ();

View File

@ -1965,11 +1965,35 @@ OASISWriter::write (const db::CellInstArray &inst, db::properties_id_type prop_i
{
m_progress.set (mp_stream->pos ());
std::vector<db::Vector> pts;
db::Vector a, b;
unsigned long amax, bmax;
bool is_reg = inst.is_regular_array (a, b, amax, bmax);
if (is_reg && (amax > 1 || bmax > 1)) {
if (inst.is_iterated_array (&pts) && pts.size () > 1) {
// Remove the first point which is implicitly contained in the repetition
// Note: we can do so because below we instantiate the shape at the front of the array which includes
// the first transformation already.
db::Vector po = pts.front ();
std::vector<db::Vector>::iterator pw = pts.begin();
for (std::vector<db::Vector>::iterator p = pw + 1; p != pts.end (); ++p) {
*pw++ = *p - po;
}
pts.erase (pw, pts.end ());
db::IrregularRepetition *rep_base = new db::IrregularRepetition ();
rep_base->points ().swap (pts);
db::Repetition array_rep (rep_base);
if (rep != db::Repetition ()) {
for (db::RepetitionIterator r = rep.begin (); ! r.at_end (); ++r) {
write_inst_with_rep (inst, prop_id, *r, array_rep);
}
} else {
write_inst_with_rep (inst, prop_id, db::Vector (), array_rep);
}
} else if (inst.is_regular_array (a, b, amax, bmax) && (amax > 1 || bmax > 1)) {
// we cannot use the repetition - instead we write every single instance and use the repetition
// for the array information

View File

@ -21,8 +21,8 @@
*/
#ifndef HDR_layDialogs
#define HDR_layDialogs
#ifndef HDR_layBooleanOperationsDialogs
#define HDR_layBooleanOperationsDialogs
#include "ui_BooleanOptionsDialog.h"
#include "ui_SizingOptionsDialog.h"

View File

@ -224,6 +224,10 @@ RdbDifferenceReceiver::produce_cell_inst (const db::CellInstArrayWithProperties
unsigned long amax, bmax;
if (ci.is_regular_array (a, b, amax, bmax)) {
r += tl::sprintf (" [a=%s, b=%s, na=%ld, nb=%ld]", a.to_string (), b.to_string (), amax, bmax);
} else if (ci.size () > 1) {
r += " (+";
r += tl::to_string (ci.size () - 1);
r += " irregular placements)";
}
item->add_value (r);

View File

@ -935,6 +935,12 @@ struct test_arg_func<gsi::VectorType>
{
void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose)
{
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) {
// for ptr or cptr, null is an allowed value
*ret = true;
return;
}
if (! PyTuple_Check (arg) && ! PyList_Check (arg)) {
*ret = false;
return;
@ -971,6 +977,12 @@ struct test_arg_func<gsi::MapType>
{
void operator () (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose)
{
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) {
// for ptr or cptr, null is an allowed value
*ret = true;
return;
}
if (! PyDict_Check (arg)) {
*ret = false;
return;
@ -999,6 +1011,7 @@ struct test_arg_func<gsi::MapType>
}
}
};
template <>
struct test_arg_func<gsi::ObjectType>
{

View File

@ -306,7 +306,11 @@ PYAObjectBase::detach ()
}
}
detach_callbacks ();
// NOTE: m_owned = false might mean the C++ object is already destroyed. We must not
// modify in this case and without is_managed() there is no way of knowing the state.
if (m_owned) {
detach_callbacks ();
}
m_obj = 0;
m_const_ref = false;

View File

@ -138,7 +138,10 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs
std::vector<PythonRef> callables;
callables.reserve (m_cbfuncs.size ());
for (std::vector<CallbackFunction>::const_iterator c = m_cbfuncs.begin (); c != m_cbfuncs.end (); ++c) {
callables.push_back (c->callable ());
PythonRef callable = c->callable ();
if (callable) {
callables.push_back (c->callable ());
}
}
PythonRef result;

View File

@ -402,7 +402,11 @@ Proxy::detach ()
}
}
clear_callbacks ();
// NOTE: m_owned = false might mean the C++ object is already destroyed. We must not
// modify in this case and without is_managed() there is no way of knowing the state.
if (m_owned) {
clear_callbacks ();
}
m_self = Qnil;
m_obj = 0;

View File

@ -956,7 +956,7 @@ struct test_arg_func<gsi::VectorType>
if ((atype.is_cptr () || atype.is_ptr ()) && arg == Qnil) {
// for pointers to vectors, nil is a valid value
*ret = true;
} if (TYPE (arg) != T_ARRAY) {
} else if (TYPE (arg) != T_ARRAY) {
*ret = false;
} else {

View File

@ -1019,7 +1019,7 @@ InputPipe::read (char *b, size_t n)
size_t ret = fread (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePReadErrorException (m_source, ferror (m_file));
throw FilePReadErrorException (m_source, errno);
}
}
@ -1049,7 +1049,7 @@ OutputPipe::OutputPipe (const std::string &path)
OutputPipe::~OutputPipe ()
{
if (m_file != NULL) {
fclose (m_file);
_pclose (m_file);
m_file = NULL;
}
}
@ -1061,7 +1061,7 @@ OutputPipe::write (const char *b, size_t n)
size_t ret = fwrite (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePWriteErrorException (m_source, ferror (m_file));
throw FilePWriteErrorException (m_source, errno);
}
}
}
@ -1106,10 +1106,22 @@ size_t
InputPipe::read (char *b, size_t n)
{
tl_assert (m_file != NULL);
size_t ret = fread (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePReadErrorException (m_source, ferror (m_file));
bool retry = true;
size_t ret = 0;
while (retry) {
retry = false;
ret = fread (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
if (errno != EINTR) {
throw FilePReadErrorException (m_source, errno);
} else if (ret == 0) {
retry = true;
clearerr (m_file);
}
}
}
}
@ -1138,7 +1150,7 @@ OutputPipe::OutputPipe (const std::string &path)
OutputPipe::~OutputPipe ()
{
if (m_file != NULL) {
fclose (m_file);
pclose (m_file);
m_file = NULL;
}
}
@ -1147,10 +1159,11 @@ void
OutputPipe::write (const char *b, size_t n)
{
tl_assert (m_file != NULL);
size_t ret = fwrite (b, 1, n, m_file);
if (ret < n) {
if (ferror (m_file)) {
throw FilePWriteErrorException (m_source, ferror (m_file));
if (ferror (m_file) && errno != EINTR) {
throw FilePReadErrorException (m_source, errno);
}
}
}

View File

@ -232,6 +232,7 @@ bool TestBase::do_test (bool editable, bool slow)
{
m_editable = editable;
m_slow = slow;
m_any_failed = false;
// Ensures the test temp directory is present
std::string tmpdir = tl::combine_path (tl::absolute_file_path (testtmp ()), m_testdir);

Some files were not shown because too many files have changed in this diff Show More