ruby QA使用Calibre DRC和SmartFill运行脚本

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ruby QA使用Calibre DRC和SmartFill运行脚本相关的知识,希望对你有一定的参考价值。

#!/usr/local/bin/ruby

# 
# QA verification script in Ruby 
# Based on C-Shell version by JJ
# By Kala Kuo
#
# 2016.4.7 Init.
# 2016.4.8 Add folder selection, drc_pre cache.  
#
# ================== TODO =======================
# o | a. Make selection for DWA/IWA/handoff
# o | b. Make drc_pre as central control version. 
# o | c. Requirement (File/Folder) pre-check
# o | d. OASIS version integration 
#   | e. Print/Read specified debug flag
#   | f. Read-in list of GDS/OASIS names as argument 
#

require 'logger'

DEBUG_RUN = false 

# Ref: http://stackoverflow.com/questions/6407141/how-can-i-have-ruby-logger-log-output-to-stdout-as-well-as-file
class MultiDelegator
  def initialize(*targets)
    @targets = targets
  end

  def self.delegate(*methods)
    methods.each do |m|
      define_method(m) do |*args|
        @targets.map { |t| t.send(m, *args) }
      end
    end
    self
  end

  class <<self
    alias to new
  end
end

# ===================================================================================================== #
# ========================================== Functions ================================================ #
# ===================================================================================================== #

def Folder_Check(deck_dir, script_dir, db_dir)
  until File.exist?(deck_dir)
    puts "### Deck folder [#{deck_dir}] is NOT existed." 
    puts "### Specify the path of Deck folder : "
    deck_dir = gets.chomp
  end

  until File.exist?(script_dir)
    puts "### Script folder [#{script_dir}] is NOT existed." 
    puts "### Specify the path of Script folder : "
    script_dir = gets.chomp
  end

  until File.exist?(db_dir)
    puts "### Testcase folder [#{db_dir}] is NOT existed." 
    puts "### Specify the path of Testcase folder : "
    db_dir = gets.chomp
  end
end

# Use for Execution Order 
def Get_FILE_LIST()
  return [
    "test.gds",
    "test.oas",
  ]
end

