Merge branch 'master' into devel

This commit is contained in:
Matthias Koefferlein 2025-07-19 21:27:52 +02:00
commit 1a9c12ce18
85 changed files with 1562 additions and 621 deletions

View File

@ -1,12 +1,12 @@
Relevant KLayout version: 0.29.11<br> Relevant KLayout version: 0.30.2<br>
Author: Kazzz-S<br> Author: Kazzz-S<br>
Last modified: 2025-01-19<br> Last modified: 2025-05-30<br>
# 1. Introduction # 1. Introduction
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.29.11 or later for different 64-bit macOS, including: This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.30.2 or later for different 64-bit macOS, including:
* Sonoma (14.x) : the primary development environment * Sequoia (15.x) : the primary development environment
* Ventura (13.x) : experimental * Sonoma (14.x) : experimental
* Sequoia (15.x) : -- ditto -- * Ventura (13.x) : -- ditto --
Building KLayout for the previous operating systems listed below has been discontinued.<br> Building KLayout for the previous operating systems listed below has been discontinued.<br>
Pre-built DMG packages are also not provided.<br> Pre-built DMG packages are also not provided.<br>
@ -18,7 +18,7 @@ Pre-built DMG packages are also not provided.<br>
* Sierra (10.12) * Sierra (10.12)
* El Capitan (10.11) * El Capitan (10.11)
Throughout this document, the primary target machine is **Intel x86_64** with **macOS Sonoma**.<br> Throughout this document, the primary target machine is **Intel x86_64** with **macOS Sequoia**.<br>
All Apple (M1|M2|M3|M4) chips are still untested, as the author does not own an (M1|M2|M3|M4) Mac.<br> All Apple (M1|M2|M3|M4) chips are still untested, as the author does not own an (M1|M2|M3|M4) Mac.<br>
However, some kind volunteers told me they successfully built on an Apple silicon machine.<br> However, some kind volunteers told me they successfully built on an Apple silicon machine.<br>
@ -43,7 +43,7 @@ If you have installed Anaconda3 under $HOME/opt/anaconda3/, make a symbolic link
/Applications/anaconda3/ ---> $HOME/opt/anaconda3/ /Applications/anaconda3/ ---> $HOME/opt/anaconda3/
``` ```
The migration work to "Qt6" is ongoing. You can try to use it; however, you might encounter some build and runtime errors.<br> The migration work to "Qt6" is ongoing. You can try to use it; however, you might encounter some build or runtime errors.<br>
If you use **Homebrew** to build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br> If you use **Homebrew** to build KLayout >= 0.29.0, you need "Qt6" to address [the compilation issue](https://github.com/KLayout/klayout/issues/1599).<br>
I have also tried migrating to "Python 3.12.x" (earlier, Python 3.11.x) in this version. I have also tried migrating to "Python 3.12.x" (earlier, Python 3.11.x) in this version.
@ -70,7 +70,7 @@ The operating system type is detected automatically.
``` ```
----------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------
<< Usage of 'build4mac.py' >> << Usage of 'build4mac.py' >>
for building KLayout 0.29.11 or later on different Apple macOS platforms. for building KLayout 0.30.2 or later on different Apple macOS platforms.
$ [python] ./build4mac.py $ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value
@ -123,7 +123,7 @@ $ [python] ./build4mac.py
``` ```
# 6. Use-cases # 6. Use-cases
In this section, the actual file and directory names are those obtained on macOS Sonoma.<br> In this section, the actual file and directory names are those obtained on macOS Sequoia.<br>
On different OS, those names differ accordingly. On different OS, those names differ accordingly.
### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts Qt5 ### 6A. Standard build using the OS-bundled Ruby and Python with MacPorts Qt5
@ -141,7 +141,7 @@ Confirm that you have:
``` ```
As of this writing, the provided Python version is `3.9.6`. As of this writing, the provided Python version is `3.9.6`.
1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Sonoma, Ventura, and Sequioa. 1. Invoke **`build4mac.py`** with the following options: **((Notes))** These options are the default values for Sequoia, Sonoma, and Ventura.
``` ```
$ cd /where/'build.sh'/exists $ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r sys -p sys $ ./build4mac.py -q qt5macports -r sys -p sys
@ -154,7 +154,7 @@ $ ./build4mac.py -q qt5macports -r sys -p sys
$ ./build4mac.py -q qt5macports -r sys -p sys -y $ ./build4mac.py -q qt5macports -r sys -p sys -y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`ST-qt5MP.pkg.macos-Sonoma-release-RsysPsys`** directory, where **`ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys`** directory, where
* "ST-" means this is a standard package. * "ST-" means this is a standard package.
* "qt5MP" means that Qt5 from MacPorts is used. * "qt5MP" means that Qt5 from MacPorts is used.
* "RsysPsys" means that Ruby is 2.6 provided by OS; Python is 3.9 provided by OS. * "RsysPsys" means that Ruby is 2.6 provided by OS; Python is 3.9 provided by OS.
@ -185,7 +185,7 @@ $ ./build4mac.py -q qt5macports -r mp33 -p mp312
$ ./build4mac.py -q qt5macports -r mp33 -p mp312 -Y $ ./build4mac.py -q qt5macports -r mp33 -p mp312 -Y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5MP.pkg.macos-Sonoma-release-Rmp33Pmp312`** directory, where **`LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312`** directory, where
* "LW-" means this is a lightweight package. * "LW-" means this is a lightweight package.
* "qt5MP" means that Qt5 from MacPorts is used. * "qt5MP" means that Qt5 from MacPorts is used.
* "Rmp33Pmp312" means that Ruby is 3.3 from MacPorts; Python is 3.12 from MacPorts. * "Rmp33Pmp312" means that Ruby is 3.3 from MacPorts; Python is 3.12 from MacPorts.
@ -218,7 +218,7 @@ $ ./build4mac.py -q qt6brew -r hb34 -p hb312
$ ./build4mac.py -q qt6brew -r hb34 -p hb312 -Y $ ./build4mac.py -q qt6brew -r hb34 -p hb312 -Y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`LW-qt6Brew.pkg.macos-Sonoma-release-Rhb34Phb312`** directory, where **`LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312`** directory, where
* "LW-" means this is a lightweight package. * "LW-" means this is a lightweight package.
* "qt6Brew" means that Qt6 from Homebrew is used. * "qt6Brew" means that Qt6 from Homebrew is used.
* "Rhb34Phb312" means that Ruby is 3.4 from Homebrew; Python is 3.12 from Homebrew. * "Rhb34Phb312" means that Ruby is 3.4 from Homebrew; Python is 3.12 from Homebrew.
@ -258,7 +258,7 @@ $ ./build4mac.py -q qt6brew -r sys -p hb311
$ ./build4mac.py -q qt6brew -r sys -p hb311 -y $ ./build4mac.py -q qt6brew -r sys -p hb311 -y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`HW-qt6Brew.pkg.macos-Sonoma-release-RsysPhb311`** directory, where **`HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311`** directory, where
* "HW-" means this is a heavyweight package because both Qt6 and Python Frameworks are deployed. * "HW-" means this is a heavyweight package because both Qt6 and Python Frameworks are deployed.
* "qt6Brew" means that Qt6 from Homebrew is used. * "qt6Brew" means that Qt6 from Homebrew is used.
* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew. * "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew.
@ -300,14 +300,14 @@ $ ./build4mac.py -q qt5macports -r sys -p hb311
$ ./build4mac.py -q qt5macports -r sys -p hb311 -y $ ./build4mac.py -q qt5macports -r sys -p hb311 -y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`HW-qt5MP.pkg.macos-Sonoma-release-RsysPhb311`** directory, where **`HW-qt5MP.pkg.macos-Sequoia-release-RsysPhb311`** directory, where
* "HW-" means this is a heavyweight package because both Qt5 and Python Frameworks are deployed. * "HW-" means this is a heavyweight package because both Qt5 and Python Frameworks are deployed.
* "qt5MP" means that Qt5 from MacPorts is used. * "qt5MP" means that Qt5 from MacPorts is used.
* "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew. * "RsysPhb311" means that Ruby is OS-bundled; Python is 3.11 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation. 4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6F. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.2 and Anaconda3 Python 3.12 ### 6F. Fully Anaconda3-flavored build with Anaconda3 Ruby 3.2 and Anaconda3 Python 3.12
0. Install Anaconda3 (Anaconda3-2024.06-1-MacOSX-x86_64.pkg), then install Ruby 3.2 and libgit2 by 0. Install Anaconda3 (Anaconda3-2024.10-1-MacOSX-x86_64.pkg), then install Ruby 3.2 and libgit2 by
``` ```
$ conda install ruby=3.2.2 $ conda install ruby=3.2.2
$ conda install libgit2=1.6.4 $ conda install libgit2=1.6.4
@ -327,7 +327,7 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y
``` ```
The application bundle **`klayout.app`** is located under:<br> The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5Ana3.pkg.macos-Sonoma-release-Rana3Pana3`** directory, where **`LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3`** directory, where
* "LW-" means this is a lightweight package. * "LW-" means this is a lightweight package.
* "qt5Ana3" means that Qt5 from Anaconda3 is used. * "qt5Ana3" means that Qt5 from Anaconda3 is used.
* "Rana3Pana3" means that Ruby (3.2) is from Anaconda3; Python (3.12) is from Anaconda3. * "Rana3Pana3" means that Ruby (3.2) is from Anaconda3; Python (3.12) is from Anaconda3.
@ -394,11 +394,11 @@ makeDMG4mac.py -> macbuild/makeDMG4mac.py
2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example, 2. Invoke **`makeDMG4mac.py`** with -p and -m options, for example,
``` ```
$ cd /where/'build.sh'/exists $ cd /where/'build.sh'/exists
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Sonoma-release-Rmp33Pmp312 -m $ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312 -m
``` ```
This command will generate the two files below:<br> This command will generate the two files below:<br>
* **`LW-klayout-0.29.11-macOS-Sonoma-1-qt5MP-Rmp33Pmp312.dmg`** ---(1) the main DMG file * **`LW-klayout-0.30.2-macOS-Sequoia-1-qt5MP-Rmp33Pmp312.dmg`** ---(1) the main DMG file
* **`LW-klayout-0.29.11-macOS-Sonoma-1-qt5MP-Rmp33Pmp312.dmg.md5`** ---(2) MD5-value text file * **`LW-klayout-0.30.2-macOS-Sequoia-1-qt5MP-Rmp33Pmp312.dmg.md5`** ---(2) MD5-value text file
# Known issues # Known issues
Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.<br> Because we assume some specific versions of non-OS-standard Ruby and Python, updating Homebrew, MacPorts, or Anaconda3 may cause build- and link errors.<br>

View File

@ -5,7 +5,7 @@
# File: "macbuild/build4mac.py" # File: "macbuild/build4mac.py"
# #
# The top Python script for building KLayout (http://www.klayout.de/index.php) # The top Python script for building KLayout (http://www.klayout.de/index.php)
# version 0.29.11 or later on different Apple Mac OSX platforms. # version 0.30.2 or later on different Apple Mac OSX platforms.
#=============================================================================== #===============================================================================
import sys import sys
import os import os
@ -45,7 +45,7 @@ def GenerateUsage(platform):
usage = "\n" usage = "\n"
usage += "-----------------------------------------------------------------------------------------------------------\n" usage += "-----------------------------------------------------------------------------------------------------------\n"
usage += "<< Usage of 'build4mac.py' >>\n" usage += "<< Usage of 'build4mac.py' >>\n"
usage += " for building KLayout 0.29.11 or later on different Apple macOS platforms.\n" usage += " for building KLayout 0.30.2 or later on different Apple macOS platforms.\n"
usage += "\n" usage += "\n"
usage += "$ [python] ./build4mac.py\n" usage += "$ [python] ./build4mac.py\n"
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value\n" usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value\n"
@ -847,29 +847,30 @@ def Build_pymod_wheel(parameters):
cmd3_args = " <wheel file> \\\n" cmd3_args = " <wheel file> \\\n"
cmd4_args = " -m setup clean --all \\\n" cmd4_args = " -m setup clean --all \\\n"
#-------------------------------------------------------------------- #-----------------------------------------------------------------------------------
# [4] Make the consolidated command lines # [4] Make the consolidated command lines
#-------------------------------------------------------------------- # "caffeinate" makes the CPU run at full speed even when the screen is locked.
#-----------------------------------------------------------------------------------
command1 = "time" command1 = "time"
command1 += " \\\n %s \\\n" % parameters['python'] command1 += " \\\n caffeinate -i %s \\\n" % parameters['python']
command1 += cmd1_args command1 += cmd1_args
command1 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile'] command1 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command1 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command1 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command2 = "time" command2 = "time"
command2 += " \\\n %s \\\n" % parameters['python'] command2 += " \\\n caffeinate -i %s \\\n" % parameters['python']
command2 += cmd2_args command2 += cmd2_args
command2 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile'] command2 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command2 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command2 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command3 = "time" command3 = "time"
command3 += " \\\n %s \\\n" % deloc_cmd command3 += " \\\n caffeinate -i %s \\\n" % deloc_cmd
command3 += cmd3_args command3 += cmd3_args
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile'] command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command4 = "time" command4 = "time"
command4 += " \\\n %s \\\n" % parameters['python'] command4 += " \\\n caffeinate -i %s \\\n" % parameters['python']
command4 += cmd4_args command4 += cmd4_args
command4 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile'] command4 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command4 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command4 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
@ -923,10 +924,10 @@ def Build_pymod_wheel(parameters):
# Refer to: https://github.com/Kazzz-S/klayout/issues/49#issuecomment-1432154118 # Refer to: https://github.com/Kazzz-S/klayout/issues/49#issuecomment-1432154118
# https://pypi.org/project/delocate/ # https://pypi.org/project/delocate/
#--------------------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------------
cmd3_args = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.29.7-cp312-cp312-macosx_12_0_x86_64.whl'] cmd3_args = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.30.2-cp312-cp312-macosx_10_15_x86_64.whl']
if len(cmd3_args) == 1: if len(cmd3_args) == 1:
command3 = "time" command3 = "time"
command3 += " \\\n %s \\\n" % deloc_cmd command3 += " \\\n caffeinate -i %s \\\n" % deloc_cmd
command3 += " %s \\\n" % cmd3_args[0] command3 += " %s \\\n" % cmd3_args[0]
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile'] command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
@ -953,13 +954,13 @@ def Build_pymod_wheel(parameters):
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# [5-C] Forcibly change the wheel file name for anaconda3 # [5-C] Forcibly change the wheel file name for anaconda3
# Ref. https://github.com/Kazzz-S/klayout/issues/53 # Ref. https://github.com/Kazzz-S/klayout/issues/53
# original: klayout-0.29.7-cp312-cp312-macosx_12_0_x86_64.whl # original: klayout-0.30.2-cp312-cp312-macosx_10_15_x86_64.whl
# | # |
# V # V
# new: klayout-0.29.7-cp312-cp312-macosx_10_9_x86_64.whl # new: klayout-0.30.2-cp312-cp312-macosx_10_9_x86_64.whl
#------------------------------------------------------------------------ #------------------------------------------------------------------------
if whlTarget == "ana3": if whlTarget == "ana3":
wheels = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.29.7-cp312-cp312-macosx_12_0_x86_64.whl'] wheels = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.30.2-cp312-cp312-macosx_10_15_x86_64.whl']
if not len(wheels) == 1: if not len(wheels) == 1:
print( "", file=sys.stderr ) print( "", file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr ) print( "-------------------------------------------------------------", file=sys.stderr )
@ -1114,11 +1115,12 @@ def Run_Build_Command(config, parameters):
else: else:
cmd_args += " \\\n -nopython" cmd_args += " \\\n -nopython"
#----------------------------------------------------- #-----------------------------------------------------------------------------------
# [4] Make the consolidated command line # [4] Make the consolidated command line
#----------------------------------------------------- # "caffeinate" makes the CPU run at full speed even when the screen is locked.
#-----------------------------------------------------------------------------------
command = "time" command = "time"
command += " \\\n %s" % parameters['build_cmd'] command += " \\\n caffeinate -i %s" % parameters['build_cmd']
command += cmd_args command += cmd_args
command += " 2>&1 | tee %s; \\\n" % parameters['logfile'] command += " 2>&1 | tee %s; \\\n" % parameters['logfile']
command += "test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command += "test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
@ -1831,33 +1833,33 @@ def Deploy_Binaries_For_Bundle(config, parameters):
#----------------------------------------------------------------------------------------------- #-----------------------------------------------------------------------------------------------
# [9] Special deployment of Python3.11 from Homebrew # [9] Special deployment of Python3.11 from Homebrew
# To use Python3.1 from Homebrew on Sonoma... # To use Python3.11 from Homebrew on Sequoia...
# in "/usr/local/opt/python@3.11/lib/" # in "/usr/local/opt/python@3.11/lib/"
# Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed # Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed
# pkgconfig/ # pkgconfig/
# #
# Use the "python3HB.py" tool to make different symbolic links [*] including the above one. # Use the "python3HB.py" tool to make different symbolic links [*] including the above one.
# Sonoma{kazzz-s} lib (1)% pwd # Sequoia{kazzz-s} lib (1)% pwd
# /usr/local/opt/python@3.11/lib # /usr/local/opt/python@3.11/lib
# Sonoma{kazzz-s} lib (2)% ll # Sequoia{kazzz-s} lib (2)% ll
# total 0 # total 0
# drwxr-xr-x 4 kazzz-s admin 128 9 21 23:03 . # drwxr-xr-x 4 kazzz-s admin 128 9 21 23:03 .
# drwxr-xr-x 14 kazzz-s admin 448 9 21 18:33 .. # drwxr-xr-x 14 kazzz-s admin 448 9 21 18:33 ..
# [*] lrwxr-xr-x 1 kazzz-s admin 31 9 21 23:03 Python.framework -> ../Frameworks/Python.framework/ # [*] lrwxr-xr-x 1 kazzz-s admin 31 9 21 23:03 Python.framework -> ../Frameworks/Python.framework/
# drwxr-xr-x 4 kazzz-s admin 128 9 7 10:03 pkgconfig # drwxr-xr-x 4 kazzz-s admin 128 9 7 10:03 pkgconfig
# #
# Sonoma{kazzz-s} Python.framework (3)% pwd # Sequoia{kazzz-s} Python.framework (3)% pwd
# /usr/local/opt/python@3.11/Frameworks/Python.framework/Versions # /usr/local/opt/python@3.11/Frameworks/Python.framework/Versions
# Sonoma{kazzz-s} Versions (4)% ll # Sequoia{kazzz-s} Versions (4)% ll
# total 0 # total 0
# drwxr-xr-x 4 kazzz-s admin 128 9 21 23:03 . # drwxr-xr-x 4 kazzz-s admin 128 9 21 23:03 .
# drwxr-xr-x 6 kazzz-s admin 192 9 21 23:03 .. # drwxr-xr-x 6 kazzz-s admin 192 9 21 23:03 ..
# drwxr-xr-x 9 kazzz-s admin 288 9 7 10:03 3.11 # drwxr-xr-x 9 kazzz-s admin 288 9 7 10:03 3.11
# [*] lrwxr-xr-x 1 kazzz-s admin 5 9 21 23:03 Current -> 3.11/ # [*] lrwxr-xr-x 1 kazzz-s admin 5 9 21 23:03 Current -> 3.11/
# #
# Sonoma{kazzz-s} Python.framework (5)% pwd # Sequoia{kazzz-s} Python.framework (5)% pwd
# /usr/local/opt/python@3.11/Frameworks/Python.framework # /usr/local/opt/python@3.11/Frameworks/Python.framework
# Sonoma{kazzz-s} Python.framework (6)% ll # Sequoia{kazzz-s} Python.framework (6)% ll
# total 0 # total 0
# drwxr-xr-x 6 kazzz-s admin 192 9 21 23:03 . # drwxr-xr-x 6 kazzz-s admin 192 9 21 23:03 .
# drwxr-xr-x 3 kazzz-s admin 96 9 7 10:03 .. # drwxr-xr-x 3 kazzz-s admin 96 9 7 10:03 ..

