# # 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. # # DESCRIPTION: Cadence techfile import for KLayout. # # Run the script with # klayout -rm import_tf.rbm ... # or put the script as "import_tf.rbm" into the installation path (on Unix for version <=0.21: # set $KLAYOUTPATH to the installation folder). # # The script will install a new menu item in the File menu: "Import Cadence Techfile". # It will prompt for a techfile name and, if it does not find a unique display resource file, # for the name of this file. # # CAUTION: the script uses a simple parsing scheme of the techfile by converting it into a # Ruby expression. Hence, no Skill code inside the techfile is evaluated. # require 'stringio' class MenuAction < RBA::Action def initialize( title, shortcut, &action ) self.title = title self.shortcut = shortcut @action = action end def triggered @action.call( self ) end private @action end class TechfileDisplayDefinitions def initialize(_packet) @packet = _packet stipple = nil frame_color = 0x808080 fill_color = 0x808080 width = 1 end attr_reader :packet attr_accessor :stipple attr_accessor :frame_color attr_accessor :fill_color attr_accessor :width end class TechFileLayer def initialize(_lp) @lp = _lp ld = nil visible = false valid = false display = nil end attr_reader :lp attr_accessor :ld attr_accessor :visible attr_accessor :valid attr_accessor :display end def produce_word(expr, word) if word == "t" expr.write("true") elsif word == "nil" expr.write("false") elsif word.length > 0 && word =~ /^-?\d*(\.\d*)?([eE][+\-]?\d*)?$/ expr.write(word) elsif (word =~ /^['"]/) expr.write(word) else expr.write("\"") expr.write(word.gsub(/\\/, "\\\\").gsub(/"/, "\"")) expr.write("\"") end end def read_skill_file_as_ruby_expr(fn) expr = StringIO.new("", "w") expr.write("[") File.open(fn) do |file| file.each_line do |line| state = :reading word = "" line.split(//).each do |c| repeat = true stop = false while repeat repeat = false if state == :reading if c == ";" # drop comments stop = true elsif c == "\"" expr.write(c); state = :quoted elsif c == "\'" expr.write(c); state = :singlequoted elsif c == "(" expr.write("["); elsif c == ")" expr.write("], "); elsif c =~ /\s/ expr.write(c) else word = c state = :read_word end elsif state == :read_word if c == "(" expr.write("[ ") produce_word(expr, word) expr.write(", ") state = :reading elsif c == ")" produce_word(expr, word) repeat = true state = :reading elsif c =~ /\s/ produce_word(expr, word) expr.write(", ") state = :reading repeat = true else word += c end elsif state == :escaped expr.write(c) state = :quoted elsif state == :quoted expr.write(c) if c == "\"" state = :reading expr.write(", ") elsif c == "\\" state = :escaped end elsif state == :singlequoted if c =~ /\s/ state = :reading expr.write("\", ") else expr.write(c) end end end if stop break end end if state == :quoted || state == :singlequoted expr.write("\"") elsif state == :read_word produce_word(expr, word) end end end expr.write("]") return expr.string end $import_tf = MenuAction.new( "Import Cadence Techfile", "" ) do app = RBA::Application.instance mw = app.main_window lv = mw.current_view if lv == nil raise "No view selected" end # Ask for the file name sel_tf_file = RBA::FileDialog.get_open_file_name("Select Cadence Techfile", ".", "Cadence techfiles (*.tf);;Text files (*.txt);;All files (*)") if sel_tf_file.has_value? tf_file = sel_tf_file.value dir = File.dirname(tf_file) drf_files = Dir.glob(File.join(dir, "*.drf")) drf_file = nil if drf_files.length == 1 drf_file = drf_files[0] else sel_drf_file = RBA::FileDialog.get_open_file_name("Select Display Resource File", dir, "Display resource files (*.drf);;All files (*)") if sel_drf_file.has_value? drf_file = sel_drf_file.value end end if !drf_file raise "Unable to locate display resource file" end tf = eval(read_skill_file_as_ruby_expr(tf_file)) drf = eval(read_skill_file_as_ruby_expr(drf_file)) lv.clear_layers lv.clear_stipples display_defs = {} begin colors = {} widths = {} stipples = {} packets = {} drf.each do |section| sname = section.shift if sname == "drDefinePacket" section.each do |defs| if defs.length >= 6 packets[defs[1]] ||= [ defs[2], defs[3], defs[4], defs[5] ] end end elsif sname == "drDefineLineStyle" section.each do |defs| if defs.length >= 3 widths[defs[1]] ||= defs[2] end end elsif sname == "drDefineStipple" section.each do |defs| if defs.length >= 3 pat = [] bits = 1 defs[2].reverse_each do |p| word = 0 bits = p.length p.reverse_each { |b| word = (word << 1) + b } if pat.size < 32 pat.push(word & 0xffffffff) end end stipples[defs[1]] ||= lv.add_stipple(defs[1], pat, bits) end end elsif sname == "drDefineColor" section.each do |defs| if defs.length >= 5 colors[defs[1]] ||= ((defs[2] << 16) + (defs[3] << 8) + defs[4]) end end end end packets.each do |k,v| stipple = stipples[v[0]] fill_color = colors[v[2]] frame_color = colors[v[3]] width = widths[v[1]] width ||= 0 if (fill_color && frame_color && width) dd = (display_defs[k] ||= TechfileDisplayDefinitions.new(k)) dd.stipple = stipple dd.fill_color = fill_color dd.frame_color = frame_color dd.width = width end end end priorities = [] layers = {} has_layers = false tf.each do |section| sname = section.shift if sname == "layerDefinitions" section.each do |defs| dname = defs.shift if dname == "techLayerPurposePriorities" defs.each { |lp| priorities.push(lp) } elsif dname == "techDisplays" defs.each do |td| if td.length >= 8 dd = display_defs[td[2]] if dd lp = [ td[0], td[1] ] tl = (layers[lp] ||= TechFileLayer.new(lp)) tl.display = dd tl.visible = td[3] tl.valid = td[7] end end end end end elsif sname == "layerRules" section.each do |defs| dname = defs.shift if dname == "streamLayers" defs.each do |td| if td.length >= 3 lp = td[0] tl = (layers[lp] ||= TechFileLayer.new(lp)) tl.ld = [ td[1], td[2] ] has_layers = true end end end end end end if !has_layers # no layers in techfile -> try to locate layermap lmap_files = Dir.glob(File.join(dir, "*.layermap")) lmap_file = nil if lmap_files.length == 1 lmap_file = lmap_files[0] else sel_lmap_file = RBA::FileDialog.get_open_file_name("Select Layer Map File", dir, "Layer Map files (*.layermap);;All files (*)") if sel_lmap_file.has_value? lmap_file = sel_lmap_file.value end end if !lmap_file raise "Unable to locate layer map file" end File.open(lmap_file) do |file| file.each_line do |l| l = l.sub(/#.*/, "").sub(/^\s*/, "").sub(/\s*$/, "").gsub(/\s+/, " ") if l != "" ll = l.split(/\s+/) if ll.size >= 3 lp = [ ll[0], ll[1] ] tl = (layers[lp] ||= TechFileLayer.new(lp)) tl.ld = [ ll[2].to_i, (ll[3] || "0").to_i ] end end end end end priorities.each do |lp| ldef = layers[lp] if ldef && ldef.ld && ldef.display lprops = RBA::LayerPropertiesNode.new lprops.source_layer = ldef.ld[0] lprops.source_datatype = ldef.ld[1] lprops.source_cellview = 0 lprops.name = lp[0] + "." + lp[1] + " - " + ldef.ld[0].to_s + "/" + ldef.ld[1].to_s lprops.width = ldef.display.width lprops.frame_color = ldef.display.frame_color lprops.fill_color = ldef.display.fill_color lprops.visible = ldef.visible lprops.dither_pattern = ldef.display.stipple || 1 lv.insert_layer(lv.end_layers, lprops) end end end end app = RBA::Application.instance mw = app.main_window menu = mw.menu menu.insert_item("file_menu.load_layer_props", "import_tf", $import_tf)