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>
Last modified: 2025-01-19<br>
Last modified: 2025-05-30<br>
# 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:
* Sonoma (14.x) : the primary development environment
* Ventura (13.x) : experimental
* Sequoia (15.x) : -- ditto --
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:
* Sequoia (15.x) : the primary development environment
* Sonoma (14.x) : experimental
* Ventura (13.x) : -- ditto --
Building KLayout for the previous operating systems listed below has been discontinued.<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)
* 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>
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/
```
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>
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' >>
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
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details) | default value
@ -123,7 +123,7 @@ $ [python] ./build4mac.py
```
# 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.
### 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`.
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
$ ./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
```
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.
* "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.
@ -185,7 +185,7 @@ $ ./build4mac.py -q qt5macports -r mp33 -p mp312
$ ./build4mac.py -q qt5macports -r mp33 -p mp312 -Y
```
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.
* "qt5MP" means that Qt5 from MacPorts is used.
* "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
```
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.
* "qt6Brew" means that Qt6 from Homebrew is used.
* "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
```
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.
* "qt6Brew" means that Qt6 from Homebrew is used.
* "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
```
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.
* "qt5MP" means that Qt5 from MacPorts is used.
* "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.
### 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 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
```
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.
* "qt5Ana3" means that Qt5 from Anaconda3 is used.
* "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,
```
$ 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>
* **`LW-klayout-0.29.11-macOS-Sonoma-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`** ---(1) the main DMG file
* **`LW-klayout-0.30.2-macOS-Sequoia-1-qt5MP-Rmp33Pmp312.dmg.md5`** ---(2) MD5-value text file
# 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>

View File

@ -5,7 +5,7 @@
# File: "macbuild/build4mac.py"
#
# 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 os
@ -45,7 +45,7 @@ def GenerateUsage(platform):
usage = "\n"
usage += "-----------------------------------------------------------------------------------------------------------\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 += "$ [python] ./build4mac.py\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"
cmd4_args = " -m setup clean --all \\\n"
#--------------------------------------------------------------------
#-----------------------------------------------------------------------------------
# [4] Make the consolidated command lines
#--------------------------------------------------------------------
# "caffeinate" makes the CPU run at full speed even when the screen is locked.
#-----------------------------------------------------------------------------------
command1 = "time"
command1 += " \\\n %s \\\n" % parameters['python']
command1 += " \\\n caffeinate -i %s \\\n" % parameters['python']
command1 += cmd1_args
command1 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command1 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command2 = "time"
command2 += " \\\n %s \\\n" % parameters['python']
command2 += " \\\n caffeinate -i %s \\\n" % parameters['python']
command2 += cmd2_args
command2 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command2 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command3 = "time"
command3 += " \\\n %s \\\n" % deloc_cmd
command3 += " \\\n caffeinate -i %s \\\n" % deloc_cmd
command3 += cmd3_args
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command4 = "time"
command4 += " \\\n %s \\\n" % parameters['python']
command4 += " \\\n caffeinate -i %s \\\n" % parameters['python']
command4 += cmd4_args
command4 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
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
# 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:
command3 = "time"
command3 += " \\\n %s \\\n" % deloc_cmd
command3 += " \\\n caffeinate -i %s \\\n" % deloc_cmd
command3 += " %s \\\n" % cmd3_args[0]
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
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
# 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
# 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":
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:
print( "", file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
@ -1114,11 +1115,12 @@ def Run_Build_Command(config, parameters):
else:
cmd_args += " \\\n -nopython"
#-----------------------------------------------------
#-----------------------------------------------------------------------------------
# [4] Make the consolidated command line
#-----------------------------------------------------
# "caffeinate" makes the CPU run at full speed even when the screen is locked.
#-----------------------------------------------------------------------------------
command = "time"
command += " \\\n %s" % parameters['build_cmd']
command += " \\\n caffeinate -i %s" % parameters['build_cmd']
command += cmd_args
command += " 2>&1 | tee %s; \\\n" % parameters['logfile']
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
# To use Python3.1 from Homebrew on Sonoma...
# To use Python3.11 from Homebrew on Sequoia...
# in "/usr/local/opt/python@3.11/lib/"
# Python.framework -> ../Frameworks/Python.framework/ <=== this symbolic was needed
# pkgconfig/
#
# 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
# Sonoma{kazzz-s} lib (2)% ll
# Sequoia{kazzz-s} lib (2)% ll
# total 0
# drwxr-xr-x 4 kazzz-s admin 128 9 21 23:03 .
# 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/
# 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
# Sonoma{kazzz-s} Versions (4)% ll
# Sequoia{kazzz-s} Versions (4)% ll
# total 0
# 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 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/
#
# Sonoma{kazzz-s} Python.framework (5)% pwd
# Sequoia{kazzz-s} Python.framework (5)% pwd
# /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
# drwxr-xr-x 6 kazzz-s admin 192 9 21 23:03 .
# drwxr-xr-x 3 kazzz-s admin 96 9 7 10:03 ..

View File

@ -6,7 +6,7 @@
#
# Here are dictionaries of ...
# 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.
#===============================================================================

View File

@ -6,7 +6,7 @@
#
# Here are utility functions and classes ...
# 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.
#========================================================================================

View File

@ -5,7 +5,7 @@
# File: "macbuild/macQAT.py"
#
# 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.
#
# This script must be copied to a "*.macQAT/" directory to run.

View File

@ -4,7 +4,7 @@
# File: "macbuild/macQAT.sh"
#
# 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.
#
# This script must be copied to a "*.macQAT/" directory to run.

View File

@ -4,7 +4,7 @@
# File: "macbuild/macQAT2.sh"
#
# 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.
#
# 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 += "<< 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 += "$ [python] ./makeDMG4mac.py\n"
Usage += " option & argument : descriptions | default value\n"
Usage += " ----------------------------------------------------------------------------------+-----------------\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 += " <-m|--make> : make a compressed DMG file | disabled\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
#
# The package directory name should look like:
# * ST-qt5MP.pkg.macos-Sonoma-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sonoma-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sonoma-release-Rhb34Phb312 --- (1)
# * LW-qt5MP.pkg.macos-Sonoma-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sonoma-release-RsysPhb311
# * ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312 --- (1)
# * LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311
#
# * ST-qt6MP.pkg.macos-Sonoma-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sonoma-release-Rmp33Pmp312
# * ST-qt6MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sequoia-release-Rmp33Pmp312
#
# 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;
# on failure, -1
@ -268,15 +268,15 @@ def CheckPkgDirectory():
#-----------------------------------------------------------------------------------------------
# [2] Identify (Qt, Ruby, Python) from PkgDir
# * ST-qt5MP.pkg.macos-Sonoma-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sonoma-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sonoma-release-Rhb34Phb312
# * LW-qt5MP.pkg.macos-Sonoma-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sonoma-release-RsysPhb311
# * EX-qt5MP.pkg.macos-Sonoma-release-Rhb34Pmp312
# * ST-qt5MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Sequoia-release-Rana3Pana3
# * LW-qt6Brew.pkg.macos-Sequoia-release-Rhb34Phb312
# * LW-qt5MP.pkg.macos-Sequoia-release-Rmp33Pmp312
# * HW-qt6Brew.pkg.macos-Sequoia-release-RsysPhb311
# * EX-qt5MP.pkg.macos-Sequoia-release-Rhb34Pmp312
#
# * ST-qt6MP.pkg.macos-Sonoma-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sonoma-release-Rmp33Pmp312
# * ST-qt6MP.pkg.macos-Sequoia-release-RsysPsys
# * LW-qt6MP.pkg.macos-Sequoia-release-Rmp33Pmp312
#-----------------------------------------------------------------------------------------------
# 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]+)'

View File

@ -339,7 +339,7 @@ def Parse_CommandLine_Arguments():
Usage += " (3) $ ./nightlyBuild.py --test |\n"
Usage += " (4) $ ./nightlyBuild.py --check (confirm the QA Test results) |\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 += "-----------------------------------------------------------------------------+----------------------------\n"

View File

@ -18,7 +18,7 @@ qtVer,target,bdType
5,0,r
5,1,r
6,2,r
6,13,r
5,13,r
5,4,r
#6,0,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 += " option & argument : descriptions | default value\n"
Usage += " -------------------------------------------------------------------+---------------\n"
Usage += " <-v|--version <number>>: in ['3.8', '3.9', '3.10', '3.11', '3.12', | ''\n"
Usage += " '3.13'] |\n"
Usage += " <-v|--version <number>>: in ['3.11', '3.12','3.13'] | ''\n"
Usage += " [-u|-unlink] : unlink only | disabled\n"
Usage += " [-?|--?] : print this usage and exit | disabled\n"
Usage += "----------------------------------------------------------------------+-----------------\n"
@ -50,7 +49,7 @@ def Parse_CLI_Args():
p.add_option( '-v', '--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',
action='store_true',
@ -75,7 +74,7 @@ def Parse_CLI_Args():
Version = opt.version
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(Usage)
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);
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. "
"This provides some cheap, but risky way of merging files. Beware of cell name conflicts.")
"Multiple files can be combined using '+' or ','. '+' will combine the files in 'blending' mode. "
"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))
;

View File

@ -22,6 +22,8 @@
#include "bdReaderOptions.h"
#include "dbLoadLayoutOptions.h"
#include "dbLayerMapping.h"
#include "dbCellMapping.h"
#include "tlCommandLineParser.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;
for (size_t pp = 0; (pp = find_file_sep (infile, p)) != std::string::npos; p = pp + 1) {
files.push_back (std::string (infile, p, pp - p));
while (true) {
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;
}
@ -850,16 +865,73 @@ void read_files (db::Layout &layout, const std::string &infile, const db::LoadLa
// db::LayoutLocker locker (&layout);
// but there are yet unknown side effects
// 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);
std::vector<std::vector<std::string> > files = split_file_list (infile);
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;
for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) {
if (i > 0) {
input += ",";
input += "+";
}
input += def_dir + "/" + def_files[i];
}
@ -510,3 +510,201 @@ TEST(10)
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
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<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) {
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 (iname->second.second != iid->second.second) {
@ -240,7 +251,7 @@ CommonReaderBase::cell_for_instance (db::Layout &layout, const std::string &cn)
}
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);
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
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);
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
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
if (with_meta) {
auto ib = layout.begin_meta (src_cell.cell_index ());
auto ie = layout.end_meta (src_cell.cell_index ());
auto ib = layout.begin_meta (src_cell_index);
auto ie = layout.end_meta (src_cell_index);
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
layout.delete_cell (src_cell.cell_index ());
m_temp_cells.erase (src_cell_index);
layout.delete_cell (src_cell_index);
}
void

View File

@ -242,12 +242,12 @@ protected:
/**
* @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
*/
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

View File

@ -77,14 +77,16 @@ public:
}
// 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_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1;
db::Box fp_bbox = fp.box ();
// 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 rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx;
@ -167,6 +169,11 @@ public:
return m_area_maps [i];
}
db::AreaMap &area_map (unsigned int i)
{
return m_area_maps [i];
}
private:
std::vector<db::AreaMap> m_area_maps;
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) {
const db::AreaMap &am1 = am.area_map (i);
db::AreaMap &am1 = am.area_map (i);
size_t nx = am1.nx ();
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;
}
ninsts += (jj - j);
db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());
db::CellInstArray array;
if (jj > j + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (), (unsigned long) (jj - j), 1);
// try to expand the array in x direction
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 {
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
tl::MutexLocker locker (&db::TilingProcessor::output_lock ());
tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array);
}
if (remaining_parts) {
if (am1.d ().y () == am1.p ().y ()) {
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)));
if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
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 {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
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 (remaining_parts) {
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);
ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);
}
return true;

View File

@ -1918,7 +1918,7 @@ rasterize_impl (const db::polygon<C> &polygon, db::area_map<C> &am)
area_type aa = a;
if (dx == py) {
if (dx == px) {
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::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

View File

@ -138,6 +138,33 @@ public:
*/
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:
virtual void init (const db::LoadLayoutOptions &options);
@ -147,6 +174,7 @@ private:
std::string m_last_warning;
int m_warn_count_for_same_message;
bool m_first_warning;
double m_expected_dbu;
};
/**
@ -231,6 +259,22 @@ public:
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:
ReaderBase *mp_actual_reader;
tl::InputStream &m_stream;

View File

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

View File

@ -1127,10 +1127,20 @@ void Shapes::reset_bbox_dirty ()
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) {
(*l)->sort ();
(*l)->update_bbox ();
}
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 &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 ();
} else if (name.is_a_string ()) {
*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);
}
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)
{
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
ProtectedShapes ps (sh, r);
for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) {
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)
{
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
ProtectedShapes ps (sh, r);
for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) {
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)
{
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)
{
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)
{
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
ProtectedShapes ps (sh, s);
for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end(); ++i) {
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)
{
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
ProtectedShapes ps (sh, s);
for (db::Shapes::shape_iterator i = s.begin (flags); !i.at_end(); ++i) {
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)
{
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
ProtectedShapes ps (sh, r);
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)
{
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
// lock the layout against updates while inserting
db::LayoutLocker locker (sh->layout ());
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
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)
{
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
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)
{
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
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) {
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)
{
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
db::Coord e = db::coord_traits<db::Coord>::rounded (de / shapes_dbu (sh));
db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
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)
{
ProtectedShapes ps (sh, r);
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->first ());
sh->insert (s->second ());
ps->insert (s->first ());
ps->insert (s->second ());
}
}
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) {
sh->insert (s->first ().transformed (trans));
sh->insert (s->second ().transformed (trans));
ps->insert (s->first ().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)
{
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) {
sh->insert (s->first ().transformed (itrans));
sh->insert (s->second ().transformed (itrans));
ps->insert (s->first ().transformed (itrans));
ps->insert (s->second ().transformed (itrans));
}
}
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) {
sh->insert (*s);
ps->insert (*s);
}
}
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) {
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)
{
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
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)
{
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
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)
{
ProtectedShapes ps (sh, r);
db::CplxTrans dbu_trans (shapes_dbu (sh));
db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans;
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();
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
# 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
# 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.
# 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
# (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)
# @/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
# 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)
top_cell = @engine._output_cell
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
cs = dbu_trans * column_step
origin = origin ? dbu_trans * origin : nil
@ -5604,6 +5626,7 @@ CODE
tp.var("rs", rs)
tp.var("cs", cs)
tp.var("origin", origin)
tp.var("fill_margin", fill_margin)
tp.var("fc_index", fc_index)
tp.var("repeat", repeat)
tp.var("with_left", with_left)
@ -5616,8 +5639,8 @@ CODE
tile_box = tile_box & tc_box;
var left = with_left ? Region.new : nil;
repeat ?
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, Vector.new, 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_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, fill_margin, left, _tile.bbox);
with_left && _output(#{result_arg}, left)
)
END
@ -5639,9 +5662,9 @@ END
@engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do
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
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

View File

@ -335,6 +335,7 @@ module DRC
@shapes = []
@origin = nil
@dim = nil
@margin = RBA::DVector::new
end
def create_cell(layout, engine)
@ -350,7 +351,11 @@ module DRC
def cell_box(def_w, def_h)
o = @origin || self._computed_origin
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
def default_xpitch
@ -454,6 +459,20 @@ module DRC
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
# 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 \
drcGenericTests.cc \
drcSimpleTests.cc \
drcFullTests.cc \
drcSuiteTests.cc \
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);
double sf = 0.01 / m_dbu;
check_dbu (m_dbu);
layout.dbu (m_dbu);
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 ..
check_dbu (m_dbu);
layout.dbu (m_dbu);
do_read (layout, top);
cleanup (layout, top);

View File

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

View File

@ -30,6 +30,8 @@
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
{
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);
#else
// 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);
#endif
}
@ -355,28 +357,46 @@ DEFImporter::produce_routing_geometry (db::Cell &design, const Polygon *style, u
bool was_path_before = false;
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 ()) {
std::vector<db::Point>::const_iterator pt0 = pt;
auto pt0 = pt;
auto ex0 = ex;
++pt;
++ex;
if (pt == pts.end ()) {
break;
}
bool multipart = false;
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;
++ex;
multipart = true;
}
if (multipart) {
--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;
@ -388,33 +408,27 @@ DEFImporter::produce_routing_geometry (db::Cell &design, const Polygon *style, u
wxy_perp = w.second;
}
// compute begin extension
// compute begin and end extensions
db::Coord be = 0;
if (pt0 == pts.begin ()) {
if (pt0->x () == pt0 [1].x ()) {
be = ext.front ().second;
} else {
be = ext.front ().first;
}
if (*ex0 != ext_not_set) {
be = (pt0->x () == pt0 [1].x ()) ? ex0->second : ex0->first;
} else if (was_path_before) {
// provides the overlap to the previous segment
be = wxy_perp / 2;
}
// compute end extension
db::Coord ee = 0;
if (pt + 1 == pts.end ()) {
if (pt [-1].x () == pt->x ()) {
ee = ext.back ().second;
} else {
ee = ext.back ().first;
}
if (*ex != ext_not_set) {
ee = (pt [-1].x () == pt->x ()) ? ex->second : ex->first;
}
auto pt_from = pt0;
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_to_split = pt_to;
@ -553,10 +567,6 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
const std::string *rulename = 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;
@ -564,6 +574,9 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
if (specialnets) {
db::Coord n = db::coord_traits<db::Coord>::rounded (get_double () * scale);
w = std::make_pair (n, n);
while (test ("+")) {
if (test ("STYLE")) {
@ -598,11 +611,17 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
rulename = &nondefaultrule;
}
std::pair<db::Coord, db::Coord> def_ext (0, 0);
if (! specialnets) {
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);
@ -694,7 +713,7 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
y = get_double ();
}
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 (")")) {
db::Coord e = db::coord_traits<db::Coord>::rounded (get_double () * scale);
ee.first = ee.second = e;
@ -706,10 +725,20 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C
}
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);
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);
}
}
// 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;
}
check_dbu (effective_options.dbu ());
layout.dbu (effective_options.dbu ());
std::string base_path;

View File

@ -432,7 +432,7 @@ TEST(def16)
// (complete example)
db::LEFDEFReaderOptions opt = default_options ();
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)
@ -1153,3 +1153,10 @@ TEST(215_multiDEF)
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 ());
}
check_dbu (m_dbu);
layout.dbu (m_dbu);
m_cells_to_read.clear ();

View File

@ -51,7 +51,6 @@ namespace db
MALYReader::MALYReader (tl::InputStream &s)
: m_stream (s),
m_progress (tl::to_string (tr ("Reading MALY file")), 1000),
m_dbu (0.001),
m_last_record_line (0)
{
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);
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_create_layers (specific_options.create_other_layers);

View File

@ -223,7 +223,6 @@ private:
tl::TextInputStream m_stream;
tl::AbsoluteProgress m_progress;
double m_dbu;
unsigned int m_last_record_line;
std::string m_record;
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_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)

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::Coord> mm_placement_x;
modal_variable<db::Coord> mm_placement_y;
modal_variable<unsigned int> mm_layer;
modal_variable<unsigned int> mm_datatype;
modal_variable<unsigned int> mm_textlayer;
modal_variable<unsigned int> mm_texttype;
modal_variable<uint32_t> mm_layer;
modal_variable<uint32_t> mm_datatype;
modal_variable<uint32_t> mm_textlayer;
modal_variable<uint32_t> mm_texttype;
modal_variable<db::Coord> mm_text_x;
modal_variable<db::Coord> mm_text_y;
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_y;
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_end_extension;
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<db::property_names_id_type> mm_last_property_name;
modal_variable<bool> mm_last_property_is_sprop;
modal_variable<property_value_list> mm_last_value_list;
std::map <unsigned long, db::properties_id_type> m_cellname_properties;
std::map <unsigned long, std::string> m_textstrings;
std::map <unsigned long, const db::StringRef *> m_text_forward_references;
std::map <unsigned long, std::string> m_propstrings;
std::map <unsigned long, std::string> m_propnames;
std::map <uint64_t, db::properties_id_type> m_cellname_properties;
std::map <uint64_t, std::string> m_textstrings;
std::map <uint64_t, const db::StringRef *> m_text_forward_references;
std::map <uint64_t, std::string> m_propstrings;
std::map <uint64_t, std::string> m_propnames;
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_all_properties;
std::map <unsigned long, db::property_names_id_type> m_propname_forward_references;
std::map <unsigned long, std::string> m_propvalue_forward_references;
std::map <uint64_t, db::property_names_id_type> m_propname_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::Instances *> > m_forward_properties_for_instances;
std::map <db::cell_index_type, db::PropertiesSet> m_future_cell_properties;
@ -234,42 +234,35 @@ private:
}
}
long long get_long_long ();
unsigned long long get_ulong_long ();
long get_long ();
unsigned long get_ulong ();
unsigned long get_ulong_for_divider ();
int get_int ();
unsigned int get_uint ();
int64_t get_int64 ();
uint64_t get_uint64 ();
uint64_t get_uint64_for_divider ();
int32_t get_int32 ();
uint32_t get_uint32 ();
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 ();
}
void get (unsigned int &l)
{
l = get_uint ();
l = get_uint32 ();
}
void get (double &d)
@ -280,12 +273,12 @@ private:
std::string get_str ();
void get_str (std::string &s);
double get_real ();
db::Vector get_gdelta (long grid = 1);
db::Vector get_3delta (long grid = 1);
db::Vector get_2delta (long grid = 1);
db::Coord get_coord (long grid = 1);
db::Coord get_ucoord (unsigned long grid = 1);
distance_type get_ucoord_as_distance (unsigned long grid = 1);
db::Vector get_gdelta (int64_t grid = 1);
db::Vector get_3delta (int64_t grid = 1);
db::Vector get_2delta (int64_t grid = 1);
db::Coord get_coord (int64_t grid = 1);
db::Coord get_ucoord (uint64_t grid = 1);
distance_type get_ucoord_as_distance (uint64_t grid = 1);
};
}

View File

@ -26,6 +26,7 @@
#include "tlDeflate.h"
#include "tlMath.h"
#include "tlUniqueName.h"
#include <math.h>
@ -411,7 +412,7 @@ Compressor<Obj>::flush (db::OASISWriter *writer)
if (dd != displacements.end ()) {
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;
++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 ()));
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);
if (df == dwindow || *df != *dd + dxy) {
break;
@ -717,45 +718,17 @@ OASISWriter::write_bytes (const char *b, size_t n)
}
void
OASISWriter::write (long long n)
OASISWriter::write (int64_t n)
{
if (n < 0) {
write (((unsigned long long) (-n) << 1) | 1);
write (((uint64_t) (-n) << 1) | 1);
} else {
write ((unsigned long long) n << 1);
write ((uint64_t) n << 1);
}
}
void
OASISWriter::write (unsigned long long 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)
OASISWriter::write (uint64_t n)
{
char buffer [50];
char *bptr = buffer;
@ -775,15 +748,15 @@ OASISWriter::write (unsigned long n)
void
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)
if (d < 0.0) {
write_byte (1);
write ((unsigned long) floor (-d + 0.5));
write ((uint64_t) floor (-d + 0.5));
} else {
write_byte (0);
write ((unsigned long) floor (d + 0.5));
write ((uint64_t) floor (d + 0.5));
}
} else {
@ -799,7 +772,7 @@ OASISWriter::write (float d)
f2i.d = d;
uint32_t i = 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);
i >>= 8;
}
@ -811,15 +784,15 @@ OASISWriter::write (float d)
void
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)
if (d < 0.0) {
write_byte (1);
write ((unsigned long) floor (-d + 0.5));
write ((uint64_t) floor (-d + 0.5));
} else {
write_byte (0);
write ((unsigned long) floor (d + 0.5));
write ((uint64_t) floor (d + 0.5));
}
} else {
@ -848,7 +821,7 @@ void
OASISWriter::write_bstring (const char *s)
{
size_t l = strlen (s);
write (l);
write ((uint64_t) l);
write_bytes (s, l);
}
@ -867,7 +840,7 @@ void
OASISWriter::write_astring (const char *s)
{
std::string nstr = make_astring (s);
write (nstr.size ());
write ((uint64_t) nstr.size ());
write_bytes (nstr.c_str (), nstr.size ());
}
@ -886,7 +859,7 @@ void
OASISWriter::write_nstring (const char *s)
{
std::string nstr = make_nstring (s);
write (nstr.size ());
write ((uint64_t) 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) {
unsigned long long dir = 0;
unsigned long long l = 0;
uint64_t dir = 0;
uint64_t l = 0;
if (x > 0) {
l = x;
@ -938,11 +911,11 @@ OASISWriter::write_gdelta (const db::Vector &p, double sf)
} else {
unsigned long long d;
uint64_t d;
if (x < 0) {
d = ((unsigned long long) -x << 2) | 3;
d = ((uint64_t) -x << 2) | 3;
} else {
d = ((unsigned long long) x << 2) | 1;
d = ((uint64_t) x << 2) | 1;
}
write (d);
write (y);
@ -1090,8 +1063,8 @@ OASISWriter::end_cblock ()
// RFC1951 compression:
write_byte (0);
write (m_cblock_buffer.size ());
write (m_cblock_compressed.size ());
write ((uint64_t) m_cblock_buffer.size ());
write ((uint64_t) 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.
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 ());
for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) {
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 ());
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);
write_record_id (7);
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.
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 ());
for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) {
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));
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);
write_record_id (9);
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);
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
write_nstring (cell_nstring (*cell));
if (! sequential) {
write ((unsigned long) *cell);
write ((uint64_t) *cell);
}
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
// 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 ());
for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) {
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));
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);
write_record_id (5);
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_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write ((uint64_t) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
write ((uint64_t) l->second.datatype);
write_record_id (12);
write_nstring (l->second.name.c_str ());
write_byte (3);
write ((unsigned long) l->second.layer);
write ((uint64_t) l->second.layer);
write_byte (3);
write ((unsigned long) l->second.datatype);
write ((uint64_t) l->second.datatype);
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 ();
}
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
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;
options.get_cells (layout, cell_set, layers);
create_cell_nstrings (layout, cell_set);
// create a cell index vector sorted bottom-up
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 ());
}
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 ()));
write_record_id (13); // CELL
write ((unsigned long) *cell);
write ((uint64_t) *cell);
reset_modal_variables ();
@ -1696,8 +1696,8 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
write_record_id (28);
write_byte (char (0xf6));
unsigned long pnid = 0;
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
uint64_t pnid = 0;
std::map <std::string, uint64_t>::const_iterator pni = m_propnames.find (klayout_context_name);
if (pni == m_propnames.end ()) {
pnid = m_propname_id++;
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 ((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) {
write_byte (14); // b-string by reference number
unsigned long psid = 0;
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
uint64_t psid = 0;
std::map <std::string, uint64_t>::const_iterator psi = m_propstrings.find (*c);
if (psi == m_propstrings.end ()) {
psid = m_propstring_id++;
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
write_byte (1);
write (cellnames_table_pos);
write ((uint64_t) cellnames_table_pos);
// textstrings
write_byte (1);
write (textstrings_table_pos);
write ((uint64_t) textstrings_table_pos);
// propnames
write_byte (1);
write (propnames_table_pos);
write ((uint64_t) propnames_table_pos);
// propstrings
write_byte (1);
write (propstrings_table_pos);
write ((uint64_t) propstrings_table_pos);
// layernames
write_byte (1);
write (layernames_table_pos);
write ((uint64_t) layernames_table_pos);
// xnames (not used)
write_byte (1);
@ -1875,11 +1875,11 @@ OASISWriter::write (const Repetition &rep)
if (g <= 1) {
write_byte (10);
write (iterated->size () - 1);
write ((uint64_t) iterated->size () - 1);
g = 1;
} else {
write_byte (11);
write (iterated->size () - 1);
write ((uint64_t) iterated->size () - 1);
write_ucoord (g, 1.0);
}
@ -1909,39 +1909,39 @@ OASISWriter::write (const Repetition &rep)
if (b.x () == 0 && b.y () >= 0) {
write_byte (3);
write (bmax - 2);
write ((uint64_t) bmax - 2);
write_ucoord (b.y ());
} else if (b.y () == 0 && b.x () >= 0) {
write_byte (2);
write (bmax - 2);
write ((uint64_t) bmax - 2);
write_ucoord (b.x ());
} else {
write_byte (9);
write (bmax - 2);
write ((uint64_t) bmax - 2);
write_gdelta (b);
}
} else if (b.x () == 0 && b.y () >= 0 && a.y () == 0 && a.x () >= 0) {
write_byte (1);
write (amax - 2);
write (bmax - 2);
write ((uint64_t) amax - 2);
write ((uint64_t) bmax - 2);
write_ucoord (a.x ());
write_ucoord (b.y ());
} else if (b.y () == 0 && b.x () >= 0 && a.x () == 0 && a.y () >= 0) {
write_byte (1);
write (bmax - 2);
write (amax - 2);
write ((uint64_t) bmax - 2);
write ((uint64_t) amax - 2);
write_ucoord (b.x ());
write_ucoord (a.y ());
} else {
write_byte (8);
write (amax - 2);
write (bmax - 2);
write ((uint64_t) amax - 2);
write ((uint64_t) bmax - 2);
write_gdelta (a);
write_gdelta (b);
@ -1985,7 +1985,7 @@ OASISWriter::write_inst_with_rep (const db::CellInstArray &inst, db::properties_
if (info & 0x80) {
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 ()) {
@ -2191,7 +2191,7 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
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
// 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 (pvl.size () >= 15) {
write ((unsigned long) pvl.size ());
write ((uint64_t) pvl.size ());
}
// 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];
@ -2234,27 +2234,27 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
} else if (v.is_longlong ()) {
write_byte (9);
write (v.to_longlong ());
write ((int64_t) v.to_longlong ());
} else if (v.is_ulonglong ()) {
write_byte (8);
write (v.to_ulonglong ());
write ((uint64_t) v.to_ulonglong ());
} else if (v.is_long ()) {
write_byte (9);
write (v.to_long ());
write ((int64_t) v.to_long ());
} else if (v.is_ulong ()) {
write_byte (8);
write (v.to_ulong ());
write ((uint64_t) v.to_ulong ());
} else {
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
// create new ID's.
@ -2335,7 +2335,7 @@ OASISWriter::write_pointlist (const std::vector<db::Vector> &pointlist, bool for
// manhattan pointlist
write_byte (type);
size_t implicit = for_polygons ? 1 : 0;
write ((unsigned long) (pointlist.size () - implicit));
write ((uint64_t) (pointlist.size () - implicit));
db::Vector plast (0, 0);
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
write_byte (4);
write ((unsigned long) pointlist.size ());
write ((uint64_t) pointlist.size ());
db::Vector plast (0, 0);
if (m_sf == 1.0) {
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 ();
unsigned long text_id = 0;
std::map <std::string, unsigned long>::const_iterator ts = m_textstrings.find (text.string ());
uint64_t text_id = 0;
std::map <std::string, uint64_t>::const_iterator ts = m_textstrings.find (text.string ());
if (ts == m_textstrings.end ()) {
text_id = m_textstring_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);
if (info & 0x40) {
mm_text_string = text.string ();
write ((unsigned long) text_id);
write ((uint64_t) text_id);
}
if (info & 0x01) {
mm_textlayer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_texttype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
if (info & 0x10) {
mm_text_x = trans.disp ().x ();
@ -2496,11 +2496,11 @@ OASISWriter::write (const db::SimplePolygon &polygon, db::properties_id_type pro
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
if (info & 0x20) {
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) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
if (info & 0x20) {
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) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
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) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
if (info & 0x20) {
mm_circle_radius = hw;
@ -2853,11 +2853,11 @@ OASISWriter::write (const db::Path &path, db::properties_id_type prop_id, const
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
if (info & 0x40) {
mm_path_halfwidth = hw;
@ -3046,11 +3046,11 @@ OASISWriter::write (const db::Edge &edge, db::properties_id_type prop_id, const
if (info & 0x01) {
mm_layer = m_layer;
write ((unsigned long) m_layer);
write ((uint64_t) m_layer);
}
if (info & 0x02) {
mm_datatype = m_datatype;
write ((unsigned long) m_datatype);
write ((uint64_t) m_datatype);
}
if (info & 0x40) {
mm_path_halfwidth = 0;

View File

@ -208,14 +208,15 @@ private:
tl::OutputMemoryStream m_cblock_buffer;
tl::OutputMemoryStream m_cblock_compressed;
bool m_in_cblock;
unsigned long m_propname_id;
unsigned long m_propstring_id;
unsigned long m_textstring_id;
uint64_t m_propname_id;
uint64_t m_propstring_id;
uint64_t m_textstring_id;
bool m_proptables_written;
std::map <std::string, unsigned long> m_textstrings;
std::map <std::string, unsigned long> m_propnames;
std::map <std::string, unsigned long> m_propstrings;
std::map <std::string, uint64_t> m_textstrings;
std::map <std::string, uint64_t> m_propnames;
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;
@ -223,23 +224,23 @@ private:
modal_variable<db::cell_index_type> mm_placement_cell;
modal_variable<db::Coord> mm_placement_x;
modal_variable<db::Coord> mm_placement_y;
modal_variable<unsigned int> mm_layer;
modal_variable<unsigned int> mm_datatype;
modal_variable<unsigned int> mm_textlayer;
modal_variable<unsigned int> mm_texttype;
modal_variable<uint32_t> mm_layer;
modal_variable<uint32_t> mm_datatype;
modal_variable<uint32_t> mm_textlayer;
modal_variable<uint32_t> mm_texttype;
modal_variable<db::Coord> mm_text_x;
modal_variable<db::Coord> mm_text_y;
modal_variable<std::string> mm_text_string;
modal_variable<db::Coord> mm_geometry_x;
modal_variable<db::Coord> mm_geometry_y;
modal_variable<db::Coord> mm_geometry_w;
modal_variable<db::Coord> mm_geometry_h;
modal_variable<db::coord_traits<db::Coord>::distance_type> mm_geometry_w;
modal_variable<db::coord_traits<db::Coord>::distance_type> mm_geometry_h;
modal_variable< std::vector<db::Vector> > mm_polygon_point_list;
modal_variable<db::Coord> mm_path_halfwidth;
modal_variable<db::Coord> mm_path_start_extension;
modal_variable<db::Coord> mm_path_end_extension;
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<std::string> mm_last_property_name;
modal_variable<bool> mm_last_property_is_sprop;
@ -248,6 +249,9 @@ private:
OASISWriterOptions m_options;
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_byte (char b);
void write_bytes (const char *b, size_t n);
@ -273,19 +277,17 @@ private:
void write (double d);
void write (float d);
void write (long n);
void write (unsigned long n);
void write (long long n);
void write (unsigned long long n);
void write (int64_t n);
void write (uint64_t 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);

View File

@ -662,20 +662,37 @@ TEST(Bug_1799)
EXPECT_EQ (ps2.value (pn).to_string (), "hello, world!");
}
// Modified in #2088 to give a warning
TEST(DuplicateCellname)
{
db::Manager m (false);
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);
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);
}
// 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 ());
data.setup_importer (&importer);
check_dbu (data.dbu);
importer.read (layout);
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)
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))
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)
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))
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(p2, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin)
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(p12, hstep(2.0, 1.0), vstep(-1.0, 2.0), auto_origin)
f2.fill(p13)
f2.fill(p14, hstep(2.0, 0), vstep(0, 2.0))
l1.output(1, 0)
f1.output(10, 0)

View File

@ -11,8 +11,12 @@ l1 = input(1, 0)
f1 = l1
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)
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
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
load("test_epilogue.rb")