View File

@ -6,7 +6,7 @@
# #
# Here are dictionaries of ... # Here are dictionaries of ...
# different modules for building KLayout (http://www.klayout.de/index.php) # different modules for building KLayout (http://www.klayout.de/index.php)
# version 0.29.11 or later on different Apple Mac OSX platforms. # version 0.30.2 or later on different Apple Mac OSX platforms.
# #
# This file is imported by 'build4mac.py' script. # This file is imported by 'build4mac.py' script.
#=============================================================================== #===============================================================================

View File

@ -6,7 +6,7 @@
# #
# Here are utility functions and classes ... # Here are utility functions and classes ...
# for building KLayout (http://www.klayout.de/index.php) # for building KLayout (http://www.klayout.de/index.php)
# version 0.29.7 or later on different Apple Mac OSX platforms. # version 0.30.2 or later on different Apple Mac OSX platforms.
# #
# This file is imported by 'build4mac.py' script. # This file is imported by 'build4mac.py' script.
#======================================================================================== #========================================================================================

View File

@ -5,7 +5,7 @@
# File: "macbuild/macQAT.py" # File: "macbuild/macQAT.py"
# #
# The top Python script to run "ut_runner" after building KLayout # The top Python script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.29.7 or later on different # (http://www.klayout.de/index.php) version 0.30.2 or later on different
# Apple Mac OSX platforms. # Apple Mac OSX platforms.
# #
# This script must be copied to a "*.macQAT/" directory to run. # This script must be copied to a "*.macQAT/" directory to run.

View File

@ -4,7 +4,7 @@
# File: "macbuild/macQAT.sh" # File: "macbuild/macQAT.sh"
# #
# The top Bash script to run "ut_runner" after building KLayout # The top Bash script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.29.7 or later on different # (http://www.klayout.de/index.php) version 0.30.2 or later on different
# Apple Mac OSX platforms. # Apple Mac OSX platforms.
# #
# This script must be copied to a "*.macQAT/" directory to run. # This script must be copied to a "*.macQAT/" directory to run.

View File

@ -4,7 +4,7 @@
# File: "macbuild/macQAT2.sh" # File: "macbuild/macQAT2.sh"
# #
# The top Bash script to run "ut_runner" after building KLayout # The top Bash script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.29.7 or later on different # (http://www.klayout.de/index.php) version 0.30.2 or later on different
# Apple Mac OSX platforms. # Apple Mac OSX platforms.
# #
# This script must be copied to a directory that can be found in $PATH. # This script must be copied to a directory that can be found in $PATH.

View File

@ -78,13 +78,13 @@ def SetGlobals():
Usage = "\n" Usage = "\n"
Usage += "---------------------------------------------------------------------------------------------------------\n" Usage += "---------------------------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'makeDMG4mac.py' >>\n" Usage += "<< Usage of 'makeDMG4mac.py' >>\n"
Usage += " for making a DMG file of KLayout 0.29.11 or later on different Apple macOS platforms.\n" Usage += " for making a DMG file of KLayout 0.30.2 or later on different Apple macOS platforms.\n"
Usage += "\n" Usage += "\n"
Usage += "$ [python] ./makeDMG4mac.py\n" Usage += "$ [python] ./makeDMG4mac.py\n"
Usage += " option & argument : descriptions | default value\n" Usage += " option & argument : descriptions | default value\n"
Usage += " ----------------------------------------------------------------------------------+-----------------\n" Usage += " ----------------------------------------------------------------------------------+-----------------\n"
Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n" Usage += " <-p|--pkg <dir>> : package directory created by `build4mac.py` with [-y|-Y] | ``\n"
Usage += " : like 'LW-qt5MP.pkg.macos-Monterey-release-Rmp33Pmp311' | \n" Usage += " : like 'LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312' | \n"
Usage += " <-c|--clean> : clean the work directory | disabled\n" Usage += " <-c|--clean> : clean the work directory | disabled\n"
Usage += " <-m|--make> : make a compressed DMG file | disabled\n" Usage += " <-m|--make> : make a compressed DMG file | disabled\n"
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n" Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
@ -218,17 +218,17 @@ def SetGlobals():
## To check the contents of the package directory ## To check the contents of the package directory
# #
# The package directory name should look like: # The package directory name should look like:
# * ST-qt5MP.pkg.macos-Sonoma-release-RsysPsys # * ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sonoma-release-Rana3Pana3 # * LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sonoma-release-Rhb34Phb312 --- (1) # * LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312 --- (1)
# * LW-qt5MP.pkg.macos-Sonoma-release-Rmp33Pmp312 # * LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sonoma-release-RsysPhb311 # * HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311
# #
# * ST-qt6MP.pkg.macos-Sonoma-release-RsysPsys # * ST-qt6MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sonoma-release-Rmp33Pmp312 # * LW-qt6MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# #
# Generated DMG will be, for example, # Generated DMG will be, for example,
# (1) ---> LW-klayout-0.29.7-macOS-Sonoma-1-qt6Brew-Rhb34Phb312.dmg # (1) ---> LW-klayout-0.30.2-macOS-Sequoia-1-qt6Brew-Rhb34Phb312.dmg
# #
# @return on success, positive integer in [MB] that tells approx. occupied disc space; # @return on success, positive integer in [MB] that tells approx. occupied disc space;
# on failure, -1 # on failure, -1
@ -268,15 +268,15 @@ def CheckPkgDirectory():
#----------------------------------------------------------------------------------------------- #-----------------------------------------------------------------------------------------------
# [2] Identify (Qt, Ruby, Python) from PkgDir # [2] Identify (Qt, Ruby, Python) from PkgDir
# * ST-qt5MP.pkg.macos-Sonoma-release-RsysPsys # * ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sonoma-release-Rana3Pana3 # * LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sonoma-release-Rhb34Phb312 # * LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312
# * LW-qt5MP.pkg.macos-Sonoma-release-Rmp33Pmp312 # * LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sonoma-release-RsysPhb311 # * HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311
# * EX-qt5MP.pkg.macos-Sonoma-release-Rhb34Pmp312 # * EX-qt5MP.pkg.macos-Sequoia-release-Rhb34Pmp312
# #
# * ST-qt6MP.pkg.macos-Sonoma-release-RsysPsys # * ST-qt6MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sonoma-release-Rmp33Pmp312 # * LW-qt6MP.pkg.macos-Sequoia-release-Rmp33Pmp312
#----------------------------------------------------------------------------------------------- #-----------------------------------------------------------------------------------------------
# 0 1 2 3 4 5 6 7 # 0 1 2 3 4 5 6 7
patQRP = r'(ST|LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-])(release|debug)([-])([0-9A-Za-z]+)' patQRP = r'(ST|LW|HW|EX)([-])([qt5|qt6][0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-])(release|debug)([-])([0-9A-Za-z]+)'

View File

@ -339,7 +339,7 @@ def Parse_CommandLine_Arguments():
Usage += " (3) $ ./nightlyBuild.py --test |\n" Usage += " (3) $ ./nightlyBuild.py --test |\n"
Usage += " (4) $ ./nightlyBuild.py --check (confirm the QA Test results) |\n" Usage += " (4) $ ./nightlyBuild.py --check (confirm the QA Test results) |\n"
Usage += " (5) $ ./nightlyBuild.py --makedmg 1 |\n" Usage += " (5) $ ./nightlyBuild.py --makedmg 1 |\n"
Usage += " (6) $ ./nightlyBuild.py --upload '0.29.7' |\n" Usage += " (6) $ ./nightlyBuild.py --upload '0.30.2' |\n"
Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n" Usage += " (7) $ ./nightlyBuild.py --cleandmg 1 |\n"
Usage += "-----------------------------------------------------------------------------+----------------------------\n" Usage += "-----------------------------------------------------------------------------+----------------------------\n"

View File

@ -18,7 +18,7 @@ qtVer,target,bdType
5,0,r 5,0,r
5,1,r 5,1,r
6,2,r 6,2,r
6,13,r 5,13,r
5,4,r 5,4,r
#6,0,r #6,0,r
#6,1,r #6,1,r

Can't render this file because it contains an unexpected character in line 5 and column 30.

View File

@ -33,8 +33,7 @@ def SetGlobals():
Usage += "\n" Usage += "\n"
Usage += " option & argument : descriptions | default value\n" Usage += " option & argument : descriptions | default value\n"
Usage += " -------------------------------------------------------------------+---------------\n" Usage += " -------------------------------------------------------------------+---------------\n"
Usage += " <-v|--version <number>>: in ['3.8', '3.9', '3.10', '3.11', '3.12', | ''\n" Usage += " <-v|--version <number>>: in ['3.11', '3.12','3.13'] | ''\n"
Usage += " '3.13'] |\n"
Usage += " [-u|-unlink] : unlink only | disabled\n" Usage += " [-u|-unlink] : unlink only | disabled\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n" Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += "----------------------------------------------------------------------+-----------------\n" Usage += "----------------------------------------------------------------------+-----------------\n"
@ -50,7 +49,7 @@ def Parse_CLI_Args():
p.add_option( '-v', '--version', p.add_option( '-v', '--version',
dest='version', dest='version',
help="python3 version=['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']" ) help="python3 version=['3.11', '3.12', '3.13']" )
p.add_option( '-u', '--unlink', p.add_option( '-u', '--unlink',
action='store_true', action='store_true',
@ -75,7 +74,7 @@ def Parse_CLI_Args():
Version = opt.version Version = opt.version
UnlinkOnly = opt.unlink UnlinkOnly = opt.unlink
if not Version in [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13' ]: if not Version in [ '3.11', '3.12', '3.13' ]:
print( "! Unsupported Python 3 version <%s>" % Version ) print( "! Unsupported Python 3 version <%s>" % Version )
print(Usage) print(Usage)
sys.exit(0) sys.exit(0)

View File

@ -43,8 +43,13 @@ int converter_main (int argc, char *argv[], const std::string &format)
generic_reader_options.add_options (cmd); generic_reader_options.add_options (cmd);
cmd << tl::arg ("input", &infile, "The input file (any format, may be gzip compressed)", cmd << tl::arg ("input", &infile, "The input file (any format, may be gzip compressed)",
"You can use '+' or ',' to supply multiple files which will be read after each other into the same layout. " "Multiple files can be combined using '+' or ','. '+' will combine the files in 'blending' mode. "
"This provides some cheap, but risky way of merging files. Beware of cell name conflicts.") "In this mode it is possible to combine identically named cells into one cell for example. This mode "
"needs to be used with care and there some constraints - e.g. the database unit of the involved "
"layouts needs to be the same. When using ',' as a separator, blending is not used, but the layouts "
"are merged by first creating two layouts and then combining them into one. This mode is more robust "
"but does not allow cell merging. '+' combination has higher priority than ',' - i.e. 'a+b,c' is "
"understood as '(a+b),c'.")
<< tl::arg ("output", &outfile, tl::sprintf ("The output file (%s format)", format)) << tl::arg ("output", &outfile, tl::sprintf ("The output file (%s format)", format))
; ;

View File

@ -22,6 +22,8 @@
#include "bdReaderOptions.h" #include "bdReaderOptions.h"
#include "dbLoadLayoutOptions.h" #include "dbLoadLayoutOptions.h"
#include "dbLayerMapping.h"
#include "dbCellMapping.h"
#include "tlCommandLineParser.h" #include "tlCommandLineParser.h"
#include "tlStream.h" #include "tlStream.h"
@ -831,15 +833,28 @@ static std::string::size_type find_file_sep (const std::string &s, std::string::
} }
} }
static std::vector<std::string> split_file_list (const std::string &infile) static std::vector<std::vector<std::string> > split_file_list (const std::string &infile)
{ {
std::vector<std::string> files; std::vector<std::vector<std::string> > files;
files.push_back (std::vector<std::string> ());
size_t p = 0; size_t p = 0;
for (size_t pp = 0; (pp = find_file_sep (infile, p)) != std::string::npos; p = pp + 1) { while (true) {
files.push_back (std::string (infile, p, pp - p));
size_t sep = find_file_sep (infile, p);
if (sep == std::string::npos) {
files.back ().push_back (std::string (infile, p));
return files;
}
files.back ().push_back (std::string (infile, p, sep - p));
if (infile [sep] == ',') {
files.push_back (std::vector<std::string> ());
}
p = sep + 1;
} }
files.push_back (std::string (infile, p));
return files; return files;
} }
@ -850,16 +865,73 @@ void read_files (db::Layout &layout, const std::string &infile, const db::LoadLa
// db::LayoutLocker locker (&layout); // db::LayoutLocker locker (&layout);
// but there are yet unknown side effects // but there are yet unknown side effects
// enter a LEF caching context for chaining multiple DEF with the same LEF std::vector<std::vector<std::string> > files = split_file_list (infile);
db::LoadLayoutOptions local_options (options);
local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true);
std::vector<std::string> files = split_file_list (infile); for (auto ff = files.begin (); ff != files.end (); ++ff) {
// enter a LEF caching context for chaining multiple DEF with the same LEF
db::LoadLayoutOptions local_options (options);
local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true);
db::Layout tmp;
db::Layout *ly = (ff == files.begin () ? &layout : &tmp);
for (auto f = ff->begin (); f != ff->end (); ++f) {
tl::InputStream stream (*f);
db::Reader reader (stream);
if (f != ff->begin ()) {
reader.set_expected_dbu (ly->dbu ());
}
reader.read (*ly, local_options);
}
if (ly != &layout) {
// Move over cells from read layout to destination ("," separated blocks).
// This path does not imply limitations in terms of DBU compatibility etc.
std::vector<db::cell_index_type> cells_target;
std::vector<db::cell_index_type> cells_source;
for (auto c = tmp.begin_top_down (); c != tmp.end_top_cells (); ++c) {
cells_source.push_back (*c);
// as a special rule, join ghost cells if the source top cell fits into
// a ghost cell of the target.
auto cell_target = layout.cell_by_name (tmp.cell_name (*c));
if (cell_target.first && layout.cell (cell_target.second).is_ghost_cell ()) {
cells_target.push_back (cell_target.second);
} else {
cells_target.push_back (layout.add_cell (tmp.cell_name (*c)));
}
}
// ghost cell joining also works the other way around: a top cell of destination
// can match a ghost cell of the source
for (auto c = tmp.end_top_cells (); c != tmp.end_top_down (); ++c) {
const db::Cell &cell_source = tmp.cell (*c);
auto cell_target = layout.cell_by_name (tmp.cell_name (*c));
if (cell_source.is_ghost_cell () && cell_target.first) {
cells_source.push_back (*c);
cells_target.push_back (cell_target.second);
}
}
db::CellMapping cm;
cm.create_multi_mapping_full (layout, cells_target, tmp, cells_source);
db::LayerMapping lm;
lm.create_full (layout, tmp);
layout.move_tree_shapes (tmp, cm, lm);
}
for (std::vector<std::string>::const_iterator f = files.begin (); f != files.end (); ++f) {
tl::InputStream stream (*f);
db::Reader reader (stream);
reader.read (layout, local_options);
} }
} }

