Skip to content

Commit 146ab54

Browse files
committed
Initial commit of KLayout util scripts
Signed-off-by: Jeff Ng <[email protected]>
1 parent 120bf65 commit 146ab54

10 files changed

+955
-0
lines changed

flow/scripts/klayout/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Collection of Ruby scripts to generate KLayout files required by ORFS
2+
3+
This directory contains a set of Ruby scripts that will generate the following KLayout files:
4+
5+
- Technology File
6+
- Layer Properties File
7+
8+
using the following files as input:
9+
10+
- Cadence Virtuoso SKILL tech file
11+
- Cadence Virtuoso layer mapping file
12+
- Technology LEF file
13+
14+
## Setup
15+
Copy the following files into ~/.klayout/ruby
16+
17+
- GenericLayerNameMapper.rb
18+
- KLayoutLayerMapGenerator.rb
19+
- KLayoutLayerPropertiesFileGenerator.rb
20+
21+
Also make sure that you have the import_tf.rb file from [tf_import](https://github.com/klayoutmatthias/tf_import) copied there as well. If you follow the instructions at [Guide to Integrate a New Platform into the OpenROAD Flow](https://openroad-flow-scripts.readthedocs.io/en/latest/contrib/PlatformBringUp.html#klayout-properties-file), it will be installed in ~/.klayout/salt/tf_import. Just copy it over to ~/.klayout/ruby, so that the scripts can find it.
22+
23+
## Usage
24+
Then gen_klayout_files.sh is the main driver to generate the files. Its usage is:
25+
26+
```
27+
Usage: gen_klayout_files.sh -v <virtuoso_tech_file> -l <virtuoso_layer_map>
28+
-t <tech_lef> -n <tech_name>
29+
-d <tech_description> -p <klayout_lyp>
30+
-o <klayout_lyt> [-m layer_name_mapper_ruby] [-h]
31+
```
32+
33+
where
34+
35+
- virtuoso_tech_file: Cadence Virtuoso SKILL tech file
36+
- virtuoso_layer_map: Cadence Virtuoso layer mapping file (maps Cadence
37+
layer/purpose pairs to GDS II layer/datatype)
38+
- tech_lef: Cadence technology LEF with layer and via definitions
39+
- tech_name: Name to put in KLayout technology file
40+
- tech_description: Description to put in KLayout technology file
41+
- klayout_lyp: KLayout layer properties file (typically ending in .lyp) -
42+
generated from Virtuoso tech file
43+
- klayout_lyt: Klayout technology file (typically ending in .lyt)
44+
- layer_name_mapper_ruby: Name of Ruby file containing custom layer name mapper
45+
Uses GenericLayerNameMapper.rb by default
46+
47+
For example:
48+
49+
```
50+
gen_klayout_files.sh -v process.tf -l process.layermap -d adv_process.lef \
51+
-n adv_process -d "Really advanced process" -p output.lyp \
52+
-o output.lyt -m AdvProcessLayerNameMapper.rb
53+
```
54+
55+
56+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env ruby
2+
#
3+
# Generic LayerNameMapper
4+
#
5+
6+
require 'optparse'
7+
8+
#
9+
# Class to map layer names (default)
10+
#
11+
class LayerNameMapper
12+
#
13+
# Initializer
14+
#
15+
def initialize()
16+
end
17+
18+
#
19+
# Maps layers to the desired name in the layer map
20+
#
21+
def map_layer_name(design_manual_layer_name, cds_layer_name, cds_purpose_name)
22+
if design_manual_layer_name && design_manual_layer_name.length > 0
23+
return design_manual_layer_name
24+
end
25+
return cds_layer_name
26+
end
27+
28+
#
29+
# Standalone main driver
30+
#
31+
def LayerNameMapper.main()
32+
options = {}
33+
OptionParser.new do |opts|
34+
opts.banner = "Usage: LayerNameMapper.rb -d design_manual_layer_name -l cds_layer_name -p cds_purpose_name"
35+
opts.on("-dDESIGN_MANUAL_LAYER_NAME", "--design_manual_layer_name=DESIGN_MANUAL_LAYER_NAME") do |design_manual_layer_name|
36+
options[:design_manual_layer_name] = design_manual_layer_name
37+
end
38+
opts.on("-lCDS_LAYER_NAME", "--layer_name=CDS_LAYER_NAME") do |cds_layer_name|
39+
options[:cds_layer_name] = cds_layer_name
40+
end
41+
opts.on("-pCDS_PURPOSE_NAME", "--purpose_name=CDS_PURPOSE_NAME") do |cds_purpose_name|
42+
options[:cds_purpose_name] = cds_purpose_name
43+
end
44+
end.parse!
45+
if options[:design_manual_layer_name] && options[:cds_layer_name] &&
46+
options[:cds_purpose_name]
47+
rep = LayerNameMapper.new()
48+
puts rep.map_layer_name(options[:design_manual_layer_name],
49+
options[:cds_layer_name],
50+
options[:cds_purpose_name])
51+
end
52+
end
53+
end
54+
55+
56+
#
57+
# Only call the main driver if we're calling this as a script
58+
#
59+
if __FILE__ == $0
60+
LayerNameMapper.main()
61+
end
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/usr/bin/env ruby
2+
#
3+
# Generates the KLayout layer map file from the Virtuoso Stream Layer Map File
4+
# It's really here to map from the Virtuoso purpose name to KLayout's LEF
5+
# purpose name
6+
#
7+
# Virtuoso Stream Layer Map File Format:
8+
# layer_name purpose_name gds_layer gds_datatype
9+
#
10+
# KLayout Layer Map File Format:
11+
# layer_name lef_purpose_name gds_layer gds_datatype
12+
#
13+
# Usage: KLayoutLayerMapGenerator.rb -i <input_file> -o <output_file>
14+
# [-m <layer_name_mapper_ruby_file>]
15+
#
16+
# layer_name_mapper is the path to a file that implements LayerNameMapper for
17+
# custom layer name mapping
18+
#
19+
# File needs to be located in ~/.klayout/ruby
20+
#
21+
22+
require 'optparse'
23+
24+
#
25+
# Class to generate the layer map from the Virtuoso Stream Layer Map file
26+
#
27+
class KLayoutLayerMapGenerator
28+
#
29+
# Structs used to store data
30+
#
31+
VirtuosoLayerInfo = Struct.new(:cds_lpp, :gds)
32+
VirtuosoLayerPurposePair = Struct.new(:layer_name, :purpose_name)
33+
GDSLayerDataType = Struct.new(:layer, :datatype)
34+
35+
#
36+
# Initializer
37+
#
38+
# layer_map: Stores mapping from layer name to VirtuosoLayerInfo object
39+
#
40+
def initialize(layer_name_mapper_file)
41+
@layer_map = {}
42+
# load in layer_name_mapper dynamically
43+
load layer_name_mapper_file
44+
@layer_name_mapper = LayerNameMapper.new()
45+
end
46+
47+
#
48+
# Reads the Virtuoso Layer Map file
49+
#
50+
def read_virtuoso_layer_map_file(file_name)
51+
fh = File.open(file_name, chomp: true)
52+
if fh
53+
read(fh)
54+
fh.close
55+
else
56+
puts "Error: can't open " + file_name
57+
end
58+
end
59+
60+
#
61+
# Reads the content from a stream
62+
#
63+
# For each layer in the layer mapping file, add an entry into layer_map.
64+
# The key is the mapped layer name and the value is a list of
65+
# VirtuosoLayerInfo objects. The value is a list since there can be multiple
66+
# entries for the same mapped layer name in the Virtuoso map
67+
#
68+
def read(fh)
69+
layer_re = /(\S+)\s+(\S+)\s+(\d+)\s+(\d+)([^#]*)\#\s*(\S+)?/
70+
fh.each_line do | line |
71+
if not line.match(/^#/)
72+
result = line.match(layer_re)
73+
if result
74+
layer_name = result[1]
75+
purpose_name = result[2]
76+
gds_layer = result[3].to_i
77+
gds_datatype = result[4].to_i
78+
design_manual_layer_name = result[6]
79+
key = map_layer_name(design_manual_layer_name, layer_name,
80+
purpose_name)
81+
cds_lpp = VirtuosoLayerPurposePair.new(layer_name, purpose_name)
82+
gds = GDSLayerDataType.new(gds_layer, gds_datatype)
83+
layer_info = VirtuosoLayerInfo.new(cds_lpp, gds)
84+
if @layer_map[key]
85+
@layer_map[key] << layer_info
86+
else
87+
@layer_map[key] = [layer_info]
88+
end
89+
else
90+
if line != "\n"
91+
puts "Skipping: " + line
92+
end
93+
end
94+
end
95+
end
96+
end
97+
98+
#
99+
# Maps layers to the desired name in the layer map
100+
#
101+
def map_layer_name(design_manual_layer_name, cds_layer_name, cds_purpose_name)
102+
return @layer_name_mapper.map_layer_name(design_manual_layer_name, cds_layer_name, cds_purpose_name)
103+
end
104+
105+
#
106+
# Writes the layer map to a file
107+
#
108+
def write_layer_map_file(file_name)
109+
out_fh = File.open(file_name, "w") do |out_fh|
110+
write_layer_map(out_fh)
111+
end
112+
end
113+
114+
#
115+
# Writes the layer map to a file handle
116+
#
117+
def write_layer_map(outfh)
118+
layer_map = get_map()
119+
layer_map.each { | layer_name, layer_list |
120+
layer_list.each { | layer_info |
121+
gds_info = layer_info.gds
122+
outfh.printf("%s %d %d\n", layer_name, gds_info.layer,
123+
gds_info.datatype)
124+
}
125+
}
126+
end
127+
128+
#
129+
# Accessor to return a hash sorted by the gds layer
130+
#
131+
def get_map
132+
sorted_hash = @layer_map.sort_by {|key, value| value[0].gds.layer }
133+
sorted_hash = Hash[sorted_hash]
134+
return sorted_hash
135+
end
136+
137+
#
138+
# Standalone main driver
139+
#
140+
def KLayoutLayerMapGenerator.main
141+
options = {"layer_name_mapper": "GenericLayerNameMapper.rb"}
142+
OptionParser.new do |opts|
143+
opts.banner = "Usage: KLayoutLayerMapGenerator.rb -i input_file -o output_file"
144+
opts.on("-iINPUT_FILE", "--input_file=INPUT_FILE") do |input_file|
145+
options[:input_file] = input_file
146+
end
147+
opts.on("-oOUTPUT_FILE", "--output_file=OUTPUT_FILE") do |output_file|
148+
options[:output_file] = output_file
149+
end
150+
opts.on("-mLAYER_NAME_MAPPER", "--layer_name_mapper=LAYER_NAME_MAPPER") do |layer_name_mapper_ruby_file|
151+
options[:layer_name_mapper_ruby_file] = layer_name_mapper_ruby_file
152+
end
153+
end.parse!
154+
155+
if options[:input_file] && options[:output_file]
156+
rep = KLayoutLayerMapGenerator.new(options[:layer_name_mapper_ruby_file])
157+
rep.read_virtuoso_layer_map_file(options[:input_file])
158+
rep.write_layer_map_file(options[:output_file])
159+
else
160+
puts "Usage: KLayoutLayerMapGenerator.rb -i input_file -o output_file [-m layer_name_mapper]"
161+
exit 1
162+
end
163+
end
164+
end
165+
166+
#
167+
# Only call the main driver if we're calling this as a script
168+
#
169+
if __FILE__ == $0
170+
KLayoutLayerMapGenerator.main()
171+
end
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/env -S klayout -b -r
2+
#
3+
# Generates a KLayout layer properties (.lyp) file from the Virtuoso SKILL Tech
4+
# File
5+
#
6+
# import_tf needs to be in ~/.klayout/ruby, not ~/.klayout/salt
7+
#
8+
# KLayout doesn't support script-specific command line options, so work around
9+
# it with env vars
10+
#
11+
# Usage: KLayoutLayerPropertiesFileGenerator.rb
12+
#
13+
14+
include RBA
15+
require 'import_tf'
16+
17+
#
18+
# Class to generate KLayout layer properties file from Virtuoso tech file
19+
#
20+
class KLayoutLayerPropertiesFileGenerator
21+
#
22+
# Initializer
23+
#
24+
# layout_view: layout view to import tech file into
25+
#
26+
def initialize()
27+
@layout_view = LayoutView.new
28+
end
29+
30+
#
31+
# Reads the Virtuoso tech file
32+
#
33+
def read_virtuoso_tech_file(file_name)
34+
TechfileToKLayout.import_techfile(@layout_view, file_name)
35+
end
36+
37+
#
38+
# Writes the layer properties file
39+
#
40+
def write_layer_properties_file(file_name)
41+
@layout_view.save_layer_props(file_name)
42+
end
43+
44+
#
45+
# Standalone main driver
46+
#
47+
# Uses env vars to get arguments since KLayout doesn't support
48+
# script-specific arguments (e.g. it thinks that all arguments are for it)
49+
#
50+
def KLayoutLayerPropertiesFileGenerator.main
51+
input_file = ENV["VIRTUOSO_TECH_FILE"]
52+
output_file = ENV["KLAYOUT_LAYER_PROPERTIES_FILE"]
53+
54+
if input_file && output_file
55+
rep = KLayoutLayerPropertiesFileGenerator.new()
56+
rep.read_virtuoso_tech_file(input_file)
57+
rep.write_layer_properties_file(output_file)
58+
else
59+
puts "Usage: KLayoutLayerPropertiesFileGenerator.rb"
60+
exit 1
61+
end
62+
end
63+
end
64+
65+
#
66+
# Only call the main driver if we're calling this as a script
67+
#
68+
if __FILE__ == $0
69+
KLayoutLayerPropertiesFileGenerator.main()
70+
end

0 commit comments

Comments
 (0)