Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding script that convert gds2txt back and forth #68

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions scripts/gds2txt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# GDS/TXT Conversion Documentation

Explains how to use the converter.

## Folder Structure

```text
📦gds2txt
┣ 📦testing
┗ 📜gds_convert.py
```

## Pre-requesties

Before using the script, A tool `python-gdsii` must be installed. Simply you can just type:

```bash
pip install python-gdsii
```

## Explaination

The `gds_convert.py` script takes a gds file and convert it to a text file or bring back a gds file by converting it from a text file.

### **Options**

1. **mode**=to : takes a gds file and convert it to a text file.
2. **mode**=from : bring back a gds file by converting it from a text file.

## Usage

```bash
gds_convert.py (--help| -h)
gds_convert.py (--gds=<layout_path>) (--mode=<mode>) (--txt=<txt_path>)
```

Example:

```bash
gds_convert.py --gds=and_gate.gds --mode=to --txt=and_gate.txt
```

### Options

`--help -h` Print a help message.

`--gds=<gds_path>` The input GDS file path.

`--mode=<mode>` The operation mode (to, form).

`--txt=<txt_path>` The input text file path.
57 changes: 57 additions & 0 deletions scripts/gds2txt/gds_convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright 2022 GlobalFoundries PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Run GDS/TXT converter.

Usage:
gds_convert.py (--help| -h)
gds_convert.py (--gds=<layout_path>) (--mode=<mode>) (--txt=<txt_path>)

Options:
--help -h Print this help message.
--gds=<gds_path> The input GDS file path.
--mode=<mode> The operation mode (to, form).
--txt=<txt_path> The input text file path.
"""

from docopt import docopt
import subprocess

def main():
if args["--gds"]:
gds = args["--gds"]
if args["--txt"]:
txt = args["--txt"]
else:
print("The script must be given a txt file or a path to be able to run conversion")
exit()
else:
print("The script must be given a gds file or a path to be able to run conversion")
exit()

if args["--mode"] == "to":
subprocess.check_call(f"gds2txt {gds} > {txt}", shell=True)
elif args["--mode"] == "from":
subprocess.check_call(f"txt2gds -o {gds} {txt}", shell=True)
else:
print("The script must be given a mode operation ['to', 'from'] only to be able to run conversion")
exit()


if __name__ == "__main__":

# Args
args = docopt(__doc__, version='GDS/TXT converter: 0.1')
main()
31 changes: 31 additions & 0 deletions scripts/gds2txt/testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# GDS/TXT Conversion Testing Documentation

Explains how to test the converter.

## Folder Structure

```text
📦testing
┣ 📦testcases
┣ 📜test_conversion.py
┗ 📜xor.drc
```

## Explaination

The `test_conversion.py` script takes all gds files in `testcases/` and convert them to text files then bring back all gds files by converting them from text files. Reports will be generated by `xor.drc` and finally analyzed to check the correctness of conversion.

## Usage

```bash
python3 test_conversion.py
```

## **Regression Outputs**

A run folder will contain:

- The text files. `<name>.txt`
- The gds files after conversion. `<name>_o.gds`
- A gds file of XOR process `<name>_r.gds`
- A layer database of the XOR file. `<name>_xor.lyrdb`
39 changes: 39 additions & 0 deletions scripts/gds2txt/testing/test_conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2022 GlobalFoundries PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import glob
import subprocess
from datetime import datetime
import xml.etree.ElementTree as ET

gds_files = glob.glob("testcases/*.gds")
current = f'run_{datetime.now().strftime("_%y_%m_%d_%H_%M_%S")}'
subprocess.check_call(f'mkdir {current}', shell=True)

for gds_file in gds_files:
txt_file = gds_file.replace(".gds", ".txt").replace("testcases", current)
out_file = gds_file.replace(".gds", "_o.gds").replace("testcases", current)
rep_file = gds_file.replace(".gds", "_r.gds").replace("testcases", current)
subprocess.check_call(f"gds2txt {gds_file} > {txt_file}", shell=True)
subprocess.check_call(f"txt2gds -o {out_file} {txt_file}", shell=True)
subprocess.check_call(f"klayout -b -r xor.drc -rd gds1={gds_file} -rd gds2={out_file} -rd out={rep_file}", shell=True)
subprocess.check_call(f"klayout -b -r report.drc -rd input={rep_file}", shell=True)

mytree = ET.parse(f'{gds_file.replace(".gds", "").replace("testcases", current)}_xor.lyrdb')
myroot = mytree.getroot()
if len(myroot[7]) == 0:
print(f'## File {gds_file.replace(".gds", "").replace("testcases/", "")} has been converted as GDS > TXT > GDS successfully')

subprocess.check_call(f"rm report.drc", shell=True)
print('\x1b[6;30;42m' + 'Success!' + '\x1b[0m')
Binary file added scripts/gds2txt/testing/testcases/ADDF_X4.gds
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added scripts/gds2txt/testing/testcases/mim_2p0fF.gds
Binary file not shown.
Binary file not shown.
Binary file added scripts/gds2txt/testing/testcases/np_3p3.gds
Binary file not shown.
Binary file added scripts/gds2txt/testing/testcases/npolyf_s.gds
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added scripts/gds2txt/testing/testcases/tm9k.gds
Binary file not shown.
Binary file not shown.
41 changes: 41 additions & 0 deletions scripts/gds2txt/testing/xor.drc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2022 GlobalFoundries PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


# This file is used to generate a XOR file from 2 files
# Usage:
# klayout -b -r xor.drc -rd gds1=a.gds -rd gds2=b.gds -rd out=out.gds

l1 = layout($gds1)
l2 = layout($gds2)

target($out, $out)

File.write("report.drc", "# Get Report of XOR\n\nsource($input)\nreport(\"XOR runset\", \"#{File.basename($gds1, ".*")}_xor.lyrdb\")\n\n")

layers = []
[ l1, l2 ].each do |l|
l.layout.layer_indices.each do |index|
info = l.layout.get_info(index)
layers << [info.layer, info.datatype ]
end
end

# collect layers
layers.sort.uniq.each do |l,d|
log "Running XOR between #{l}/#{d} .."
(l1.input(l, d) ^ l2.input(l, d)).output(l, d)
File.write("report.drc", "l#{l}_#{d} = polygons(#{l},#{d})\n", mode: "a")
File.write("report.drc", "l#{l}_#{d}.output(\"l#{l}_#{d}\",\"l#{l}_#{d}\")\n", mode: "a")
end