View File

@ -483,7 +483,7 @@ TEST(10)
std::string input; std::string input;
for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) { for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) {
if (i > 0) { if (i > 0) {
input += ","; input += "+";
} }
input += def_dir + "/" + def_files[i]; input += def_dir + "/" + def_files[i];
} }
@ -510,3 +510,201 @@ TEST(10)
db::compare_layouts (this, layout, input_au, db::WriteOAS); db::compare_layouts (this, layout, input_au, db::WriteOAS);
} }
// Merging with +
TEST(11_1)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_1.oas";
std::string input = input_dir + "/strm2oas_1.oas+" + input_dir + "/strm2oas_2.oas";
std::string output = this->tmp_file ("strm2oas_1.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
}
db::compare_layouts (this, layout, input_au, db::WriteOAS);
}
// Merging with + not allowed on different DBUs
TEST(11_2)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_1.oas";
std::string input = input_dir + "/strm2oas_1.oas+" + input_dir + "/strm2oas_2_10nm.oas";
std::string output = this->tmp_file ("strm2oas_1.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
try {
bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name);
EXPECT_EQ (1, 0);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Former and present database units are not compatible: 0.001 (former) vs. 0.01 (present)");
}
}
// Merging with + not allowed on different DBUs
TEST(11_3)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_3.oas";
std::string input = input_dir + "/strm2oas_1.oas," + input_dir + "/strm2oas_2_10nm.oas";
std::string output = this->tmp_file ("strm2oas_3.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
}
db::compare_layouts (this, layout, input_au, db::WriteOAS);
}
// Merging with + and , under the presence of ghost cells: test+test,top->(test)
TEST(12_1)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_12_1.oas";
std::string input = input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas," + input_dir + "/strm2oas_c.oas";
std::string output = this->tmp_file ("strm2oas_12_1.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
}
db::compare_layouts (this, layout, input_au, db::WriteOAS);
}
// Merging with + and , under the presence of ghost cells: top->(test),test+test
TEST(12_2)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_12_2.oas";
std::string input = input_dir + "/strm2oas_c.oas," + input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas";
std::string output = this->tmp_file ("strm2oas_12_2.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
}
db::compare_layouts (this, layout, input_au, db::WriteOAS);
}
// Merging with + and , under the presence of ghost cells: test+test,toptop->top->(test)
TEST(12_3)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_12_3.oas";
std::string input = input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas," + input_dir + "/strm2oas_cc.oas";
std::string output = this->tmp_file ("strm2oas_12_3.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
}
db::compare_layouts (this, layout, input_au, db::WriteOAS);
}
// Merging with + and , under the presence of ghost cells: toptop->top->(test),test+test
TEST(12_4)
{
std::string input_dir = tl::testdata ();
input_dir += "/bd";
std::string input_au = input_dir + "/strm2oas_au_12_4.oas";
std::string input = input_dir + "/strm2oas_cc.oas," + input_dir + "/strm2oas_a.oas+" + input_dir + "/strm2oas_b.oas";
std::string output = this->tmp_file ("strm2oas_12_4.oas");
const char *argv[] = { "x",
"--blend-mode=0",
input.c_str (),
output.c_str ()
};
EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::LoadLayoutOptions options;
db::Reader reader (stream);
reader.read (layout, options);
}
db::compare_layouts (this, layout, input_au, db::WriteOAS);
}

View File

@ -146,8 +146,6 @@ CommonReaderBase::name_for_id (size_t id) const
void void
CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string &cn) CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string &cn)
{ {
m_name_for_id.insert (std::make_pair (id, cn));
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id); std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn); std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
@ -156,9 +154,22 @@ CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string
} }
if (iname != m_name_map.end () && iname->second.first != null_id && iname->second.first != id) { if (iname != m_name_map.end () && iname->second.first != null_id && iname->second.first != id) {
common_reader_error (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld")), cn, id, iname->second.first));
// picking a different name on name clash (issue #2088)
std::string cn_new = cn + "_id$" + tl::to_string (id);
for (size_t i = 0; m_name_map.find (cn_new) != m_name_map.end (); ++i) {
cn_new = cn + "_id$" + tl::to_string (id) + "$" + tl::to_string (i);
}
common_reader_warn (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld, renaming first to %s")), cn, id, iname->second.first, cn_new));
rename_cell (layout, id, cn_new);
return;
} }
m_name_for_id.insert (std::make_pair (id, cn));
if (iid != m_id_map.end () && iname != m_name_map.end ()) { if (iid != m_id_map.end () && iname != m_name_map.end ()) {
if (iname->second.second != iid->second.second) { if (iname->second.second != iid->second.second) {
@ -240,7 +251,7 @@ CommonReaderBase::cell_for_instance (db::Layout &layout, const std::string &cn)
} }
void void
CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta, bool no_duplicate_instances) const CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta, bool no_duplicate_instances)
{ {
const db::Cell &src_cell = layout.cell (src_cell_index); const db::Cell &src_cell = layout.cell (src_cell_index);
db::Cell &target_cell = layout.cell (target_cell_index); db::Cell &target_cell = layout.cell (target_cell_index);
@ -284,7 +295,7 @@ CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cel
} }
void void
CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta) const CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta)
{ {
const db::Cell &src_cell = layout.cell (src_cell_index); const db::Cell &src_cell = layout.cell (src_cell_index);
db::Cell &target_cell = layout.cell (target_cell_index); db::Cell &target_cell = layout.cell (target_cell_index);
@ -297,20 +308,21 @@ CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_ind
} }
// replace all instances of the new cell with the original one // replace all instances of the new cell with the original one
layout.replace_instances_of (src_cell.cell_index (), target_cell.cell_index ()); layout.replace_instances_of (src_cell_index, target_cell_index);
// merge meta info // merge meta info
if (with_meta) { if (with_meta) {
auto ib = layout.begin_meta (src_cell.cell_index ()); auto ib = layout.begin_meta (src_cell_index);
auto ie = layout.end_meta (src_cell.cell_index ()); auto ie = layout.end_meta (src_cell_index);
for (auto i = ib; i != ie; ++i) { for (auto i = ib; i != ie; ++i) {
layout.add_meta_info (target_cell.cell_index (), i->first, i->second); layout.add_meta_info (target_cell_index, i->first, i->second);
} }
} }
layout.clear_meta (src_cell.cell_index ()); layout.clear_meta (src_cell_index);
// finally delete the new cell // finally delete the new cell
layout.delete_cell (src_cell.cell_index ()); m_temp_cells.erase (src_cell_index);
layout.delete_cell (src_cell_index);
} }
void void

View File

@ -242,12 +242,12 @@ protected:
/** /**
* @brief Merge (and delete) the src_cell into target_cell * @brief Merge (and delete) the src_cell into target_cell
*/ */
void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta, bool no_duplicate_instances) const; void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta, bool no_duplicate_instances);
/** /**
* @brief Merge (and delete) the src_cell into target_cell without instances * @brief Merge (and delete) the src_cell into target_cell without instances
*/ */
void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta) const; void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta);
/** /**
* @brief Gets the layer name map * @brief Gets the layer name map

View File

@ -77,14 +77,16 @@ public:
} }
// because the rasterizer can't handle overlapping cells we need to multiply the row and columns steps // because the rasterizer can't handle overlapping cells we need to multiply the row and columns steps
// with an integer until the effective rasterizer pitch get big enough. // with an integer until the effective rasterizer pitch gets big enough.
m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1; m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1;
m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1; m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1;
db::Box fp_bbox = fp.box (); db::Box fp_bbox = fp.box ();
// compensate for distortion by sheared kernel // compensate for distortion by sheared kernel
fp_bbox.enlarge (db::Vector (db::coord_traits<db::Coord>::rounded (double (fp_bbox.height ()) * std::abs (m_column_step.x ()) / dy), db::coord_traits<db::Coord>::rounded (double (fp_bbox.width ()) * std::abs (m_row_step.y ()) / dx))); db::Coord ex = std::max (std::abs (db::Coord (m_column_step.x () * m_column_steps)), std::abs (db::Coord (m_row_step.x () * m_row_steps)));
db::Coord ey = std::max (std::abs (db::Coord (m_column_step.y () * m_column_steps)), std::abs (db::Coord (m_row_step.y () * m_row_steps)));
fp_bbox.enlarge (db::Vector (ex, ey));
int columns_per_rows = (int (m_row_steps) * m_row_step.y ()) / dy; int columns_per_rows = (int (m_row_steps) * m_row_step.y ()) / dy;
int rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx; int rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx;
@ -167,6 +169,11 @@ public:
return m_area_maps [i]; return m_area_maps [i];
} }
db::AreaMap &area_map (unsigned int i)
{
return m_area_maps [i];
}
private: private:
std::vector<db::AreaMap> m_area_maps; std::vector<db::AreaMap> m_area_maps;
db::Vector m_row_step, m_column_step; db::Vector m_row_step, m_column_step;
@ -246,7 +253,7 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
for (unsigned int i = 0; i < am.area_maps (); ++i) { for (unsigned int i = 0; i < am.area_maps (); ++i) {
const db::AreaMap &am1 = am.area_map (i); db::AreaMap &am1 = am.area_map (i);
size_t nx = am1.nx (); size_t nx = am1.nx ();
size_t ny = am1.ny (); size_t ny = am1.ny ();
@ -263,31 +270,54 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
++jj; ++jj;
} }
ninsts += (jj - j);
db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin; db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ()); p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());
db::CellInstArray array; db::CellInstArray array;
if (jj > j + 1) { // try to expand the array in x direction
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (), (unsigned long) (jj - j), 1); size_t ii = i + 1;
for ( ; ii < nx; ++ii) {
bool all = true;
for (size_t k = j; k < jj && all; ++k) {
all = am1.get (ii, k) == am1.pixel_area ();
}
if (all) {
for (size_t k = j; k < jj; ++k) {
// disable pixel, so we do not see it again in the following columns
am1.get (ii, k) = 0;
}
} else {
break;
}
}
ninsts += (jj - j) * (ii - i);
if (jj > j + 1 || ii > i + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (am1.d ().x (), 0), (unsigned long) (jj - j), (unsigned long) (ii - i));
} else { } else {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0)); array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
} }
{ {
// In case we run this from a tiling processor we need to lock against multithread races // In case we run this from a tiling processor we need to lock against multithread races
tl::MutexLocker locker (&db::TilingProcessor::output_lock ()); tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array); cell->insert (array);
} }
if (remaining_parts) { if (remaining_parts) {
if (am1.d ().y () == am1.p ().y ()) { if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
filled_regions.push_back (db::Polygon (db::Box (db::Point (), db::Point (am1.p ().x (), am1.p ().y () * db::Coord (jj - j))).moved (kernel_origin + p0))); db::Box fill_box (db::Point (), db::Point (am1.p ().x () * db::Coord (ii - i), am1.p ().y () * db::Coord (jj - j)));
filled_regions.push_back (db::Polygon (fill_box.enlarged (fill_margin).moved (kernel_origin + p0)));
} else { } else {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
for (size_t k = 0; k < jj - j; ++k) { for (size_t k = 0; k < jj - j; ++k) {
filled_regions.push_back (db::Polygon (db::Box (db::Point (), db::Point () + am1.p ()).moved (kernel_origin + p0 + db::Vector (0, am1.d ().y () * db::Coord (k))))); for (size_t l = 0; l < ii - i; ++l) {
filled_regions.push_back (db::Polygon (fill_box.moved (kernel_origin + p0 + db::Vector (am1.d ().x () * db::Coord (l), am1.d ().y () * db::Coord (k)))));
}
} }
} }
} }
@ -314,19 +344,9 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
if (any_fill) { if (any_fill) {
if (remaining_parts) { if (remaining_parts) {
std::vector <db::Polygon> fp1; std::vector <db::Polygon> fp1;
if (fill_margin != db::Vector ()) {
ep.size (filled_regions, fill_margin.x (), fill_margin.y (), fp1, 3 /*mode*/, false /*=don't resolve holes*/);
filled_regions.swap (fp1);
fp1.clear ();
}
fp1.push_back (fp0); fp1.push_back (fp0);
ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/); ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);
} }
return true; return true;

View File

@ -1918,7 +1918,7 @@ rasterize_impl (const db::polygon<C> &polygon, db::area_map<C> &am)
area_type aa = a; area_type aa = a;
if (dx == py) { if (dx == px) {
box_type cell (x, y, xx, yy); box_type cell (x, y, xx, yy);

View File

@ -61,7 +61,7 @@ join_layer_names (std::string &s, const std::string &n)
// ReaderBase implementation // ReaderBase implementation
ReaderBase::ReaderBase () ReaderBase::ReaderBase ()
: m_warnings_as_errors (false), m_warn_level (1), m_warn_count_for_same_message (0), m_first_warning (true) : m_warnings_as_errors (false), m_warn_level (1), m_warn_count_for_same_message (0), m_first_warning (true), m_expected_dbu (0.0)
{ {
} }
@ -114,6 +114,20 @@ ReaderBase::compress_warning (const std::string &msg)
} }
} }
void
ReaderBase::set_expected_dbu (double dbu)
{
m_expected_dbu = dbu;
}
void
ReaderBase::check_dbu (double dbu) const
{
if (m_expected_dbu > db::epsilon && fabs (dbu - m_expected_dbu) > db::epsilon) {
throw ReaderException (tl::sprintf (tl::to_string (tr ("Former and present database units are not compatible: %.12g (former) vs. %.12g (present)")), m_expected_dbu, dbu));
}
}
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Reader implementation // Reader implementation

View File

@ -138,6 +138,33 @@ public:
*/ */
int compress_warning (const std::string &msg); int compress_warning (const std::string &msg);
/**
* @brief Sets the expected database unit
*
* With this value set, the reader can check if the present database unit is
* compatible with the expected one and either take actions to scale the layouts
* or to reject the file.
*
* Setting the value to 0 resets the expected DBU and will disable all checks
* or scaling.
*/
void set_expected_dbu (double dbu);
/**
* @brief Gets the expected database unit
*/
double expected_dbu () const
{
return m_expected_dbu;
}
/**
* @brief Checks the given DBU against the expected one
*
* This method will raise an exception if the database units do not match.
*/
void check_dbu (double dbu) const;
protected: protected:
virtual void init (const db::LoadLayoutOptions &options); virtual void init (const db::LoadLayoutOptions &options);
@ -147,6 +174,7 @@ private:
std::string m_last_warning; std::string m_last_warning;
int m_warn_count_for_same_message; int m_warn_count_for_same_message;
bool m_first_warning; bool m_first_warning;
double m_expected_dbu;
}; };
/** /**
@ -231,6 +259,22 @@ public:
return mp_actual_reader->warnings_as_errors (); return mp_actual_reader->warnings_as_errors ();
} }
/**
* @brief Sets the expected database unit (see ReaderBase)
*/
void set_expected_dbu (double dbu)
{
return mp_actual_reader->set_expected_dbu (dbu);
}
/**
* @brief Gets the expected database unit
*/
double expected_dbu () const
{
return mp_actual_reader->expected_dbu ();
}
private: private:
ReaderBase *mp_actual_reader; ReaderBase *mp_actual_reader;
tl::InputStream &m_stream; tl::InputStream &m_stream;

View File