def Get_TopCell(file)
  topcell = %x[calibredrv -a puts [layout peek #{file} -topcell]].chomp
  topcell = topcell.split("\n")[-1] if topcell.include?("\n")
  return topcell
end

def Get_Format(file)
  format = %x[calibredrv -a puts [layout peek #{file} -format]].chomp
  format = format.split("\n")[-1] if format.include?("\n")
  return format
end

def Get_RunTime(file)
  time = %x[grep -e "--- TOTAL .* REAL TIME = " #{file}].chomp
  return time.include?(" ") ? time.split(' ')[-1] : ""
end

def Make_Sure_Folder_Exist(*dir)
  dir.each do |f|
    %x[mkdir #{f}] if !File.exist?(f)
  end
end

def Cleanup(folder, *files) 
  files.each do |f|
    %x[rm #{folder}/#{f}] if File.exist?("#{folder}/#{f}")
  end
end

def Get_Deck(input_file, input_deck, tmp_deck_name)
  # input_deck should accept GDSII format input
  # If input_file is OASIS format, generate and return OASIS version deck
  if Get_Format(input_file).chomp.include?("OAS")
    %x[sed -e 's/LAYOUT SYSTEM GDSII/LAYOUT SYSTEM OASIS/' #{input_deck} > #{tmp_deck_name}]
    return tmp_deck_name
  end
  return input_deck
end

def Get_Merge_Script(filename)
  if !File.exist?(filename) 
    filename = "_merge_newtop.abst"
    File.open(filename, "w") do |f|
      f.puts "set orgds [lindex $argv 0]"
      f.puts "set dmgds [lindex $argv 1]"
      f.puts "set opfile [lindex $argv 2]"
      f.puts "layout filemerge -in $orgds -in $dmgds -out $opfile -createtop NEWTOP"
    end
  end
  return filename
end

# ===================================================================================================== #
# ========================================== End of Func ============================================== #
# ===================================================================================================== #

class Conf
  attr_accessor :mgc_home, :deck_dir, :script_dir, :drc_pre_dir, :working_dir,
    :db_dir, :out_gds, :out_sum, :drc_out, 
    :run_id, :run_dir, :report_file,
    :fill_deck, :drc_deck, :run_drc 

  def initialize
    # ========================================== CALIBRE SETTING ========================================== #
    wa = Get_Calibre_Folder()
    @mgc_home = wa.include?("_cal_") ? wa : "#{wa}/ic/ic_superproj/aoi/Mgc_home" # "_cal_" is for handoff
    ENV["CALIBRE_HOME"] = ENV["MGC_HOME"] = @mgc_home
    ENV["PATH"]="#{@mgc_home}/bin:/test_suite/dfm/utils/bin/:#{ENV["PATH"]}"
    ENV["LD_LIBRARY_PATH"]="#{@mgc_home}/pkgs/icv_lib/lib64:#{@mgc_home}/lib"
    ENV["TEST_SUITE_TOP"]="/test_suite"
    ENV["DESIGN_DIR"]="/design_dir"

    # ========================================== GENERAL SETTING ========================================== #
    @working_dir  = %x[pwd].chomp
    @db_dir       = "#{@working_dir}/QA_CASE"
    @script_dir   = "#{@working_dir}/scripts"
    @deck_dir     = "#{@working_dir}/deck"
    @out_gds      = ENV["OUT_GDS"] = "FEOL.gds"
    @out_sum      = ENV["OUT_SUM"] = "FEOL_fill.sum"
    @drc_out      = ENV["DRC_OUT"] = "DRC_RES.gds"
    @drc_sum      = ENV["DRC_SUM"] = "DRC.sum"
    @run_id       = "run_#{wa.split('/').last}_#{Time.now.strftime("%Y%m%d-%H%M")}"
    @run_dir      = "#{@working_dir}/#{@run_id}"
    @report_file  = "#{@run_dir}/compare_drc.txt.gds"
    @fill_deck    = "#{@deck_dir}/SmartFillDeck"
    @drc_deck     = "#{@deck_dir}/DRCDeck"

    # ========================================== FOLDER Check ============================================ #
    Folder_Check(@deck_dir, @script_dir, @db_dir)

    # ========================================== DECK SELECTION ========================================== #
    @fill_deck    = Get_Fill_Deck()
    puts "### RUN_DRC ?   (1/0)"
    @run_drc      = gets.chomp == "1" # true 
    @drc_deck     = Get_Drc_Deck() if @run_drc
    @drc_pre_dir  = "#{@working_dir}/drc_pre/#{File.basename(@drc_deck)}/"
    
    Make_Sure_Folder_Exist(@run_dir, @drc_pre_dir)
  end
 
  # Calibre folder selection
  def Get_Calibre_Folder()
    folder_list = [] + 
      Dir.glob("folder1") + 
      Dir.glob("folder2")

    folder_list.each_with_index do |f,i|
      exist = File.exist?("#{f}/ic/ic_superproj/aoi/Mgc_home") ? "YES" : "NO"
      puts "[#{i}] [MGC_HOME:#{exist}] #{f}"
    end

    puts "\n### Which Calibre folder to use ? ###\n"
    return folder_list[gets.chomp.to_i] 
  end

  # Determine SmartFill Deck 
  def Get_Fill_Deck()
    filldecks = Dir.glob("#{@deck_dir}/*CalibreYE*[^~]").reverse
    filldecks.each_with_index do |f,i|
      puts "[#{i}] #{f}"
    end
    idx = ""
    until !idx.empty?
      puts "\n### Which SmartFill deck to use ? ###\n"
      idx = gets.chomp
    end
    return filldecks[idx.to_i]
  end

  # Determine DRC Deck 
  def Get_Drc_Deck()
    drcdecks = Dir.glob("#{@deck_dir}/*CLN*[^~]").reverse
    drcdecks.each_with_index do |f,i|
      puts "[#{i}] #{f}"
    end
    idx = ""
    until !idx.empty?
      puts "\n### Which DRC deck to use ? ###\n"
      idx = gets.chomp
    end
    return drcdecks[idx.to_i]
  end
end

# ========================================== Script Start ============================================= #

conf = Conf.new

# ===================================================================================================== #
# ========================================== Information ============================================== #
# ===================================================================================================== #

LOGNAME  = "#{conf.run_id}.log"
log_file = File.open(LOGNAME, "w")
logger   = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)

FILE_LIST = Get_FILE_LIST().delete_if { |f| !File.exist?("#{conf.db_dir}/#{f}") } # Specific execution order

logger.info("====================================================================================================")
logger.info("### MGC_HOME     : #{ENV["MGC_HOME"]}       ")
logger.info("### CALIBRE_HOME : #{ENV["CALIBRE_HOME"]}   ")
logger.info("### FILL DECK    : #{conf.fill_deck}        ")
logger.info("### DRC DECK     : #{conf.drc_deck}         ") if conf.run_drc
logger.info("### DB_DIR       : #{conf.db_dir}           ")
logger.info("### SCRIPT       : #{conf.script_dir}       ")
logger.info("### DECK_DIR     : #{conf.deck_dir}         ")
logger.info("### RUN DIR      : #{conf.run_dir}          ")
logger.info("### FILE LIST    : #{FILE_LIST.inspect} ")
logger.info("====================================================================================================")

# ===================================================================================================== #
# =============================================== ENV FLAG ============================================ #
# ===================================================================================================== #
ENV["CALIBRE_DFM_FILL_DEBUG_FILLABLE_REGION_OUTPUT"]="1"

# Result check file
%x[echo "#{conf.script_dir}/check.rb #{conf.drc_deck} #{conf.report_file}" > #{conf.run_id}_check.sh] if conf.run_drc

FILE_LIST.each do |cell| 
  tmp_deck_name = "_tmp_deck" 
  ENV["LAYOUT_PATH"] = $LAYOUT_PATH = "#{conf.db_dir}/#{cell}"
 
  # TOP CELL
  topcell = Get_TopCell($LAYOUT_PATH)
  logger.info("[#{cell}] | TOP_CELL: #{topcell} ... ")

  # Folder/File Preparation
  $CELL_DIR    = "#{conf.run_dir}/#{cell}"
  $DRC_DIR     = "#{$CELL_DIR}/drc"
  $DRC_PRE_DIR = "#{conf.drc_pre_dir}/#{cell}"
  Make_Sure_Folder_Exist($CELL_DIR, $DRC_DIR, $DRC_PRE_DIR)
  Cleanup($CELL_DIR, conf.out_gds, conf.out_sum)
  
  Dir.chdir($CELL_DIR)

  # ========================================== SmartFill ============================================== #
  logger.info("[#{cell}] | SmartFill Run ... ")
  deck_name = Get_Deck($LAYOUT_PATH, conf.fill_deck, tmp_deck_name)
  %x[calibre -drc -hier -turbo -hyper #{deck_name} | tee run_fill.log] if !DEBUG_RUN
  logger.info("[#{cell}] | SmartFill RunTime: %s" % Get_RunTime("run_fill.log"))
  Cleanup($CELL_DIR, tmp_deck_name)  # Delete tmp deck if existed

  # ========================================== MergeFile ============================================== #
  logger.info("[#{cell}] | Merge file ... ")
  merge_script = Get_Merge_Script("#{conf.script_dir}/merge_newtop.abst") # If file not exist, generate one
  %x[calibredrv #{merge_script} #{$LAYOUT_PATH} #{conf.out_gds} Merged.gds | tee run_merge.log] if !DEBUG_RUN

  if conf.run_drc 
    # ========================================== DRC Pre ============================================== #
    if !File.exist?("#{$DRC_PRE_DIR}/#{conf.drc_out}")
      logger.info("[#{cell}] | Pre-DRC ... ")
      Dir.chdir($DRC_PRE_DIR)
      deck_name = Get_Deck($LAYOUT_PATH, conf.drc_deck, tmp_deck_name)
      %x[calibre -drc -hier -turbo -hyper #{deck_name} | tee run_drc.log]
      logger.info("[#{cell}] | Pre-DRC Runtime: %s" % Get_RunTime("run_drc.log"))
      Cleanup($CELL_DIR, tmp_deck_name)  # Delete tmp deck if existed
      Dir.chdir($CELL_DIR)
    else
      logger.info("[#{cell}] | Pre-DRC: Use #{$DRC_PRE_DIR} as reference. ")
    end
    %x[ln -s #{$DRC_PRE_DIR} drc_pre] # Link the drc_pre folder

    # ========================================== DRC Post ============================================== #
    Dir.chdir($DRC_DIR)
    logger.info("[#{cell}] | Post-DRC ... ")
    ENV["LAYOUT_PATH"] = $LAYOUT_PATH = "../Merged.gds"
    deck_name = Get_Deck($LAYOUT_PATH, conf.drc_deck, tmp_deck_name)
    %x[calibre -drc -hier -turbo -hyper #{deck_name} | tee run_drc.log]
    logger.info("[#{cell}] | Post-DRC Runtime: %s" % Get_RunTime("run_drc.log"))
    Cleanup($CELL_DIR, tmp_deck_name)  # Delete tmp deck if existed

    # ========================================== dbdiff ============================================== #
    format1  = Get_Format("#{$DRC_DIR}/#{conf.drc_out}")
    format2  = Get_Format("#{$DRC_PRE_DIR}/#{conf.drc_out}")
    topcell1 = Get_TopCell("#{$DRC_DIR}/#{conf.drc_out}")
    topcell2 = Get_TopCell("#{$DRC_PRE_DIR}/#{conf.drc_out}")
    logger.info("[#{cell}] | dbdiff Stage ... ")
    %x[dbdiff -system #{format1} -design #{$DRC_DIR}/#{conf.drc_out} #{topcell1} -refsystem #{format2} -refdesign #{$DRC_PRE_DIR}/#{conf.drc_out} #{topcell2} -write_xor_rules compare.svrf | tee run_dbdiff.log]
    logger.info("[#{cell}] | dbdiff Runtime: %s" % Get_RunTime("run_dbdiff.log"))
   
    if File.exist?("compare.svrf") 
      # ========================================== DRC Result XOR ============================================== #
      logger.info("[#{cell}] | XOR Stage ... ")
      sedStr='{s/DRC SUMMARY REPORT \"compare.svrf.summary\" HIER/DRC SUMMARY REPORT \"compare.svrf.summary\" \/\/HIER/g; s/DRC CELL NAME YES CELL SPACE XFORM/\/\/DRC CELL NAME YES CELL SPACE XFORM/g ; s/XOR/NOT INTERACT/g }'
      %x[sed -e '#{sedStr}' compare.svrf > a]
      %x[echo "LAYOUT INPUT EXCEPTION SEVERITY PATH_WIDTH_ZERO 0"|cat - a > a1]
      %x[mv a1 compare.svrf]
      %x[calibre -64 -drc -hier -turbo -hyper compare.svrf | tee run_compare.log]
      logger.info("[#{cell}] | XOR Runtime: %s" % Get_RunTime("run_compare.log"))

      # ========================================== Result Gen ============================================== #
      %x[echo "--------------------------------------------" >> #{conf.report_file}]
      %x[echo #{cell} >> #{conf.report_file}]
      %x[grep RULECHECK #{$DRC_DIR}/compare.svrf.summary     >> #{conf.report_file}]
      %x[echo "--------------------------------------------" >> #{conf.report_file}] 
    else 
      logger.error("[#{cell}] | dbdiff Stage : compare.svrf is NOT generated.")
    end
  end
end

Dir.chdir(conf.working_dir)

logger.info("================ QA Run Completed ================\n\n")
log_file.flush # Make sure log_file have data

# Get DRC Result
if conf.run_drc
  %x[sh #{conf.run_id}_check.sh >> #{conf.run_id}_RESULTS]
  %x[echo ================ DRC RESULTS ================ >> #{LOGNAME}]
  %x[cat #{conf.run_id}_RESULTS >> #{LOGNAME}]
  log_file.flush # Make sure log_file have data
end

# Send result by email
%x[mail -s "[QA Run Result] #{conf.run_id}" "kala_kuo@xxx.com" < #{LOGNAME}]

以上是关于ruby QA使用Calibre DRC和SmartFill运行脚本的主要内容,如果未能解决你的问题,请参考以下文章

开源电子书工具Calibre 6.3 发布

电子书本地转换软件 Calibre

Word+calibre制作属于自己的kindle电子书

Epub电子书编辑和合订本Epub拆分,Calibre软件使用

请教关于用calibre给书添加字体的问题

使用Calibre自带工具批量转换电子书格式