@ -500,15 +500,21 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
} }
if (mp_shapes) { if (mp_shapes) {
// Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor) // Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor)
// TODO: get rid of that const cast // TODO: get rid of that const cast
(const_cast <db::Shapes *> (mp_shapes))->update (); (const_cast <db::Shapes *> (mp_shapes))->update ();
start_shapes (); start_shapes ();
} else if (mp_layout && (! m_has_layers || m_current_layer < m_layers.size ())) { } else if (mp_layout && (! m_has_layers || m_current_layer < m_layers.size ())) {
// Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor) // Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor)
mp_layout->update (); mp_layout->update ();
new_cell (receiver); new_cell (receiver);
next_shape (receiver); next_shape (receiver);
} }
if (mp_layout && ! at_end ()) { if (mp_layout && ! at_end ()) {

View File

@ -1127,10 +1127,20 @@ void Shapes::reset_bbox_dirty ()
void Shapes::update () void Shapes::update ()
{ {
std::unique_ptr<tl::MutexLocker> locker;
// If not in a layout context, we should lock here against multiple calls from different threads.
// In a layout context, the Layout object will do that for us.
if (layout () == 0) {
static tl::Mutex lock;
locker.reset (new tl::MutexLocker (&lock));
}
for (tl::vector<LayerBase *>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { for (tl::vector<LayerBase *>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) {
(*l)->sort (); (*l)->sort ();
(*l)->update_bbox (); (*l)->update_bbox ();
} }
set_dirty (false); set_dirty (false);
} }

View File

@ -149,7 +149,7 @@ TextWriter::write_props (const db::Layout & /*layout*/, size_t prop_id)
const tl::Variant &name = p->first; const tl::Variant &name = p->first;
const tl::Variant &value = p->second; const tl::Variant &value = p->second;
if (name.is_long () || name.is_ulong ()) { if (name.can_convert_to_long ()) {
*this << " {" << int (name.to_long ()) << " {" << value.to_string () << "}}" << endl_str (); *this << " {" << int (name.to_long ()) << " {" << value.to_string () << "}}" << endl_str ();
} else if (name.is_a_string ()) { } else if (name.is_a_string ()) {
*this << " {{" << name.to_string () << "} {" << value.to_string () << "}}" << endl_str (); *this << " {{" << name.to_string () << "} {" << value.to_string () << "}}" << endl_str ();

View File

@ -223,213 +223,305 @@ static db::Shape insert_shape_with_dcplx_trans (db::Shapes *s, const db::Shape &
return s->insert (shape, dbu_trans.inverted () * trans * dbu_trans, pm); return s->insert (shape, dbu_trans.inverted () * trans * dbu_trans, pm);
} }
namespace {
/**
* @brief Provides protection against inserting shapes into a target that is also source
*
* The strategy is to use an temporary Shapes container if needed.
*/
class ProtectedShapes
{
public:
ProtectedShapes (db::Shapes *target, const db::RecursiveShapeIterator &src)
: mp_target (target), mp_tmp_shapes ()
{
if (target == src.shapes () || target->layout () == src.layout ()) {
mp_tmp_shapes.reset (new db::Shapes ());
}
}
ProtectedShapes (db::Shapes *target, const db::Shapes &src)
: mp_target (target), mp_tmp_shapes ()
{
if (target == &src || target->layout () == src.layout ()) {
mp_tmp_shapes.reset (new db::Shapes ());
}
}
ProtectedShapes (db::Shapes *target, const db::Region &src)
: mp_target (target), mp_tmp_shapes ()
{
auto iter = src.begin_iter ();
if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) {
mp_tmp_shapes.reset (new db::Shapes ());
}
}
ProtectedShapes (db::Shapes *target, const db::Texts &src)
: mp_target (target), mp_tmp_shapes ()
{
auto iter = src.begin_iter ();
if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) {
mp_tmp_shapes.reset (new db::Shapes ());
}
}
ProtectedShapes (db::Shapes *target, const db::Edges &src)
: mp_target (target), mp_tmp_shapes ()
{
auto iter = src.begin_iter ();
if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) {
mp_tmp_shapes.reset (new db::Shapes ());
}
}
ProtectedShapes (db::Shapes *target, const db::EdgePairs &src)
: mp_target (target), mp_tmp_shapes ()
{
auto iter = src.begin_iter ();
if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) {
mp_tmp_shapes.reset (new db::Shapes ());
}
}
~ProtectedShapes ()
{
if (mp_tmp_shapes.get ()) {
mp_target->insert (*mp_tmp_shapes.get ());
}
}
db::Shapes *operator-> () const
{
if (mp_tmp_shapes.get ()) {
return mp_tmp_shapes.get ();
} else {
return mp_target;
}
}
private:
db::Shapes *mp_target;
std::unique_ptr<db::Shapes> mp_tmp_shapes;
};
}
static void insert_iter (db::Shapes *sh, const db::RecursiveShapeIterator &r) static void insert_iter (db::Shapes *sh, const db::RecursiveShapeIterator &r)
{ {
// NOTE: if the source (r) is from the same layout than the shapes live in, we better ProtectedShapes ps (sh, r);
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) { for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) {
tl::ident_map<db::properties_id_type> pm; tl::ident_map<db::properties_id_type> pm;
sh->insert (*i, i.trans (), pm); ps->insert (*i, i.trans (), pm);
} }
} }
static void insert_iter_with_trans (db::Shapes *sh, const db::RecursiveShapeIterator &r, const db::ICplxTrans &trans) static void insert_iter_with_trans (db::Shapes *sh, const db::RecursiveShapeIterator &r, const db::ICplxTrans &trans)
{ {
// NOTE: if the source (r) is from the same layout than the shapes live in, we better ProtectedShapes ps (sh, r);
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) { for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) {
tl::ident_map<db::properties_id_type> pm; tl::ident_map<db::properties_id_type> pm;
sh->insert (*i, trans * i.trans (), pm); ps->insert (*i, trans * i.trans (), pm);
} }
} }
static void insert_shapes (db::Shapes *sh, const db::Shapes &s) static void insert_shapes (db::Shapes *sh, const db::Shapes &s)
{ {
sh->insert (s); ProtectedShapes ps (sh, s);
ps->insert (s);
} }
static void insert_shapes_with_flags (db::Shapes *sh, const db::Shapes &s, unsigned int flags) static void insert_shapes_with_flags (db::Shapes *sh, const db::Shapes &s, unsigned int flags)
{ {
sh->insert (s, flags); ProtectedShapes ps (sh, s);
ps->insert (s, flags);
} }
static void insert_shapes_with_trans (db::Shapes *sh, const db::Shapes &s, const db::ICplxTrans &trans) static void insert_shapes_with_trans (db::Shapes *sh, const db::Shapes &s, const db::ICplxTrans &trans)
{ {
// NOTE: if the source (r) is from the same layout than the shapes live in, we better ProtectedShapes ps (sh, s);
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end(); ++i) { for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end(); ++i) {
tl::ident_map<db::properties_id_type> pm; tl::ident_map<db::properties_id_type> pm;
sh->insert (*i, trans, pm); ps->insert (*i, trans, pm);
} }
} }
static void insert_shapes_with_flag_and_trans (db::Shapes *sh, const db::Shapes &s, unsigned int flags, const db::ICplxTrans &trans) static void insert_shapes_with_flag_and_trans (db::Shapes *sh, const db::Shapes &s, unsigned int flags, const db::ICplxTrans &trans)
{ {
// NOTE: if the source (r) is from the same layout than the shapes live in, we better ProtectedShapes ps (sh, s);
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
for (db::Shapes::shape_iterator i = s.begin (flags); !i.at_end(); ++i) { for (db::Shapes::shape_iterator i = s.begin (flags); !i.at_end(); ++i) {
tl::ident_map<db::properties_id_type> pm; tl::ident_map<db::properties_id_type> pm;
sh->insert (*i, trans, pm); ps->insert (*i, trans, pm);
} }
} }
static void insert_region (db::Shapes *sh, const db::Region &r) static void insert_region (db::Shapes *sh, const db::Region &r)
{ {
// NOTE: if the source (r) is from the same layout than the shapes live in, we better ProtectedShapes ps (sh, r);
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (*s); ps->insert (*s);
} }
} }
static void insert_region_with_trans (db::Shapes *sh, const db::Region &r, const db::ICplxTrans &trans) static void insert_region_with_trans (db::Shapes *sh, const db::Region &r, const db::ICplxTrans &trans)
{ {
// NOTE: if the source (r) is from the same layout than the shapes live in, we better ProtectedShapes ps (sh, r);
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (trans)); ps->insert (s->transformed (trans));
} }
} }
static void insert_region_with_dtrans (db::Shapes *sh, const db::Region &r, const db::DCplxTrans &trans) static void insert_region_with_dtrans (db::Shapes *sh, const db::Region &r, const db::DCplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (itrans)); ps->insert (s->transformed (itrans));
} }
} }
static void insert_edges (db::Shapes *sh, const db::Edges &r) static void insert_edges (db::Shapes *sh, const db::Edges &r)
{ {
ProtectedShapes ps (sh, r);
for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (*s); ps->insert (*s);
} }
} }
static void insert_edges_with_trans (db::Shapes *sh, const db::Edges &r, const db::ICplxTrans &trans) static void insert_edges_with_trans (db::Shapes *sh, const db::Edges &r, const db::ICplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (trans)); ps->insert (s->transformed (trans));
} }
} }
static void insert_edges_with_dtrans (db::Shapes *sh, const db::Edges &r, const db::DCplxTrans &trans) static void insert_edges_with_dtrans (db::Shapes *sh, const db::Edges &r, const db::DCplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (itrans)); ps->insert (s->transformed (itrans));
} }
} }
static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs &r, db::Coord e) static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs &r, db::Coord e)
{ {
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->normalized ().to_simple_polygon (e)); ps->insert (s->normalized ().to_simple_polygon (e));
} }
} }
static void insert_edge_pairs_as_polygons_d (db::Shapes *sh, const db::EdgePairs &r, db::DCoord de) static void insert_edge_pairs_as_polygons_d (db::Shapes *sh, const db::EdgePairs &r, db::DCoord de)
{ {
ProtectedShapes ps (sh, r);
db::Coord e = db::coord_traits<db::Coord>::rounded (de / shapes_dbu (sh)); db::Coord e = db::coord_traits<db::Coord>::rounded (de / shapes_dbu (sh));
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->normalized ().to_simple_polygon (e)); ps->insert (s->normalized ().to_simple_polygon (e));
} }
} }
static void insert_edge_pairs_as_polygons_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans, db::Coord e) static void insert_edge_pairs_as_polygons_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans, db::Coord e)
{ {
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->normalized ().to_simple_polygon (e).transformed (trans)); ps->insert (s->normalized ().to_simple_polygon (e).transformed (trans));
} }
} }
static void insert_edge_pairs_as_polygons_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans, db::DCoord de) static void insert_edge_pairs_as_polygons_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans, db::DCoord de)
{ {
ProtectedShapes ps (sh, r);
db::Coord e = db::coord_traits<db::Coord>::rounded (de / shapes_dbu (sh)); db::Coord e = db::coord_traits<db::Coord>::rounded (de / shapes_dbu (sh));
db::CplxTrans dbu_trans (shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->normalized ().to_simple_polygon (e).transformed (itrans)); ps->insert (s->normalized ().to_simple_polygon (e).transformed (itrans));
} }
} }
static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r) static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r)
{ {
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->first ()); ps->insert (s->first ());
sh->insert (s->second ()); ps->insert (s->second ());
} }
} }
static void insert_edge_pairs_as_edges_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) static void insert_edge_pairs_as_edges_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->first ().transformed (trans)); ps->insert (s->first ().transformed (trans));
sh->insert (s->second ().transformed (trans)); ps->insert (s->second ().transformed (trans));
} }
} }
static void insert_edge_pairs_as_edges_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans) static void insert_edge_pairs_as_edges_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->first ().transformed (itrans)); ps->insert (s->first ().transformed (itrans));
sh->insert (s->second ().transformed (itrans)); ps->insert (s->second ().transformed (itrans));
} }
} }
static void insert_edge_pairs (db::Shapes *sh, const db::EdgePairs &r) static void insert_edge_pairs (db::Shapes *sh, const db::EdgePairs &r)
{ {
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (*s); ps->insert (*s);
} }
} }
static void insert_edge_pairs_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) static void insert_edge_pairs_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (trans)); ps->insert (s->transformed (trans));
} }
} }
static void insert_edge_pairs_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans) static void insert_edge_pairs_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (itrans)); ps->insert (s->transformed (itrans));
} }
} }
static void insert_texts (db::Shapes *sh, const db::Texts &r) static void insert_texts (db::Shapes *sh, const db::Texts &r)
{ {
ProtectedShapes ps (sh, r);
for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (*s); ps->insert (*s);
} }
} }
static void insert_texts_with_trans (db::Shapes *sh, const db::Texts &r, const db::ICplxTrans &trans) static void insert_texts_with_trans (db::Shapes *sh, const db::Texts &r, const db::ICplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (trans)); ps->insert (s->transformed (trans));
} }
} }
static void insert_texts_with_dtrans (db::Shapes *sh, const db::Texts &r, const db::DCplxTrans &trans) static void insert_texts_with_dtrans (db::Shapes *sh, const db::Texts &r, const db::DCplxTrans &trans)
{ {
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) { for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->transformed (itrans)); ps->insert (s->transformed (itrans));
} }
} }

View File

@ -345,3 +345,35 @@ TEST(5)
CHECKPOINT(); CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au5.oas", db::WriteOAS); db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au5.oas", db::WriteOAS);
} }
// issue #2087
TEST(6)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/fill_tool6.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second;
db::cell_index_type top_cell = ly.cell_by_name ("TOP").second;
unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0));
db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer));
db::Region remaining_polygons;
db::Vector rs (2500, 0);
db::Vector cs (650, 2500);
db::Box fc_box = ly.cell (fill_cell).bbox ();
db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), false, &remaining_polygons);
unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0));
remaining_polygons.insert_into (&ly, top_cell, l100);
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au6.oas", db::WriteOAS);
}

View File

@ -5460,8 +5460,17 @@ CODE
# as the reference point. The reference point will also defined the footprint of the fill cell - more precisely # as the reference point. The reference point will also defined the footprint of the fill cell - more precisely
# the lower left corner. When step vectors are given, the fill cell's footprint is taken to be a rectangle # the lower left corner. When step vectors are given, the fill cell's footprint is taken to be a rectangle
# having the horizontal and vertical step pitch for width and height respectively. This way the fill cells # having the horizontal and vertical step pitch for width and height respectively. This way the fill cells
# will be arrange seamlessly. However, the cell's dimensions can be changed, so that the fill cells # will be arrange seamlessly.
#
# However, the cell's dimensions can be changed, so that the fill cells
# can overlap or there is a space between the cells. To change the dimensions use the "dim" method. # can overlap or there is a space between the cells. To change the dimensions use the "dim" method.
# This example will use a fill cell footprint of 1x1 micrometers, regardless of the step pitch:
#
# @code
# p = fill_pattern("FILL_CELL")
# p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
# p.dim(1.0, 1.0)
# @/code
# #
# The following example specifies a fill cell with an active area of -0.5 .. 1.5 in both directions # The following example specifies a fill cell with an active area of -0.5 .. 1.5 in both directions
# (2 micron width and height). With these dimensions the fill cell's footprint is independent of the # (2 micron width and height). With these dimensions the fill cell's footprint is independent of the
@ -5474,6 +5483,18 @@ CODE
# p.dim(2.0, 2.0) # p.dim(2.0, 2.0)
# @/code # @/code
# #
# Finally, the fill cell can be given a margin: this is a space around the fill cell which needs
# to be inside the fill region. Hence, the margin can be used to implement a distance, the fill
# cells (more precisely: their footprints) will maintain to the outside border of the fill region.
# The following example implements a margin of 200 nm in horizontal and 250 nm in vertical direction:
#
# @code
# p = fill_pattern("FILL_CELL")
# p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
# p.dim(1.0, 1.0)
# p.margin(0.2, 0.25)
# @/code
#
# With these ingredients will can use the fill function. The first example fills the polygons # With these ingredients will can use the fill function. The first example fills the polygons
# of "to_fill" with an orthogonal pattern of 1x1 micron rectangles with a pitch of 2 microns: # of "to_fill" with an orthogonal pattern of 1x1 micron rectangles with a pitch of 2 microns:
# #
@ -5574,6 +5595,7 @@ CODE
fill_cell = pattern.create_cell(@engine._output_layout, @engine) fill_cell = pattern.create_cell(@engine._output_layout, @engine)
top_cell = @engine._output_cell top_cell = @engine._output_cell
fc_box = dbu_trans * pattern.cell_box(row_step.x, column_step.y) fc_box = dbu_trans * pattern.cell_box(row_step.x, column_step.y)
fill_margin = dbu_trans * pattern.fill_margin
rs = dbu_trans * row_step rs = dbu_trans * row_step
cs = dbu_trans * column_step cs = dbu_trans * column_step
origin = origin ? dbu_trans * origin : nil origin = origin ? dbu_trans * origin : nil
@ -5604,6 +5626,7 @@ CODE
tp.var("rs", rs) tp.var("rs", rs)
tp.var("cs", cs) tp.var("cs", cs)
tp.var("origin", origin) tp.var("origin", origin)
tp.var("fill_margin", fill_margin)
tp.var("fc_index", fc_index) tp.var("fc_index", fc_index)
tp.var("repeat", repeat) tp.var("repeat", repeat)
tp.var("with_left", with_left) tp.var("with_left", with_left)
@ -5616,8 +5639,8 @@ CODE
tile_box = tile_box & tc_box; tile_box = tile_box & tc_box;
var left = with_left ? Region.new : nil; var left = with_left ? Region.new : nil;
repeat ? repeat ?
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, Vector.new, left, _tile.bbox) : (region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, left, _tile.bbox) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, Vector.new, left, _tile.bbox); (region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, fill_margin, left, _tile.bbox);
with_left && _output(#{result_arg}, left) with_left && _output(#{result_arg}, left)
) )
END END
@ -5639,9 +5662,9 @@ END
@engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do @engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do
if repeat if repeat
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, RBA::Vector::new, result) self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, result)
else else
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, RBA::Vector::new, result) self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, fill_margin, result)
end end
end end

View File

@ -335,6 +335,7 @@ module DRC
@shapes = [] @shapes = []
@origin = nil @origin = nil
@dim = nil @dim = nil
@margin = RBA::DVector::new
end end
def create_cell(layout, engine) def create_cell(layout, engine)
@ -350,7 +351,11 @@ module DRC
def cell_box(def_w, def_h) def cell_box(def_w, def_h)
o = @origin || self._computed_origin o = @origin || self._computed_origin
d = @dim || RBA::DVector::new(def_w, def_h) d = @dim || RBA::DVector::new(def_w, def_h)
RBA::DBox::new(o, o + d) RBA::DBox::new(o, o + d).enlarged(@margin)
end
def fill_margin
-@margin
end end
def default_xpitch def default_xpitch
@ -454,6 +459,20 @@ module DRC
end end
def margin(w, h)
if !w.is_a?(1.class) && !w.is_a?(1.0.class)
raise("w argument not numeric FillCell#dim")
end
if !h.is_a?(1.class) && !h.is_a?(1.0.class)
raise("h argument not numeric FillCell#dim")
end
@margin = RBA::DVector::new(w, h)
self
end
end end
# A wrapper for the fill step definition # A wrapper for the fill step definition

View File

@ -0,0 +1,70 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2025 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 "tlUnitTest.h"
#include "dbReader.h"
#include "dbTestSupport.h"
#include "lymMacro.h"
TEST(1_IHPMetal1Fill)
{
test_is_long_runner ();
std::string rs = tl::testdata ();
rs += "/drc/drcFullTest_1.drc";
std::string input = tl::testdata ();
input += "/drc/drcFullTest_1.oas";
std::string au = tl::testdata ();
au += "/drc/drcFullTest_au1.oas";
std::string output = this->tmp_file ("tmp.oas");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_force_gc = true\n"
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}

View File

@ -10,6 +10,7 @@ SOURCES = \
drcBasicTests.cc \ drcBasicTests.cc \
drcGenericTests.cc \ drcGenericTests.cc \
drcSimpleTests.cc \ drcSimpleTests.cc \
drcFullTests.cc \
drcSuiteTests.cc \ drcSuiteTests.cc \
INCLUDEPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC INCLUDEPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC

View File

@ -836,6 +836,7 @@ CIFReader::do_read (db::Layout &layout)
db::LayoutLocker locker (&layout); db::LayoutLocker locker (&layout);
double sf = 0.01 / m_dbu; double sf = 0.01 / m_dbu;
check_dbu (m_dbu);
layout.dbu (m_dbu); layout.dbu (m_dbu);
m_cellname = "{CIF top level}"; m_cellname = "{CIF top level}";

View File

@ -350,6 +350,7 @@ DXFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
db::cell_index_type top = layout.add_cell("TOP"); // TODO: make variable .. db::cell_index_type top = layout.add_cell("TOP"); // TODO: make variable ..
check_dbu (m_dbu);
layout.dbu (m_dbu); layout.dbu (m_dbu);
do_read (layout, top); do_read (layout, top);
cleanup (layout, top); cleanup (layout, top);

View File

@ -236,6 +236,7 @@ GDS2ReaderBase::do_read (db::Layout &layout)
m_dbuu = dbuu; m_dbuu = dbuu;
m_dbu = dbum * 1e6; /*in micron*/ m_dbu = dbum * 1e6; /*in micron*/
check_dbu (m_dbu);
layout.dbu (m_dbu); layout.dbu (m_dbu);
} else { } else {

View File

@ -30,6 +30,8 @@
namespace db namespace db
{ {
static const std::pair<db::Coord, db::Coord> ext_not_set = std::make_pair (std::numeric_limits<db::Coord>::min (), std::numeric_limits<db::Coord>::min ());
struct DEFImporterGroup struct DEFImporterGroup
{ {
DEFImporterGroup (const std::string &n, const std::string &rn, const std::vector<tl::GlobPattern> &m) DEFImporterGroup (const std::string &n, const std::string &rn, const std::vector<tl::GlobPattern> &m)
@ -133,7 +135,7 @@ DEFImporter::get_def_ext (const std::string & /*ln*/, const std::pair<db::Coord,
return std::make_pair (de, de); return std::make_pair (de, de);
#else #else
// This implementation follows the LEFDEF 5.8 spec saying the "default extension is half the wire width": // This implementation follows the LEFDEF 5.8 spec saying the "default extension is half the wire width":
db::Coord de = std::min (wxy.first, wxy.second) / 2; auto de = std::min (wxy.second, wxy.first) / 2;
return std::make_pair (de, de); return std::make_pair (de, de);
#endif #endif
} }
@ -355,28 +357,46 @@ DEFImporter::produce_routing_geometry (db::Cell &design, const Polygon *style, u
bool was_path_before = false; bool was_path_before = false;
std::vector<db::Point>::const_iterator pt = pts.begin (); std::vector<db::Point>::const_iterator pt = pts.begin ();
std::vector<std::pair<db::Coord, db::Coord> >::const_iterator ex = ext.begin ();
while (pt != pts.end ()) { while (pt != pts.end ()) {
std::vector<db::Point>::const_iterator pt0 = pt; auto pt0 = pt;
auto ex0 = ex;
++pt; ++pt;
++ex;
if (pt == pts.end ()) { if (pt == pts.end ()) {
break; break;
} }
bool multipart = false; bool multipart = false;
if (is_isotropic) { if (is_isotropic) {
while (pt != pts.end () && (pt[-1].x () == pt[0].x () || pt[-1].y () == pt[0].y())) { while (pt != pts.end ()) {
if (! (pt[-1].x () == pt[0].x () || pt[-1].y () == pt[0].y())) {
// non-orthogonal segments are treated otherwise, not by paths
break;
}
if (pt + 1 != pts.end () && ex[0] != ext_not_set) {
// connection points feature non-default extensions and should not be represented by paths
break;
}
++pt; ++pt;
++ex;
multipart = true; multipart = true;
} }
if (multipart) { if (multipart) {
--pt; --pt;
--ex;
} }
} }
// The next part is the interval [pt0..pt] (pt inclusive) // The next part is the interval [pt0..pt] (including pt)
if (multipart || (pt0->x () == pt0[1].x () || pt0->y () == pt0[1].y())) { if (! multipart && (pt0->x () == pt0[1].x () && pt0->y () == pt0[1].y())) {
// ignore single-point paths
} else if (multipart || (pt0->x () == pt0[1].x () || pt0->y () == pt0[1].y())) {
db::Coord wxy, wxy_perp; db::Coord wxy, wxy_perp;
@ -388,33 +408,27 @@ DEFImporter::produce_routing_geometry (db::Cell &design, const Polygon *style, u
wxy_perp = w.second; wxy_perp = w.second;
} }
// compute begin extension // compute begin and end extensions
db::Coord be = 0; db::Coord be = 0;
if (pt0 == pts.begin ()) { if (*ex0 != ext_not_set) {
if (pt0->x () == pt0 [1].x ()) { be = (pt0->x () == pt0 [1].x ()) ? ex0->second : ex0->first;
be = ext.front ().second;
} else {
be = ext.front ().first;
}
} else if (was_path_before) { } else if (was_path_before) {
// provides the overlap to the previous segment // provides the overlap to the previous segment
be = wxy_perp / 2; be = wxy_perp / 2;
} }
// compute end extension
db::Coord ee = 0; db::Coord ee = 0;
if (pt + 1 == pts.end ()) { if (*ex != ext_not_set) {
if (pt [-1].x () == pt->x ()) { ee = (pt [-1].x () == pt->x ()) ? ex->second : ex->first;
ee = ext.back ().second;
} else {
ee = ext.back ().first;
}
} }
auto pt_from = pt0; auto pt_from = pt0;
auto pt_to = pt + 1; auto pt_to = pt + 1;
// do not split away end segments if they are shorter than half the width // Pplit paths if "joined_paths" is off. Sorry for spending the effort before to
// compute multipath chains.
// But now we can keep end segments joined if they are shorter than half the width
// to establish a proper path end in that case.
auto pt_from_split = pt_from; auto pt_from_split = pt_from;
auto pt_to_split = pt_to; auto pt_to_split = pt_to;
@ -553,10 +567,6 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
const std::string *rulename = 0; const std::string *rulename = 0;
std::pair<db::Coord, db::Coord> w (0, 0); std::pair<db::Coord, db::Coord> w (0, 0);
if (specialnets) {
db::Coord n = db::coord_traits<db::Coord>::rounded (get_double () * scale);
w = std::make_pair (n, n);
}
const db::Polygon *style = 0; const db::Polygon *style = 0;
@ -564,6 +574,9 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
if (specialnets) { if (specialnets) {
db::Coord n = db::coord_traits<db::Coord>::rounded (get_double () * scale);
w = std::make_pair (n, n);
while (test ("+")) { while (test ("+")) {
if (test ("STYLE")) { if (test ("STYLE")) {
@ -598,11 +611,17 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
rulename = &nondefaultrule; rulename = &nondefaultrule;
} }
std::pair<db::Coord, db::Coord> def_ext (0, 0);
if (! specialnets) { if (! specialnets) {
w = get_wire_width_for_rule (*rulename, ln, layout.dbu ()); w = get_wire_width_for_rule (*rulename, ln, layout.dbu ());
def_ext = get_def_ext (ln, w, layout.dbu ()); }
// default extension for first and last point
std::pair<db::Coord, db::Coord> def_ext (0, 0);
std::pair<db::Coord, db::Coord> def_ext_conn = get_def_ext (ln, w, layout.dbu ());
if (! specialnets) {
// first and last extensions are half width by default for routed nets
def_ext = def_ext_conn;
} }
std::map<int, db::Polygon>::const_iterator s = m_styles.find (sn); std::map<int, db::Polygon>::const_iterator s = m_styles.find (sn);
@ -694,7 +713,7 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
y = get_double (); y = get_double ();
} }
pts.push_back (db::Point (db::DPoint (x * scale, y * scale))); pts.push_back (db::Point (db::DPoint (x * scale, y * scale)));
std::pair<db::Coord, db::Coord> ee = def_ext; std::pair<db::Coord, db::Coord> ee = ext_not_set;
if (! peek (")")) { if (! peek (")")) {
db::Coord e = db::coord_traits<db::Coord>::rounded (get_double () * scale); db::Coord e = db::coord_traits<db::Coord>::rounded (get_double () * scale);
ee.first = ee.second = e; ee.first = ee.second = e;
@ -706,10 +725,20 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
} }
if (pts.size () > 1) { if (pts.size () > 1) {
// replace the default extensions
if (ext.front () == ext_not_set) {
ext.front () = def_ext;
}
if (ext.back () == ext_not_set) {
ext.back () = def_ext;
}
std::set <unsigned int> dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask); std::set <unsigned int> dl = open_layer (layout, ln, specialnets ? SpecialRouting : Routing, mask);
for (std::set<unsigned int>::const_iterator l = dl.begin (); l != dl.end (); ++l) { for (std::set<unsigned int>::const_iterator l = dl.begin (); l != dl.end (); ++l) {
produce_routing_geometry (design, style, *l, prop_id, pts, ext, w); produce_routing_geometry (design, style, *l, prop_id, pts, ext, w);
} }
} }
// continue a segment with the current point and the new mask // continue a segment with the current point and the new mask

View File

@ -119,6 +119,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti
effective_options = *lefdef_options; effective_options = *lefdef_options;
} }
check_dbu (effective_options.dbu ());
layout.dbu (effective_options.dbu ()); layout.dbu (effective_options.dbu ());
std::string base_path; std::string base_path;

View File

@ -432,7 +432,7 @@ TEST(def16)
// (complete example) // (complete example)
db::LEFDEFReaderOptions opt = default_options (); db::LEFDEFReaderOptions opt = default_options ();
opt.set_macro_resolution_mode (1); opt.set_macro_resolution_mode (1);
run_test (_this, "def16", "lef:a.lef+lef:tech.lef+def:a.def", "au_4b.oas.gz", opt); run_test (_this, "def16", "lef:a.lef+lef:tech.lef+def:a.def", "au_4c.oas.gz", opt);
} }
TEST(100) TEST(100)
@ -1153,3 +1153,10 @@ TEST(215_multiDEF)
db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS); db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS);
} }
// issue-2075
TEST(216_line_extensions)
{
run_test (_this, "issue-2075", "map:test.map+lef:test.lef+def:test.def", "au.oas", default_options (), false);
}

View File

@ -98,6 +98,7 @@ MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
top_cell = layout.add_cell (top_cellname.c_str ()); top_cell = layout.add_cell (top_cellname.c_str ());
} }
check_dbu (m_dbu);
layout.dbu (m_dbu); layout.dbu (m_dbu);
m_cells_to_read.clear (); m_cells_to_read.clear ();

View File

@ -51,7 +51,6 @@ namespace db
MALYReader::MALYReader (tl::InputStream &s) MALYReader::MALYReader (tl::InputStream &s)
: m_stream (s), : m_stream (s),
m_progress (tl::to_string (tr ("Reading MALY file")), 1000), m_progress (tl::to_string (tr ("Reading MALY file")), 1000),
m_dbu (0.001),
m_last_record_line (0) m_last_record_line (0)
{ {
m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); m_progress.set_format (tl::to_string (tr ("%.0fk lines")));
@ -89,7 +88,10 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
init (options); init (options);
const db::MALYReaderOptions &specific_options = options.get_options<db::MALYReaderOptions> (); const db::MALYReaderOptions &specific_options = options.get_options<db::MALYReaderOptions> ();
m_dbu = specific_options.dbu; double dbu = specific_options.dbu;
check_dbu (dbu);
layout.dbu (dbu);
set_layer_map (specific_options.layer_map); set_layer_map (specific_options.layer_map);
set_create_layers (specific_options.create_other_layers); set_create_layers (specific_options.create_other_layers);

View File

@ -223,7 +223,6 @@ private:
tl::TextInputStream m_stream; tl::TextInputStream m_stream;
tl::AbsoluteProgress m_progress; tl::AbsoluteProgress m_progress;
double m_dbu;
unsigned int m_last_record_line; unsigned int m_last_record_line;
std::string m_record; std::string m_record;
std::string m_record_returned; std::string m_record_returned;

View File

@ -135,6 +135,7 @@ TEST(10_BasicLayout)
{ {
run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas");
run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_lm_au.oas", "A: 10, B: 11, C: 12, D: 13"); run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_lm_au.oas", "A: 10, B: 11, C: 12, D: 13");
run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_dbu10nm_au.oas", 0, 0.01);
} }
TEST(11_Titles) TEST(11_Titles)

File diff suppressed because it is too large Load Diff

View File

@ -140,14 +140,14 @@ private:
modal_variable<db::cell_index_type> mm_placement_cell; modal_variable<db::cell_index_type> mm_placement_cell;
modal_variable<db::Coord> mm_placement_x; modal_variable<db::Coord> mm_placement_x;
modal_variable<db::Coord> mm_placement_y; modal_variable<db::Coord> mm_placement_y;
modal_variable<unsigned int> mm_layer; modal_variable<uint32_t> mm_layer;
modal_variable<unsigned int> mm_datatype; modal_variable<uint32_t> mm_datatype;
modal_variable<unsigned int> mm_textlayer; modal_variable<uint32_t> mm_textlayer;
modal_variable<unsigned int> mm_texttype; modal_variable<uint32_t> mm_texttype;
modal_variable<db::Coord> mm_text_x; modal_variable<db::Coord> mm_text_x;
modal_variable<db::Coord> mm_text_y; modal_variable<db::Coord> mm_text_y;
modal_variable<std::string> mm_text_string; modal_variable<std::string> mm_text_string;
modal_variable<unsigned int> mm_text_string_id; modal_variable<uint64_t> mm_text_string_id;
modal_variable<db::Coord> mm_geometry_x; modal_variable<db::Coord> mm_geometry_x;
modal_variable<db::Coord> mm_geometry_y; modal_variable<db::Coord> mm_geometry_y;
modal_variable<distance_type> mm_geometry_w; modal_variable<distance_type> mm_geometry_w;
@ -157,17 +157,17 @@ private:
modal_variable<db::Coord> mm_path_start_extension; modal_variable<db::Coord> mm_path_start_extension;
modal_variable<db::Coord> mm_path_end_extension; modal_variable<db::Coord> mm_path_end_extension;
modal_variable< std::vector<db::Point> > mm_path_point_list; modal_variable< std::vector<db::Point> > mm_path_point_list;
modal_variable<unsigned int> mm_ctrapezoid_type; modal_variable<uint32_t> mm_ctrapezoid_type;
modal_variable<distance_type> mm_circle_radius; modal_variable<distance_type> mm_circle_radius;
modal_variable<db::property_names_id_type> mm_last_property_name; modal_variable<db::property_names_id_type> mm_last_property_name;
modal_variable<bool> mm_last_property_is_sprop; modal_variable<bool> mm_last_property_is_sprop;
modal_variable<property_value_list> mm_last_value_list; modal_variable<property_value_list> mm_last_value_list;
std::map <unsigned long, db::properties_id_type> m_cellname_properties; std::map <uint64_t, db::properties_id_type> m_cellname_properties;
std::map <unsigned long, std::string> m_textstrings; std::map <uint64_t, std::string> m_textstrings;
std::map <unsigned long, const db::StringRef *> m_text_forward_references; std::map <uint64_t, const db::StringRef *> m_text_forward_references;
std::map <unsigned long, std::string> m_propstrings; std::map <uint64_t, std::string> m_propstrings;
std::map <unsigned long, std::string> m_propnames; std::map <uint64_t, std::string> m_propnames;
std::map <db::cell_index_type, std::vector<tl::Variant> > m_context_strings_per_cell; std::map <db::cell_index_type, std::vector<tl::Variant> > m_context_strings_per_cell;
@ -178,8 +178,8 @@ private:
bool m_read_properties; bool m_read_properties;
bool m_read_all_properties; bool m_read_all_properties;
std::map <unsigned long, db::property_names_id_type> m_propname_forward_references; std::map <uint64_t, db::property_names_id_type> m_propname_forward_references;
std::map <unsigned long, std::string> m_propvalue_forward_references; std::map <uint64_t, std::string> m_propvalue_forward_references;
std::map <db::properties_id_type, std::set<db::Shapes *> > m_forward_properties_for_shapes; std::map <db::properties_id_type, std::set<db::Shapes *> > m_forward_properties_for_shapes;
std::map <db::properties_id_type, std::set<db::Instances *> > m_forward_properties_for_instances; std::map <db::properties_id_type, std::set<db::Instances *> > m_forward_properties_for_instances;
std::map <db::cell_index_type, db::PropertiesSet> m_future_cell_properties; std::map <db::cell_index_type, db::PropertiesSet> m_future_cell_properties;
@ -234,42 +234,35 @@ private:
} }
} }
long long get_long_long (); int64_t get_int64 ();
unsigned long long get_ulong_long (); uint64_t get_uint64 ();
long get_long (); uint64_t get_uint64_for_divider ();
unsigned long get_ulong (); int32_t get_int32 ();
unsigned long get_ulong_for_divider (); uint32_t get_uint32 ();
int get_int ();
unsigned int get_uint ();
void get (long long &l) void get (int64_t &l)
{ {
l = get_long_long (); l = get_int64 ();
} }
void get (unsigned long long &l) void get_size (size_t &l)
{ {
l = get_ulong_long (); l = get_uint64 ();
} }
void get (long &l) void get (uint64_t &l)
{ {
l = get_long (); l = get_uint64 ();
} }
void get (unsigned long &l) void get (int32_t &l)
{ {
l = get_ulong (); l = get_int32 ();
} }
void get (int &l) void get (uint32_t &l)
{ {
l = get_int (); l = get_uint32 ();
}
void get (unsigned int &l)
{
l = get_uint ();
} }
void get (double &d) void get (double &d)
@ -280,12 +273,12 @@ private:
std::string get_str (); std::string get_str ();
void get_str (std::string &s); void get_str (std::string &s);
double get_real (); double get_real ();
db::Vector get_gdelta (long grid = 1); db::Vector get_gdelta (int64_t grid = 1);
db::Vector get_3delta (long grid = 1); db::Vector get_3delta (int64_t grid = 1);
db::Vector get_2delta (long grid = 1); db::Vector get_2delta (int64_t grid = 1);
db::Coord get_coord (long grid = 1); db::Coord get_coord (int64_t grid = 1);
db::Coord get_ucoord (unsigned long grid = 1); db::Coord get_ucoord (uint64_t grid = 1);
distance_type get_ucoord_as_distance (unsigned long grid = 1); distance_type get_ucoord_as_distance (uint64_t grid = 1);
}; };
} }

View File

@ -26,6 +26,7 @@
#include "tlDeflate.h" #include "tlDeflate.h"
#include "tlMath.h" #include "tlMath.h"
#include "tlUniqueName.h"
#include <math.h> #include <math.h>
@ -411,7 +412,7 @@ Compressor<Obj>::flush (db::OASISWriter *writer)
if (dd != displacements.end ()) { if (dd != displacements.end ()) {
dxy = xrep ? db::Vector (safe_diff (dd->x (), d->x ()), 0) : db::Vector (0, safe_diff (dd->y (), d->y ())); dxy = xrep ? db::Vector (safe_diff (dd->x (), d->x ()), 0) : db::Vector (0, safe_diff (dd->y (), d->y ()));
while (dd != displacements.end () && *dd == dd[-1] + dxy) { while (dd != displacements.end () && *dd == dd[-1] + dxy && nxy < std::numeric_limits<int>::max ()) {
++dd; ++dd;
++nxy; ++nxy;
} }
@ -453,7 +454,7 @@ Compressor<Obj>::flush (db::OASISWriter *writer)
db::Vector dxy = xrep ? db::Vector (safe_diff (dd->x (), d->x ()), 0) : db::Vector (0, safe_diff (dd->y (), d->y ())); db::Vector dxy = xrep ? db::Vector (safe_diff (dd->x (), d->x ()), 0) : db::Vector (0, safe_diff (dd->y (), d->y ()));
int nxy = 2; int nxy = 2;
while (dd != dwindow) { while (dd != dwindow && nxy < std::numeric_limits<int>::max ()) {
disp_vector::iterator df = std::lower_bound (dd + 1, dwindow, *dd + dxy); disp_vector::iterator df = std::lower_bound (dd + 1, dwindow, *dd + dxy);
if (df == dwindow || *df != *dd + dxy) { if (df == dwindow || *df != *dd + dxy) {
break; break;
@ -717,45 +718,17 @@ OASISWriter::write_bytes (const char *b, size_t n)
} }
void void
OASISWriter::write (long long n) OASISWriter::write (int64_t n)
{ {
if (n < 0) { if (n < 0) {
write (((unsigned long long) (-n) << 1) | 1); write (((uint64_t) (-n) << 1) | 1);
} else { } else {
write ((unsigned long long) n << 1); write ((uint64_t) n << 1);
} }
} }
void void
OASISWriter::write (unsigned long long n) OASISWriter::write (uint64_t n)
{
char buffer [50];
char *bptr = buffer;
do {
unsigned char b = n & 0x7f;
n >>= 7;
if (n > 0) {
b |= 0x80;
}
*bptr++ = (char) b;
} while (n > 0);
write_bytes (buffer, bptr - buffer);
}
void
OASISWriter::write (long n)
{
if (n < 0) {
write (((unsigned long) (-n) << 1) | 1);
} else {
write ((unsigned long) n << 1);
}
}
void
OASISWriter::write (unsigned long n)
{ {
char buffer [50]; char buffer [50];
char *bptr = buffer; char *bptr = buffer;
@ -775,15 +748,15 @@ OASISWriter::write (unsigned long n)
void void
OASISWriter::write (float d) OASISWriter::write (float d)
{ {
if (fabs (d) >= 0.5 && fabs (floor (d + 0.5) - d) < 1e-6 && fabs (d) < double (std::numeric_limits<long>::max ())) { if (fabs (d) >= 0.5 && fabs (floor (d + 0.5) - d) < 1e-6 && fabs (d) < double (std::numeric_limits<int64_t>::max ())) {
// whole number (negative or positive) // whole number (negative or positive)
if (d < 0.0) { if (d < 0.0) {
write_byte (1); write_byte (1);
write ((unsigned long) floor (-d + 0.5)); write ((uint64_t) floor (-d + 0.5));
} else { } else {
write_byte (0); write_byte (0);
write ((unsigned long) floor (d + 0.5)); write ((uint64_t) floor (d + 0.5));
} }
} else { } else {
@ -799,7 +772,7 @@ OASISWriter::write (float d)
f2i.d = d; f2i.d = d;
uint32_t i = f2i.i; uint32_t i = f2i.i;
char b[sizeof (f2i.i)]; char b[sizeof (f2i.i)];
for (unsigned int n = 0; n < sizeof (f2i.i); n++) { for (size_t n = 0; n < sizeof (f2i.i); n++) {
b[n] = char (i & 0xff); b[n] = char (i & 0xff);
i >>= 8; i >>= 8;
} }
@ -811,15 +784,15 @@ OASISWriter::write (float d)
void void
OASISWriter::write (double d) OASISWriter::write (double d)
{ {
if (fabs (d) >= 0.5 && fabs (floor (d + 0.5) - d) < 1e-10 && fabs (d) < double (std::numeric_limits<long>::max ())) { if (fabs (d) >= 0.5 && fabs (floor (d + 0.5) - d) < 1e-10 && fabs (d) < double (std::numeric_limits<int64_t>::max ())) {
// whole number (negative or positive) // whole number (negative or positive)
if (d < 0.0) { if (d < 0.0) {
write_byte (1); write_byte (1);
write ((unsigned long) floor (-d + 0.5)); write ((uint64_t) floor (-d + 0.5));
} else { } else {
write_byte (0); write_byte (0);
write ((unsigned long) floor (d + 0.5)); write ((uint64_t) floor (d + 0.5));
} }
} else { } else {
@ -848,7 +821,7 @@ void
OASISWriter::write_bstring (const char *s) OASISWriter::write_bstring (const char *s)
{ {
size_t l = strlen (s); size_t l = strlen (s);
write (l); write ((uint64_t) l);
write_bytes (s, l); write_bytes (s, l);
} }
@ -867,7 +840,7 @@ void
OASISWriter::write_astring (const char *s) OASISWriter::write_astring (const char *s)
{ {
std::string nstr = make_astring (s); std::string nstr = make_astring (s);
write (nstr.size ()); write ((uint64_t) nstr.size ());
write_bytes (nstr.c_str (), nstr.size ()); write_bytes (nstr.c_str (), nstr.size ());
} }
@ -886,7 +859,7 @@ void
OASISWriter::write_nstring (const char *s) OASISWriter::write_nstring (const char *s)
{ {
std::string nstr = make_nstring (s); std::string nstr = make_nstring (s);
write (nstr.size ()); write ((uint64_t) nstr.size ());
write_bytes (nstr.c_str (), nstr.size ()); write_bytes (nstr.c_str (), nstr.size ());
} }
@ -903,8 +876,8 @@ OASISWriter::write_gdelta (const db::Vector &p, double sf)
if (x == -y || x == y || x == 0 || y == 0) { if (x == -y || x == y || x == 0 || y == 0) {
unsigned long long dir = 0; uint64_t dir = 0;
unsigned long long l = 0; uint64_t l = 0;
if (x > 0) { if (x > 0) {
l = x; l = x;
@ -938,11 +911,11 @@ OASISWriter::write_gdelta (const db::Vector &p, double sf)
} else { } else {
unsigned long long d; uint64_t d;
if (x < 0) { if (x < 0) {
d = ((unsigned long long) -x << 2) | 3; d = ((uint64_t) -x << 2) | 3;
} else { } else {
d = ((unsigned long long) x << 2) | 1; d = ((uint64_t) x << 2) | 1;
} }
write (d); write (d);
write (y); write (y);
@ -1090,8 +1063,8 @@ OASISWriter::end_cblock ()
// RFC1951 compression: // RFC1951 compression:
write_byte (0); write_byte (0);
write (m_cblock_buffer.size ()); write ((uint64_t) m_cblock_buffer.size ());
write (m_cblock_compressed.size ()); write ((uint64_t) m_cblock_compressed.size ());
write_bytes (m_cblock_compressed.data (), m_cblock_compressed.size ()); write_bytes (m_cblock_compressed.data (), m_cblock_compressed.size ());
@ -1158,7 +1131,7 @@ OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vecto
{ {
// write the property names collected so far in the order of the ID's. // write the property names collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, std::string> > rev_pn; std::vector<std::pair<uint64_t, std::string> > rev_pn;
rev_pn.reserve (m_propnames.size ()); rev_pn.reserve (m_propnames.size ());
for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) { for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) {
rev_pn.push_back (std::make_pair (p->second, p->first)); rev_pn.push_back (std::make_pair (p->second, p->first));
@ -1166,7 +1139,7 @@ OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vecto
std::sort (rev_pn.begin (), rev_pn.end ()); std::sort (rev_pn.begin (), rev_pn.end ());
for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) { for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); tl_assert (p->first == (uint64_t)(p - rev_pn.begin ()));
begin_table (propnames_table_pos); begin_table (propnames_table_pos);
write_record_id (7); write_record_id (7);
write_nstring (p->second.c_str ()); write_nstring (p->second.c_str ());
@ -1236,7 +1209,7 @@ OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::v
{ {
// write the property strings collected so far in the order of the ID's. // write the property strings collected so far in the order of the ID's.
std::vector<std::pair<unsigned long, const std::string *> > rev_ps; std::vector<std::pair<uint64_t, const std::string *> > rev_ps;
rev_ps.reserve (m_propstrings.size ()); rev_ps.reserve (m_propstrings.size ());
for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
rev_ps.push_back (std::make_pair (p->second, &p->first)); rev_ps.push_back (std::make_pair (p->second, &p->first));
@ -1246,7 +1219,7 @@ OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::v
tl_assert (rev_ps.size () == size_t (m_propstring_id)); tl_assert (rev_ps.size () == size_t (m_propstring_id));
for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) { for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) {
tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); tl_assert (p->first == (uint64_t)(p - rev_ps.begin ()));
begin_table (propstrings_table_pos); begin_table (propstrings_table_pos);
write_record_id (9); write_record_id (9);
write_bstring (p->second->c_str ()); write_bstring (p->second->c_str ());
@ -1334,9 +1307,9 @@ OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vecto
begin_table (cellnames_table_pos); begin_table (cellnames_table_pos);
write_record_id (sequential ? 3 : 4); write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell)); write_nstring (cell_nstring (*cell));
if (! sequential) { if (! sequential) {
write ((unsigned long) *cell); write ((uint64_t) *cell);
} }
if (m_options.write_std_properties >= 1) { if (m_options.write_std_properties >= 1) {
@ -1391,7 +1364,7 @@ OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::v
// write present text strings // write present text strings
// collect present strings by ID // collect present strings by ID
std::vector<std::pair<unsigned long, const std::string *> > rev_ts; std::vector<std::pair<uint64_t, const std::string *> > rev_ts;
rev_ts.reserve (m_textstrings.size ()); rev_ts.reserve (m_textstrings.size ());
for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) { for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) {
rev_ts.push_back (std::make_pair (p->second, &p->first)); rev_ts.push_back (std::make_pair (p->second, &p->first));
@ -1401,7 +1374,7 @@ OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::v
tl_assert (rev_ts.size () == size_t (m_textstring_id)); tl_assert (rev_ts.size () == size_t (m_textstring_id));
for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) { for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) {
tl_assert (t->first == (unsigned long)(t - rev_ts.begin ())); tl_assert (t->first == (uint64_t)(t - rev_ts.begin ()));
begin_table (textstrings_table_pos); begin_table (textstrings_table_pos);
write_record_id (5); write_record_id (5);
write_nstring (t->second->c_str ()); write_nstring (t->second->c_str ());
@ -1444,16 +1417,16 @@ OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vec
write_record_id (11); write_record_id (11);
write_nstring (l->second.name.c_str ()); write_nstring (l->second.name.c_str ());
write_byte (3); write_byte (3);
write ((unsigned long) l->second.layer); write ((uint64_t) l->second.layer);
write_byte (3); write_byte (3);
write ((unsigned long) l->second.datatype); write ((uint64_t) l->second.datatype);
write_record_id (12); write_record_id (12);
write_nstring (l->second.name.c_str ()); write_nstring (l->second.name.c_str ());
write_byte (3); write_byte (3);
write ((unsigned long) l->second.layer); write ((uint64_t) l->second.layer);
write_byte (3); write_byte (3);
write ((unsigned long) l->second.datatype); write ((uint64_t) l->second.datatype);
m_progress.set (mp_stream->pos ()); m_progress.set (mp_stream->pos ());
@ -1476,6 +1449,31 @@ static bool skip_cell_body (const db::Cell &cref)
return cref.is_ghost_cell () && cref.empty (); return cref.is_ghost_cell () && cref.empty ();
} }
void
OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set <db::cell_index_type> &cell_set)
{
m_cell_nstrings.clear ();
std::set<std::string> names;
for (auto c = cell_set.begin (); c != cell_set.end (); ++c) {
std::string cn = make_nstring (layout.cell_name (*c));
cn = tl::unique_name (cn, names);
m_cell_nstrings.insert (std::make_pair (*c, cn));
names.insert (cn);
}
}
const char *
OASISWriter::cell_nstring (db::cell_index_type cell_index)
{
auto n = m_cell_nstrings.find (cell_index);
tl_assert (n != m_cell_nstrings.end ());
return n->second.c_str ();
}
void void
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
@ -1510,6 +1508,8 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
std::set <db::cell_index_type> cell_set; std::set <db::cell_index_type> cell_set;
options.get_cells (layout, cell_set, layers); options.get_cells (layout, cell_set, layers);
create_cell_nstrings (layout, cell_set);
// create a cell index vector sorted bottom-up // create a cell index vector sorted bottom-up
std::vector <db::cell_index_type> cells, cells_by_index; std::vector <db::cell_index_type> cells, cells_by_index;
@ -1602,7 +1602,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
is_top = (cell_set.find (*p) == cell_set.end ()); is_top = (cell_set.find (*p) == cell_set.end ());
} }
if (is_top) { if (is_top) {
write_property_def (s_top_cell_name, tl::Variant (make_nstring (layout.cell_name (*cell))), true); write_property_def (s_top_cell_name, tl::Variant (cell_nstring (*cell)), true);
} }
} }
@ -1679,7 +1679,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
cell_positions.insert (std::make_pair (*cell, mp_stream->pos ())); cell_positions.insert (std::make_pair (*cell, mp_stream->pos ()));
write_record_id (13); // CELL write_record_id (13); // CELL
write ((unsigned long) *cell); write ((uint64_t) *cell);
reset_modal_variables (); reset_modal_variables ();
@ -1696,8 +1696,8 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_record_id (28); write_record_id (28);
write_byte (char (0xf6)); write_byte (char (0xf6));
unsigned long pnid = 0; uint64_t pnid = 0;
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name); std::map <std::string, uint64_t>::const_iterator pni = m_propnames.find (klayout_context_name);
if (pni == m_propnames.end ()) { if (pni == m_propnames.end ()) {
pnid = m_propname_id++; pnid = m_propname_id++;
m_propnames.insert (std::make_pair (klayout_context_name, pnid)); m_propnames.insert (std::make_pair (klayout_context_name, pnid));
@ -1706,12 +1706,12 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
} }
write (pnid); write (pnid);
write ((unsigned long) context_prop_strings.size ()); write ((uint64_t) context_prop_strings.size ());
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
write_byte (14); // b-string by reference number write_byte (14); // b-string by reference number
unsigned long psid = 0; uint64_t psid = 0;
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c); std::map <std::string, uint64_t>::const_iterator psi = m_propstrings.find (*c);
if (psi == m_propstrings.end ()) { if (psi == m_propstrings.end ()) {
psid = m_propstring_id++; psid = m_propstring_id++;
m_propstrings.insert (std::make_pair (*c, psid)).second; m_propstrings.insert (std::make_pair (*c, psid)).second;
@ -1795,23 +1795,23 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
// cellnames // cellnames
write_byte (1); write_byte (1);
write (cellnames_table_pos); write ((uint64_t) cellnames_table_pos);
// textstrings // textstrings
write_byte (1); write_byte (1);
write (textstrings_table_pos); write ((uint64_t) textstrings_table_pos);
// propnames // propnames
write_byte (1); write_byte (1);
write (propnames_table_pos); write ((uint64_t) propnames_table_pos);
// propstrings // propstrings
write_byte (1); write_byte (1);
write (propstrings_table_pos); write ((uint64_t) propstrings_table_pos);
// layernames // layernames
write_byte (1); write_byte (1);
write (layernames_table_pos); write ((uint64_t) layernames_table_pos);
// xnames (not used) // xnames (not used)
write_byte (1); write_byte (1);
@ -1875,11 +1875,11 @@ OASISWriter::write (const Repetition &rep)
if (g <= 1) { if (g <= 1) {
write_byte (10); write_byte (10);
write (iterated->size () - 1); write ((uint64_t) iterated->size () - 1);
g = 1; g = 1;
} else { } else {
write_byte (11); write_byte (11);
write (iterated->size () - 1); write ((uint64_t) iterated->size () - 1);
write_ucoord (g, 1.0); write_ucoord (g, 1.0);
} }
@ -1909,39 +1909,39 @@ OASISWriter::write (const Repetition &rep)
if (b.x () == 0 && b.y () >= 0) { if (b.x () == 0 && b.y () >= 0) {
write_byte (3); write_byte (3);
write (bmax - 2); write ((uint64_t) bmax - 2);
write_ucoord (b.y ()); write_ucoord (b.y ());
} else if (b.y () == 0 && b.x () >= 0) { } else if (b.y () == 0 && b.x () >= 0) {
write_byte (2); write_byte (2);
write (bmax - 2); write ((uint64_t) bmax - 2);
write_ucoord (b.x ()); write_ucoord (b.x ());
} else { } else {
write_byte (9); write_byte (9);
write (bmax - 2); write ((uint64_t) bmax - 2);
write_gdelta (b); write_gdelta (b);
} }
} else if (b.x () == 0 && b.y () >= 0 && a.y () == 0 && a.x () >= 0) { } else if (b.x () == 0 && b.y () >= 0 && a.y () == 0 && a.x () >= 0) {
write_byte (1); write_byte (1);
write (amax - 2); write ((uint64_t) amax - 2);
write (bmax - 2); write ((uint64_t) bmax - 2);
write_ucoord (a.x ()); write_ucoord (a.x ());
write_ucoord (b.y ()); write_ucoord (b.y ());
} else if (b.y () == 0 && b.x () >= 0 && a.x () == 0 && a.y () >= 0) { } else if (b.y () == 0 && b.x () >= 0 && a.x () == 0 && a.y () >= 0) {
write_byte (1); write_byte (1);
write (bmax - 2); write ((uint64_t) bmax - 2);
write (amax - 2); write ((uint64_t) amax - 2);
write_ucoord (b.x ()); write_ucoord (b.x ());
write_ucoord (a.y ()); write_ucoord (a.y ());
} else { } else {
write_byte (8); write_byte (8);
write (amax - 2); write ((uint64_t) amax - 2);
write (bmax - 2); write ((uint64_t) bmax - 2);
write_gdelta (a); write_gdelta (a);
write_gdelta (b); write_gdelta (b);
@ -1985,7 +1985,7 @@ OASISWriter::write_inst_with_rep (const db::CellInstArray &inst, db::properties_
if (info & 0x80) { if (info & 0x80) {
mm_placement_cell = inst.object ().cell_index (); mm_placement_cell = inst.object ().cell_index ();
write ((unsigned long) mm_placement_cell.get ()); write ((uint64_t) mm_placement_cell.get ());
} }
if (inst.is_complex ()) { if (inst.is_complex ()) {
@ -2191,7 +2191,7 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
if (! same_name) { if (! same_name) {
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (name_str); std::map <std::string, uint64_t>::const_iterator pni = m_propnames.find (name_str);
// In strict mode always write property ID's: before we have issued the table we can // In strict mode always write property ID's: before we have issued the table we can
// create new ID's. // create new ID's.
@ -2219,11 +2219,11 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
if (! same_value) { if (! same_value) {
if (pvl.size () >= 15) { if (pvl.size () >= 15) {
write ((unsigned long) pvl.size ()); write ((uint64_t) pvl.size ());
} }
// write property values // write property values
for (unsigned long i = 0; i < pvl.size (); ++i) { for (uint64_t i = 0; i < pvl.size (); ++i) {
const tl::Variant &v = pvl[i]; const tl::Variant &v = pvl[i];
@ -2234,27 +2234,27 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
} else if (v.is_longlong ()) { } else if (v.is_longlong ()) {
write_byte (9); write_byte (9);
write (v.to_longlong ()); write ((int64_t) v.to_longlong ());
} else if (v.is_ulonglong ()) { } else if (v.is_ulonglong ()) {
write_byte (8); write_byte (8);
write (v.to_ulonglong ()); write ((uint64_t) v.to_ulonglong ());
} else if (v.is_long ()) { } else if (v.is_long ()) {
write_byte (9); write_byte (9);
write (v.to_long ()); write ((int64_t) v.to_long ());
} else if (v.is_ulong ()) { } else if (v.is_ulong ()) {
write_byte (8); write_byte (8);
write (v.to_ulong ()); write ((uint64_t) v.to_ulong ());
} else { } else {
const char *pvs = v.to_string (); const char *pvs = v.to_string ();
std::map <std::string, unsigned long>::const_iterator pvi = m_propstrings.find (pvs); std::map <std::string, uint64_t>::const_iterator pvi = m_propstrings.find (pvs);
// In strict mode always write property string ID's: before we have issued the table we can // In strict mode always write property string ID's: before we have issued the table we can
// create new ID's. // create new ID's.
@ -2335,7 +2335,7 @@ OASISWriter::write_pointlist (const std::vector<db::Vector> &pointlist, bool for
// manhattan pointlist // manhattan pointlist
write_byte (type); write_byte (type);
size_t implicit = for_polygons ? 1 : 0; size_t implicit = for_polygons ? 1 : 0;
write ((unsigned long) (pointlist.size () - implicit)); write ((uint64_t) (pointlist.size () - implicit));
db::Vector plast (0, 0); db::Vector plast (0, 0);
for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end () - implicit; ++p) { for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end () - implicit; ++p) {
@ -2353,7 +2353,7 @@ OASISWriter::write_pointlist (const std::vector<db::Vector> &pointlist, bool for
// generic pointlist // generic pointlist
write_byte (4); write_byte (4);
write ((unsigned long) pointlist.size ()); write ((uint64_t) pointlist.size ());
db::Vector plast (0, 0); db::Vector plast (0, 0);
if (m_sf == 1.0) { if (m_sf == 1.0) {
for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end (); ++p) { for (std::vector<db::Vector>::const_iterator p = pointlist.begin (); p != pointlist.end (); ++p) {
@ -2378,8 +2378,8 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const
db::Trans trans = text.trans (); db::Trans trans = text.trans ();
unsigned long text_id = 0; uint64_t text_id = 0;
std::map <std::string, unsigned long>::const_iterator ts = m_textstrings.find (text.string ()); std::map <std::string, uint64_t>::const_iterator ts = m_textstrings.find (text.string ());
if (ts == m_textstrings.end ()) { if (ts == m_textstrings.end ()) {
text_id = m_textstring_id++; text_id = m_textstring_id++;
m_textstrings.insert (std::make_pair (text.string (), text_id)); m_textstrings.insert (std::make_pair (text.string (), text_id));
@ -2412,15 +2412,15 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const
write_byte (info); write_byte (info);
if (info & 0x40) { if (info & 0x40) {
mm_text_string = text.string (); mm_text_string = text.string ();
write ((unsigned long) text_id); write ((uint64_t) text_id);
} }
if (info & 0x01) { if (info & 0x01) {
mm_textlayer = m_layer; mm_textlayer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_texttype = m_datatype; mm_texttype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
if (info & 0x10) { if (info & 0x10) {
mm_text_x = trans.disp ().x (); mm_text_x = trans.disp ().x ();
@ -2496,11 +2496,11 @@ OASISWriter::write (const db::SimplePolygon &polygon, db::properties_id_type pro
if (info & 0x01) { if (info & 0x01) {
mm_layer = m_layer; mm_layer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_datatype = m_datatype; mm_datatype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
if (info & 0x20) { if (info & 0x20) {
mm_polygon_point_list.swap (m_pointlist); mm_polygon_point_list.swap (m_pointlist);
@ -2597,11 +2597,11 @@ OASISWriter::write (const db::Polygon &polygon, db::properties_id_type prop_id,
if (info & 0x01) { if (info & 0x01) {
mm_layer = m_layer; mm_layer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_datatype = m_datatype; mm_datatype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
if (info & 0x20) { if (info & 0x20) {
mm_polygon_point_list.swap (m_pointlist); mm_polygon_point_list.swap (m_pointlist);
@ -2667,11 +2667,11 @@ OASISWriter::write (const db::Box &box, db::properties_id_type prop_id, const db
if (info & 0x01) { if (info & 0x01) {
mm_layer = m_layer; mm_layer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_datatype = m_datatype; mm_datatype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
mm_geometry_w = box.width (); mm_geometry_w = box.width ();
@ -2772,11 +2772,11 @@ OASISWriter::write (const db::Path &path, db::properties_id_type prop_id, const
if (info & 0x01) { if (info & 0x01) {
mm_layer = m_layer; mm_layer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_datatype = m_datatype; mm_datatype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
if (info & 0x20) { if (info & 0x20) {
mm_circle_radius = hw; mm_circle_radius = hw;
@ -2853,11 +2853,11 @@ OASISWriter::write (const db::Path &path, db::properties_id_type prop_id, const
if (info & 0x01) { if (info & 0x01) {
mm_layer = m_layer; mm_layer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_datatype = m_datatype; mm_datatype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
if (info & 0x40) { if (info & 0x40) {
mm_path_halfwidth = hw; mm_path_halfwidth = hw;
@ -3046,11 +3046,11 @@ OASISWriter::write (const db::Edge &edge, db::properties_id_type prop_id, const
if (info & 0x01) { if (info & 0x01) {
mm_layer = m_layer; mm_layer = m_layer;
write ((unsigned long) m_layer); write ((uint64_t) m_layer);
} }
if (info & 0x02) { if (info & 0x02) {
mm_datatype = m_datatype; mm_datatype = m_datatype;
write ((unsigned long) m_datatype); write ((uint64_t) m_datatype);
} }
if (info & 0x40) { if (info & 0x40) {
mm_path_halfwidth = 0; mm_path_halfwidth = 0;

View File

@ -208,14 +208,15 @@ private:
tl::OutputMemoryStream m_cblock_buffer; tl::OutputMemoryStream m_cblock_buffer;
tl::OutputMemoryStream m_cblock_compressed; tl::OutputMemoryStream m_cblock_compressed;
bool m_in_cblock; bool m_in_cblock;
unsigned long m_propname_id; uint64_t m_propname_id;
unsigned long m_propstring_id; uint64_t m_propstring_id;
unsigned long m_textstring_id; uint64_t m_textstring_id;
bool m_proptables_written; bool m_proptables_written;
std::map <std::string, unsigned long> m_textstrings; std::map <std::string, uint64_t> m_textstrings;
std::map <std::string, unsigned long> m_propnames; std::map <std::string, uint64_t> m_propnames;
std::map <std::string, unsigned long> m_propstrings; std::map <std::string, uint64_t> m_propstrings;
std::map <db::cell_index_type, std::string> m_cell_nstrings;
typedef std::vector<tl::Variant> property_value_list; typedef std::vector<tl::Variant> property_value_list;
@ -223,23 +224,23 @@ private:
modal_variable<db::cell_index_type> mm_placement_cell; modal_variable<db::cell_index_type> mm_placement_cell;
modal_variable<db::Coord> mm_placement_x; modal_variable<db::Coord> mm_placement_x;
modal_variable<db::Coord> mm_placement_y; modal_variable<db::Coord> mm_placement_y;
modal_variable<unsigned int> mm_layer; modal_variable<uint32_t> mm_layer;
modal_variable<unsigned int> mm_datatype; modal_variable<uint32_t> mm_datatype;
modal_variable<unsigned int> mm_textlayer; modal_variable<uint32_t> mm_textlayer;
modal_variable<unsigned int> mm_texttype; modal_variable<uint32_t> mm_texttype;
modal_variable<db::Coord> mm_text_x; modal_variable<db::Coord> mm_text_x;
modal_variable<db::Coord> mm_text_y; modal_variable<db::Coord> mm_text_y;
modal_variable<std::string> mm_text_string; modal_variable<std::string> mm_text_string;
modal_variable<db::Coord> mm_geometry_x; modal_variable<db::Coord> mm_geometry_x;
modal_variable<db::Coord> mm_geometry_y; modal_variable<db::Coord> mm_geometry_y;
modal_variable<db::Coord> mm_geometry_w; modal_variable<db::coord_traits<db::Coord>::distance_type> mm_geometry_w;
modal_variable<db::Coord> mm_geometry_h; modal_variable<db::coord_traits<db::Coord>::distance_type> mm_geometry_h;
modal_variable< std::vector<db::Vector> > mm_polygon_point_list; modal_variable< std::vector<db::Vector> > mm_polygon_point_list;
modal_variable<db::Coord> mm_path_halfwidth; modal_variable<db::Coord> mm_path_halfwidth;
modal_variable<db::Coord> mm_path_start_extension; modal_variable<db::Coord> mm_path_start_extension;
modal_variable<db::Coord> mm_path_end_extension; modal_variable<db::Coord> mm_path_end_extension;
modal_variable< std::vector<db::Vector> > mm_path_point_list; modal_variable< std::vector<db::Vector> > mm_path_point_list;
modal_variable<unsigned int> mm_ctrapezoid_type; modal_variable<uint32_t> mm_ctrapezoid_type;
modal_variable<db::Coord> mm_circle_radius; modal_variable<db::Coord> mm_circle_radius;
modal_variable<std::string> mm_last_property_name; modal_variable<std::string> mm_last_property_name;
modal_variable<bool> mm_last_property_is_sprop; modal_variable<bool> mm_last_property_is_sprop;
@ -248,6 +249,9 @@ private:
OASISWriterOptions m_options; OASISWriterOptions m_options;
tl::AbsoluteProgress m_progress; tl::AbsoluteProgress m_progress;
void create_cell_nstrings (const db::Layout &layout, const std::set <db::cell_index_type> &cell_set);
const char *cell_nstring(db::cell_index_type cell_index);
void write_record_id (char b); void write_record_id (char b);
void write_byte (char b); void write_byte (char b);
void write_bytes (const char *b, size_t n); void write_bytes (const char *b, size_t n);
@ -273,19 +277,17 @@ private:
void write (double d); void write (double d);
void write (float d); void write (float d);
void write (long n); void write (int64_t n);
void write (unsigned long n); void write (uint64_t n);
void write (long long n);
void write (unsigned long long n);
void write (int n) void write (int32_t n)
{ {
write (long (n)); write (int64_t (n));
} }
void write (unsigned int n) void write (uint32_t n)
{ {
write ((unsigned long) (n)); write ((uint64_t) (n));
} }
void write (const Repetition &rep); void write (const Repetition &rep);

View File

@ -662,20 +662,37 @@ TEST(Bug_1799)
EXPECT_EQ (ps2.value (pn).to_string (), "hello, world!"); EXPECT_EQ (ps2.value (pn).to_string (), "hello, world!");
} }
// Modified in #2088 to give a warning
TEST(DuplicateCellname) TEST(DuplicateCellname)
{ {
db::Manager m (false); db::Manager m (false);
db::Layout layout (&m); db::Layout layout (&m);
try { tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas");
tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas"); db::OASISReader reader (file);
reader.read (layout);
std::string fn_au (tl::testdata () + "/oasis/duplicate_cellname_au.oas");
db::compare_layouts (_this, layout, fn_au, db::NoNormalization, 1);
}
TEST(BlendCrash)
{
db::Manager m (false);
db::Layout layout (&m);
{
tl::InputStream file (tl::testdata () + "/oasis/blend_crash1.oas");
db::OASISReader reader (file); db::OASISReader reader (file);
reader.read (layout); reader.read (layout);
EXPECT_EQ (false, true);
} catch (tl::CancelException &ex) {
// Seen when private test data is not installed
throw;
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg ().find ("Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)"), size_t (0));
} }
{
tl::InputStream file (tl::testdata () + "/oasis/blend_crash2.oas");
db::OASISReader reader (file);
reader.read (layout);
}
std::string fn_au (tl::testdata () + "/oasis/blend_crash_au.gds.gz");
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
} }

View File

@ -2072,3 +2072,31 @@ TEST(130d)
run_test130 (_this, true, true); run_test130 (_this, true, true);
} }
// Issue #2088 (name duplication)
TEST(140)
{
db::Layout layout_org;
layout_org.add_cell ("X X");
layout_org.add_cell ("X*X");
std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter140.oas"));
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_format ("OASIS");
db::Writer writer (options);
writer.write (layout_org, out);
}
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
db::Layout gg;
reader.set_warnings_as_errors (true);
reader.read (gg);
db::compare_layouts (_this, gg, tl::testdata () + "/oasis/dbOASISWriter40_au.gds", db::NoNormalization);
}
}

View File

@ -1142,6 +1142,8 @@ public:
db::GerberImporter importer (warn_level ()); db::GerberImporter importer (warn_level ());
data.setup_importer (&importer); data.setup_importer (&importer);
check_dbu (data.dbu);
importer.read (layout); importer.read (layout);
std::string lyr_file = data.get_layer_properties_file (); std::string lyr_file = data.get_layer_properties_file ();

BIN
testdata/algo/fill_tool6.gds vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
testdata/algo/fill_tool_au6.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_1.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_2.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_2_10nm.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_a.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_1.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_12_1.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_12_2.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_12_3.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_12_4.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_au_3.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_b.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_c.oas vendored Normal file

Binary file not shown.

BIN
testdata/bd/strm2oas_cc.oas vendored Normal file

Binary file not shown.

58
testdata/drc/drcFullTest_1.drc vendored Normal file
View File

@ -0,0 +1,58 @@
source($drc_test_source)
target($drc_test_target)
verbose
ncpu = 4
chip = input(189, 4)
chip.output(189, 4)
# NOTE: this must not happen in tiled mode as the sealring
# is only visible as a whole in flat mode
sealring = source.cell("sealring")
metal1_seal = sealring.input(8, 0)
metal1_seal_inner = metal1_seal.holes
# NOTE: metal1_seal_outer is empty if there is no sealring ->
# the full chip will be filled
metal1_seal_outer = chip.interacting(metal1_seal_inner) - metal1_seal_inner
# Everything else can be done in tiled mode
tiles(500)
tile_borders(2.0)
threads(ncpu)
metal1 = input(8, 0)
metal1.output(8, 0)
metal1_fill = input(8, 22)
metal1_nofill = input(8, 23) + metal1_seal_outer
metal1_dist = 0.42
min_space_to_fill = 1.0
pattern = fill_pattern("METAL1_FILL1")
pattern.shape(8, 22, box(0.0, 0.0, 5.0, 5.0))
pattern.dim(5.0, 5.0)
pattern.margin(metal1_dist, metal1_dist)
to_fill = chip - metal1_nofill - metal1
to_fill = to_fill.fill_with_left(pattern, hstep(7.0, 0), vstep(1.5, 7.0), multi_origin)
pattern = fill_pattern("METAL1_FILL2")
pattern.shape(8, 22, box(0.0, 0.0, 2.0, 2.0))
pattern.dim(2.0, 2.0)
pattern.margin(metal1_dist, metal1_dist)
to_fill = to_fill.fill_with_left(pattern, hstep(2.42, 0), vstep(0.65, 2.42), multi_origin)
pattern = fill_pattern("METAL1_FILL3")
pattern.shape(8, 22, box(0.0, 0.0, 1.2, 1.2))
pattern.dim(1.2, 1.2)
pattern.margin(metal1_dist, metal1_dist)
to_fill = to_fill.fill_with_left(pattern, hstep(1.62, 0), vstep(0.3, 1.62), multi_origin)

BIN
testdata/drc/drcFullTest_1.oas vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcFullTest_au1.oas vendored Normal file

Binary file not shown.

View File

@ -14,18 +14,22 @@ f2 = extent - l1.sized(1.0)
p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um)
p2 = fill_pattern("PAT2").shape(100, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) p2 = fill_pattern("PAT2").shape(100, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um)
p3 = fill_pattern("PAT3").shape(100, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um)) p3 = fill_pattern("PAT3").shape(100, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um))
p4 = fill_pattern("PAT4").shape(100, 3, box(0, 0, 1.um, 1.um)).dim(1.um, 1.um).margin(2.um, 4.um)
p11 = fill_pattern("PAT11").shape(101, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) p11 = fill_pattern("PAT11").shape(101, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um)
p12 = fill_pattern("PAT12").shape(101, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) p12 = fill_pattern("PAT12").shape(101, 1, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um)
p13 = fill_pattern("PAT13").shape(101, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um)) p13 = fill_pattern("PAT13").shape(101, 2, box(0, 0, 1.um, 1.um)).shape(1000, 0, box(-0.5.um, -0.5.um, 1.5.um, 1.5.um))
p14 = fill_pattern("PAT14").shape(101, 3, box(0, 0, 1.um, 1.um)).dim(1.um, 1.um).margin(2.um, 4.um)
f1.fill(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)) f1.fill(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0))
f1.fill(p2, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) f1.fill(p2, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin)
f1.fill(p3) f1.fill(p3)
f1.fill(p4, hstep(2.0, 0), vstep(0, 2.0))
f2.fill(p11, hstep(2.0, 1.0), vstep(-1.0, 2.0)) f2.fill(p11, hstep(2.0, 1.0), vstep(-1.0, 2.0))
f2.fill(p12, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin) f2.fill(p12, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin)
f2.fill(p13) f2.fill(p13)
f2.fill(p14, hstep(2.0, 0), vstep(0, 2.0))
l1.output(1, 0) l1.output(1, 0)
f1.output(10, 0) f1.output(10, 0)

View File

@ -11,8 +11,12 @@ l1 = input(1, 0)
f1 = l1 f1 = l1
p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um) p1 = fill_pattern("PAT1").shape(100, 0, box(0, 0, 1.um, 1.um)).origin(-0.5.um, -0.5.um)
p2 = fill_pattern("PAT1").shape(100, 1, box(0, 0, 1.um, 1.um)).dim(1.um, 1.um)
p3 = fill_pattern("PAT1").shape(100, 2, box(0, 0, 1.um, 1.um)).dim(1.um, 1.um).margin(1.um, 2.um)
f1.fill_with_left(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(100, 0) f1.fill_with_left(p1, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(101, 0)
f1.fill_with_left(p2, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(101, 1)
f1.fill_with_left(p3, hstep(2.0, 1.0), vstep(-1.0, 2.0)).output(101, 2)
l1.output(1, 0) l1.output(1, 0)
f1.output(10, 0) f1.output(10, 0)

Binary file not shown.

Binary file not shown.

BIN
testdata/lefdef/issue-2075/au.oas vendored Normal file

Binary file not shown.

16
testdata/lefdef/issue-2075/test.def vendored Normal file
View File

@ -0,0 +1,16 @@
VERSION 5.8 ;
DESIGN test ;
UNITS DISTANCE MICRONS 2000 ;
DIEAREA ( 0 0 ) ( 6000 3000 ) ;
VIAS 1 ;
END VIAS
NETS 1 ;
- dummy + ROUTED M2 ( 1670 830 ) ( * * 300 ) ( 4950 * 300 ) ( * 2000 300 ) ;
END NETS
END DESIGN

12
testdata/lefdef/issue-2075/test.lef vendored Normal file
View File

@ -0,0 +1,12 @@
VERSION 5.8 ;
UNITS
DATABASE MICRONS 2000 ;
END UNITS
LAYER M2
TYPE ROUTING ;
WIDTH 0.2 ;
END M2
END LIBRARY

2
testdata/lefdef/issue-2075/test.map vendored Normal file
View File

@ -0,0 +1,2 @@
DIEAREA ALL 108 0
M2 NET 32 0

BIN
testdata/maly/maly_test10_dbu10nm_au.oas vendored Normal file

Binary file not shown.

BIN
testdata/oasis/blend_crash1.oas vendored Normal file

Binary file not shown.

BIN
testdata/oasis/blend_crash2.oas vendored Normal file

Binary file not shown.

BIN
testdata/oasis/blend_crash_au.gds.gz vendored Normal file

Binary file not shown.

BIN
testdata/oasis/dbOASISWriter40_au.gds vendored Normal file

Binary file not shown.

BIN
testdata/oasis/duplicate_cellname_au.oas vendored Normal file

Binary file not shown.

View File

@ -1728,6 +1728,181 @@ class DBShapes_TestClass < TestBase
end end
def test_15
# Various container insert methods
ly = RBA::Layout::new
l1 = ly.layer(1, 0)
top = ly.create_cell("TOP")
top.shapes(l1).insert(RBA::Box::new(2000))
shapes2 = RBA::Shapes::new
shapes2.insert(RBA::Box::new(4000))
shapes = RBA::Shapes::new
shapes.insert(shapes2)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-2000,-2000;2000,2000)")
# self-insert
shapes.insert(shapes)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-2000,-2000;2000,2000);box (-2000,-2000;2000,2000)")
shapes.clear
shapes.insert(shapes2, RBA::Shapes::SBoxes)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-2000,-2000;2000,2000)")
shapes.clear
shapes.insert(shapes2, RBA::Shapes::SPolygons)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "")
shapes.clear
shapes.insert(shapes2, RBA::ICplxTrans::new(RBA::Vector::new(100, 200)))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-1900,-1800;2100,2200)")
shapes.clear
shapes.insert(shapes2, RBA::Shapes::SBoxes, RBA::ICplxTrans::new(RBA::Vector::new(100, 200)))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-1900,-1800;2100,2200)")
shapes.clear
shapes.insert(shapes2, RBA::Shapes::SPolygons, RBA::ICplxTrans::new(RBA::Vector::new(100, 200)))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "")
shapes.clear
shapes.insert(top.begin_shapes_rec(l1))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-1000,-1000;1000,1000)")
shapes.clear
shapes.insert(top.begin_shapes_rec(l1), RBA::ICplxTrans::new(100, 200))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-900,-800;1100,1200)")
top.shapes(l1).insert(top.begin_shapes_rec(l1))
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "box (-1000,-1000;1000,1000);box (-1000,-1000;1000,1000)")
shapes.clear
r = RBA::Region::new(RBA::Box::new(2000))
shapes.insert(r)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "polygon (-1000,-1000;-1000,1000;1000,1000;1000,-1000)")
shapes.clear
r = RBA::Region::new(RBA::Box::new(2000))
shapes.insert(r, RBA::ICplxTrans::new(100, 200))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "polygon (-900,-800;-900,1200;1100,1200;1100,-800)")
top.shapes(l1).clear
top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2))
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "polygon (-900,-800;-900,1200;1100,1200;1100,-800)")
shapes.clear
r = RBA::Edges::new(RBA::Edge::new(0, 0, 1000, 2000))
shapes.insert(r)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (0,0;1000,2000)")
shapes.clear
r = RBA::Edges::new(RBA::Edge::new(0, 0, 1000, 2000))
shapes.insert(r, RBA::ICplxTrans::new(100, 200))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (100,200;1100,2200)")
top.shapes(l1).clear
top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2))
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "edge (100,200;1100,2200)")
shapes.clear
r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000)))
shapes.insert_as_polygons(r, 10)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "simple_polygon (-10,-10;-10,2010;110,2010;110,-10)")
shapes.clear
r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000)))
shapes.insert_as_polygons(r, RBA::ICplxTrans::new(100, 200), 10)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "simple_polygon (90,190;90,2210;210,2210;210,190)")
top.shapes(l1).clear
top.shapes(l1).insert_as_polygons(r, RBA::DCplxTrans::new(0.1, 0.2), 0.01)
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "simple_polygon (90,190;90,2210;210,2210;210,190)")
shapes.clear
r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000)))
shapes.insert_as_edges(r)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (0,0;0,2000);edge (100,0;100,2000)")
shapes.clear
r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000)))
shapes.insert_as_edges(r, RBA::ICplxTrans::new(100, 200))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (100,200;100,2200);edge (200,200;200,2200)")
top.shapes(l1).clear
top.shapes(l1).insert_as_edges(r, RBA::DCplxTrans::new(0.1, 0.2))
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "edge (100,200;100,2200);edge (200,200;200,2200)")
shapes.clear
r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000)))
shapes.insert(r)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge_pair (0,0;0,2000)/(100,0;100,2000)")
shapes.clear
r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000)))
shapes.insert(r, RBA::ICplxTrans::new(100, 200))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge_pair (100,200;100,2200)/(200,200;200,2200)")
top.shapes(l1).clear
top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2))
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "edge_pair (100,200;100,2200)/(200,200;200,2200)")
shapes.clear
r = RBA::Texts::new(RBA::Text::new("Text", RBA::Trans::new(100, 200)))
shapes.insert(r)
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "text ('Text',r0 100,200)")
shapes.clear
r = RBA::Texts::new(RBA::Text::new("Text", RBA::Trans::new(100, 200)))
shapes.insert(r, RBA::ICplxTrans::new(100, 200))
assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "text ('Text',r0 200,400)")
top.shapes(l1).clear
top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2))
assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "text ('Text',r0 200,400)")
end
def test_16
# issue #2094
# speedy insert -> should not take more than a few seconds
ly = RBA::Layout::new
l1 = ly.layer(1, 0)
main = ly.create_cell("MAIN")
box = RBA::Region::new(RBA::Box::new(4000))
100000.times do
main.shapes(l1).insert(box)
end
assert_equal(main.shapes(l1).size, 100000)
end
end end
load("test_epilogue.rb") load("test_epilogue.rb")