how to read DRC min. value?

Hi sir ,
in our team , we not only need to know if the case is out of rule , but we also need what the min. value of out rule issue is.
below is what my code , as you can see , I make a DRC check in first , then let it export result and re-load it into programming to sort and get min value.
But it is not smare way due to we have to run this code 2 times.
in first time, DRC function export the text data.
in second time , using this code to read the text data and get min. value.
do you have another way or smart function to get the same result?

#############################################
def dis(a1x,a1y,b1x,b1y)
distance=(((a1x-b1x)**2 + (a1y-b1y)**2)**0.5).round(3)
return distance
end
############################################
def disMin(a1x,a1y,a2x,a2y,c1x,c1y) 
distanceR=((c1x-a1x)*(a2x-a1x) + (c1y-a1y)*(a2y-a1y)) / ((a1x-a2x)**2 + (a1y-a2y)**2)
    if distanceR <= 0 then
    return dis(a1x,a1y,c1x,c1y)
    else 
    return dis(a2x,a2y,c1x,c1y)
    end
end
###############rule for width check################# 
 def readFile_getdis(filename)
 gap=0.05
 dataR= Array.new()
 dataC=Array.new()
 realdata=Array.new()
 IO.foreach(filename) {
|block| 
      if block.split(":")[0]=="    <value>edge-pair" then
      realdata << (block.split(":")[1].split("<")[0]).gsub('(',"").gsub(')',"").gsub('/','|')   
      dataZ = (block.split(":")[1].split("<")[0]).gsub('(',"").gsub(')',"").gsub('/','|')   
      dataZ_E1=dataZ.split("|")[0]
      dataZ_E2=dataZ.split("|")[1]
      dataZ_E1P1=dataZ_E1.split(";")[0]
      dataZ_E1P2=dataZ_E1.split(";")[1]
      dataZ_E2P1=dataZ_E2.split(";")[0]
      dataZ_E2P2=dataZ_E2.split(";")[1]      
      dataZ_E1P1x=dataZ_E1P1.split(",")[0].to_f
      dataZ_E1P1y=dataZ_E1P1.split(",")[1].to_f
      dataZ_E1P2x=dataZ_E1P2.split(",")[0].to_f
      dataZ_E1P2y=dataZ_E1P2.split(",")[1].to_f
      dataZ_E2P1x=dataZ_E2P1.split(",")[0].to_f
      dataZ_E2P1y=dataZ_E2P1.split(",")[1].to_f
      dataZ_E2P2x=dataZ_E2P2.split(",")[0].to_f
      dataZ_E2P2y=dataZ_E2P2.split(",")[1].to_f
      value1=disMin(dataZ_E1P1x,dataZ_E1P1y,dataZ_E1P2x,dataZ_E1P2y,dataZ_E2P1x,dataZ_E2P1y)
      value2=disMin(dataZ_E1P1x,dataZ_E1P1y,dataZ_E1P2x,dataZ_E1P2y,dataZ_E2P2x,dataZ_E2P2y)
      if value1 > gap then
      dataC << value1
      end

      if value2 > gap then
      dataC << value2
      end

      end
#############################################################      
}
min_value_dataC=dataC.min
puts "min. value is #{min_value_dataC}"
end

##################################################################################
layoutlibrary = RBA::Application::instance.main_window.current_view.active_cellview.name
rulefile="D:\\rulefile.txt"
ubm=input(94,0)
ruleE=26 ##Check space
report("rulechecker",rulefile)
ubm.space(ruleE.micron,projection).output("RuleE")
if File.exist?(rulefile) then
minvalueE=readFile_getdis(rulefile)
end

Comments

  • edited December 2023

    Hi jiunnweiyeh

    Because you request to dump DRC results to a file so the file will be occupied by DRC engine until the script is completed finished, which blocks the readFile_getdis from accessing the contents.

    The way to avoid this is actually simple, since the space check DRC already provides a EdgePairs object, contains all the data required for this distiance calculation so we can drop the text parsing completely.

    The readFile_getdis and disMin can be combined into this:

    ubm    = input(94,0)
    ruleE  = 26
    ubm_sp = ubm.space(ruleE.micron,projection) 
    
    def disMin(eps)
        dist = Float::INFINITY
        eps.each{|ep|
            dist1 = ep.first.distance_abs(ep.second.p1)
            dist2 = ep.first.distance_abs(ep.second.p2)
            dist  = [dist, dist1, dist2].min
        }
        puts "min. value is #{dist}"
    end
    
    disMin(ubm_sp)
    report("rulechecker")
    ubm_sp.output("RuleE")
    

  • Hi @RawrRanger,

    thanks for bringing this up. I noticed that on C++ level, the edge pairs have a distance property, but not on Python or Ruby level. I will add this implementation to the API.

    The C++ implementation computes the distance in a slightly different way, but in normal cases, the results should be same.

    Best regards,

    Matthias

  • Hi @RawrRanger
    Thanks , it is workable.

  • Here is my code for reference .

    ubm    = input(94,0)
    ruleE  = 20
    ubm_sp = ubm.space(ruleE.micron,projection) 
    
    def disMin(eps)
        dist = Float::INFINITY
        eps.each{|ep|
            dist1 = ep.first.distance_abs(ep.second.p1)
            dist2 = ep.first.distance_abs(ep.second.p2)
            dist  = [dist, dist1, dist2].min
        }
        return "#{dist.round(3)}"
    end
    runing_time=0
    check_result=disMin(ubm_sp)
    while check_result.match("Infinity") and runing_time <10
    ruleE  = ruleE+5
    ubm_sp = ubm.space(ruleE.micron,projection) 
    check_result=disMin(ubm_sp)
    runing_time+=1
    end
    puts check_result
    RBA::MessageBox.info("result","min. value is #{check_result}", RBA::MessageBox.b_ok)
    report("rulechecker")
    ubm_sp.output("RuleE")
    
  • Hi @RawrRanger
    May I know what the function for "distance_abs"?
    Yes , I knew that wording should be check what the distance between first / sencond.
    But when I usnig code as below to double confirm the distance .
    dist1 , using distnace_abs function ,
    distance_fp1sp1 , using (((X1-X2)^2+(Y1-Y2)^2))^0.5 to do the cacaulator.
    I got difference answer... :'(

    ubm    = input(94,0)
    ruleE  = 24.8
    ubm_sp = ubm.space(ruleE.micron) 
    
    def disMin(eps)
        dist = Float::INFINITY
        dist_cacu = Float::INFINITY
        eps.each{|ep|
            dist1 = ep.first.distance_abs(ep.second.p1).round(3)
            dist2 = ep.first.distance_abs(ep.second.p2).round(3)
            dist  = [dist, dist1, dist2].min
            fp1x=ep.first.p1.x.to_f
            fp1y=ep.first.p1.y.to_f
            fp2x=ep.first.p2.x.to_f
            fp2y=ep.first.p2.x.to_f
            sp1x=ep.second.p1.x.to_f
            sp1y=ep.second.p1.y.to_f
            sp2x=ep.second.p2.x.to_f
            sp2y=ep.second.p2.x.to_f
            distance_fp1sp1=(((fp1x-sp1x)**2 + (fp1y-sp1y)**2)**0.5).round(3)
            distance_fp1sp2=(((fp1x-sp2x)**2 + (fp1y-sp2y)**2)**0.5).round(3)
            distance_fp2sp1=(((fp2x-sp1x)**2 + (fp2y-sp1y)**2)**0.5).round(3)
            distance_fp2sp2=(((fp2x-sp2x)**2 + (fp2y-sp2y)**2)**0.5).round(3)
            distance_min=[distance_fp1sp1,distance_fp1sp2,distance_fp2sp1,distance_fp2sp2].min
            dist_cacu  = [dist_cacu, distance_min].min
        }
        puts "dist result : #{dist}"
        puts "dist_cacu result : #{dist_cacu}" 
        return "#{dist_cacu}"
    
    end
    runing_time=0
    check_result=disMin(ubm_sp)
    while check_result.match("Infinity") and runing_time <10
    ruleE  = ruleE+5
    ubm_sp = ubm.space(ruleE.micron) 
    check_result=disMin(ubm_sp)
    runing_time+=1
    end
    RBA::MessageBox.info("result","min. value is #{check_result}", RBA::MessageBox.b_ok)
    #report("rulechecker")
    ubm_sp.output("RuleE")
    

  • by the way , I am using Klayout 0.28.12

  • edited December 2023

    sorry for code typo , I update it as below.
    But , same issue I got.. the distance_abs result didn't match with real data.

  • hi sir, sorry , the code should be update as below.
    But same issue,
    dist1 , using distnace_abs function ,
    distance_fp1sp1 , using (((X1-X2)^2+(Y1-Y2)^2))^0.5 to do the cacaulator.
    I got difference answer.

    ubm    = input(94,0)
    ruleE  = 24.8
    ubm_sp = ubm.space(ruleE.micron) 
    
    def disMin(eps)
        dist = Float::INFINITY
        dist_cacu = Float::INFINITY
        eps.each{|ep|
            dist1 = ep.first.distance_abs(ep.second.p1).round(3)
            dist2 = ep.first.distance_abs(ep.second.p2).round(3)
            dist  = [dist, dist1, dist2].min
            fp1x=ep.first.p1.x.to_f
            fp1y=ep.first.p1.y.to_f
            fp2x=ep.first.p2.x.to_f
            fp2y=ep.first.p2.y.to_f
            sp1x=ep.second.p1.x.to_f
            sp1y=ep.second.p1.y.to_f
            sp2x=ep.second.p2.x.to_f
            sp2y=ep.second.p2.y.to_f
            distance_fp1sp1=(((fp1x-sp1x)**2 + (fp1y-sp1y)**2)**0.5).round(3)
            distance_fp1sp2=(((fp1x-sp2x)**2 + (fp1y-sp2y)**2)**0.5).round(3)
            distance_fp2sp1=(((fp2x-sp1x)**2 + (fp2y-sp1y)**2)**0.5).round(3)
            distance_fp2sp2=(((fp2x-sp2x)**2 + (fp2y-sp2y)**2)**0.5).round(3)
            distance_min=[distance_fp1sp1,distance_fp1sp2,distance_fp2sp1,distance_fp2sp2].min
            dist_cacu  = [dist_cacu, distance_min].min
        }
        puts "dist result : #{dist}"
        puts "dist_cacu result : #{dist_cacu}" 
        return "#{dist_cacu}"
    
    end
    runing_time=0
    check_result=disMin(ubm_sp)
    while check_result.match("Infinity") and runing_time <10
    ruleE  = ruleE+5
    ubm_sp = ubm.space(ruleE.micron) 
    check_result=disMin(ubm_sp)
    runing_time+=1
    end
    RBA::MessageBox.info("result","min. value is #{check_result}", RBA::MessageBox.b_ok)
    #report("rulechecker")
    ubm_sp.output("RuleE")
    
  • Hi jiunnweiyeh

    The abs_distance provided from edge returns a minimum distance between point and given line which extends to infinity. Dependes on use case, you will need to add some conditions check to get what you want.

    Here's an example of running result of different min dist extraction method

    Method-1 : use edge.abs_distance only, which does not always get correct results.
    Method-2 : use 4 vertiex from edge pair to find distance, which only works for certain cases.
    Method-3 : combine the concept of both case, dilivers the correct results.

    Both method-1 and 2 failed on case-2 for different reasons. Case-2 returns 2 error EdgePairs EP1 and EP2
    The checked distance is marked in black double arrow.

    Method-1
    Check Edge from EdgePair to points on another sides, the issue of this method is the abs_distance returns distance of a Point to Edge EXTENDED line. additional condition check is required to get good results.

    Method-2
    Check distance of four corner and not actual Edge to point distance.

    Method-3
    Check Point to Edge relation, if point is in direct projection range of the Edge, use abs_distance, otherwise returns Point to closest Edge.Point distance.

    Example as below, this compares three method with 5um spacing check test case, test case gds is as attached.


    def vectorAngle(v1, v2) vp, sp = v1.vprod(v2), v1.sprod(v2) return(Math.atan2(vp, sp) * 180.0 / Math::PI) end def edgePointDist(edge, point) ev1, ev2 = edge.p2 - edge.p1, edge.p1 - edge.p2 pv1, pv2 = point - edge.p1, point - edge.p2 aep1, aep2 = vectorAngle(ev1, pv1).abs, vectorAngle(ev2, pv2).abs return [ (aep1 <= 90 && aep2 <= 90) ? edge.distance_abs(point) : Float::INFINITY, edge.p1.distance(point), edge.p2.distance(point), ].min end def disMin(eps) dist_method1 = Float::INFINITY dist_method2 = Float::INFINITY dist_method3 = Float::INFINITY eps.each{|ep| dist_method1 = [ dist_method1, ep.second.distance_abs(ep.first.p1), ep.second.distance_abs(ep.first.p2), ].min dist_method2 = [ dist_method2, ep.first.p1.distance(ep.second.p1), ep.first.p1.distance(ep.second.p2), ep.first.p2.distance(ep.second.p1), ep.first.p2.distance(ep.second.p2), ].min dist_method3 = [ dist_method3, edgePointDist(ep.first, ep.second.p1), edgePointDist(ep.first, ep.second.p2), edgePointDist(ep.second, ep.first.p1), edgePointDist(ep.second, ep.first.p2), ].min } puts "dist method1 : #{dist_method1.round(3)}" puts "dist method2 : #{dist_method2.round(3)}" puts "dist method3 : #{dist_method3.round(3)}" end ubm = input(94,0) ruleE = 5 ubm_sp = ubm.space(ruleE.micron) disMin(ubm_sp) ubm_sp.output("RuleE")
  • edited December 2023

    Hi @RawrRanger
    Thanks very much for your help , look like it is what I need.
    But in my case , the method 1/3 will get 0..
    I need to check what is better way in my side.
    Thanks your hard work.

  • Here I have some modify , just a little difference..
    hope this will be helpful for everyone.

    def vectorAngle(v1, v2)
        vp, sp = v1.vprod(v2), v1.sprod(v2)
        return(Math.atan2(vp, sp) * 180.0 / Math::PI)
    end
    
    def edgePointDist(edge, point)
        ev1,  ev2  = edge.p2 - edge.p1, edge.p1 - edge.p2
        pv1,  pv2  = point   - edge.p1, point   - edge.p2
        aep1, aep2 = vectorAngle(ev1, pv1).abs, vectorAngle(ev2, pv2).abs
        return [
            (aep1 <= 90 && aep2 <= 90) ? edge.distance_abs(point) : Float::INFINITY,
            edge.p1.distance(point),
            edge.p2.distance(point),
        ].min
    end
    
    def disMin(eps)
        small_value=0.001
        dist_method1 = Float::INFINITY
        dist_method2 = Float::INFINITY
        dist_method3 = Float::INFINITY
        dist_sort=Array.new()
        eps.each{|ep|
    
            dist_method1 = [
                dist_method1,
                ep.second.distance_abs(ep.first.p1),
                ep.second.distance_abs(ep.first.p2),
            ].min
    
            dist_method2 = [
                dist_method2,
                ep.first.p1.distance(ep.second.p1),
                ep.first.p1.distance(ep.second.p2),
                ep.first.p2.distance(ep.second.p1),
                ep.first.p2.distance(ep.second.p2),
            ].min
    
            dist_method3 = [
                dist_method3,
                edgePointDist(ep.first,  ep.second.p1),
                edgePointDist(ep.first,  ep.second.p2),
                edgePointDist(ep.second, ep.first.p1),
                edgePointDist(ep.second, ep.first.p2),
            ].min
        }
        if dist_method1.round(3) > small_value then
        dist_sort << dist_method1.round(3)
        end
    
        if dist_method2.round(3) > small_value then
        dist_sort << dist_method2.round(3)
        end
    
        if dist_method3.round(3) > small_value then
        dist_sort << dist_method3.round(3)
        end
      return dist_sort.min
    end
    
    ubm    = input(94,0)
    ruleE  = 51.04
    ubm_sp = ubm.space(ruleE.micron) 
    min_distance=disMin(ubm_sp) + 0.1
    ubm_sp = ubm.space(min_distance.micron) 
    ubm_sp.output("RuleE")
    RBA::MessageBox.info("Answer","Finally , it is answer  #{min_distance}", RBA::MessageBox.b_ok)
    
  • edited December 2023

    I have just released version 0.28.14 where EdgePair features a method "distance". It will return the minimum distance between every two points on edge 1 and 2.

    It is based on the definition of an euclidian distance of a point from an edge:

    The minimum distance is either the distance of one of the points of edge 1 against edge 2 or vice versa.

    The C++ implementation is this:

    Edge:
    
      distance_type euclidian_distance (const db::point<C> &p)
      {
        if (db::sprod_sign (p - p1 (), d ()) < 0) {
          return p1 ().distance (p);
        } else if (db::sprod_sign (p - p2 (), d ()) > 0) {
          return p2 ().distance (p);
        } else {
          return std::abs (distance (p));
        }
      }
    
    Edge Pair:
    
      distance_type distance () const
      {
        db::edge<C> e1 = first (), e2 = second ();
        if (! e1.intersect (e2)) {
          distance_type d12 = std::min (e2.euclidian_distance (e1.p1 ()), e2.euclidian_distance (e1.p2 ()));
          distance_type d21 = std::min (e1.euclidian_distance (e2.p1 ()), e1.euclidian_distance (e2.p2 ()));
          return std::min (d12, d21);
        } else {
          return 0;
        }
      }
    

    Matthias

  • Hi sir,
    1 issue need your help for DRC function.

    In bumping house , we usually using "circle" or "oval(track)" pattern for UBM layer (Under Bump metal )
    that always not a regular shape like suqare or octagon.
    Yes, that is similar to PCB...not only using a track shape , we also rotate it.....

    as what the programming /code in space .
    sometimes that can't find the min. space between edge to edge in this case.
    Look like that programming wants to checking where the out of rule in pattern edge (shift to on grid )
    to another edge(shift to on grid).

    As below ( this is a Hyperbole picture , just to shown what I means)
    so that will cause the DRC result have little shift (compare with what the real space)around 0.05um.
    (in fact , I didn't know what the gap is , I just base on current pattern to guess that)
    Is any possible way to avoid this gap or make it as shrink /little /reduce as possible?

  • edited December 2023

    Hi jiunnweiyeh,

    I've adjust the Min distance code based on Matthias suggestion, the only difference I've made is this section in epDistance.

        if edge1.length == 0 or edge2.length == 0
            return edge1.p1.distance(edge2.p1)
    

    this length check exist is to avoid the zero distance issue you've mensioned in previous post, which I found is triggered by Edge in EdgePair having length that is 0, in this case the edge to edge calculation is no longer suitable and returns point to point distance instead.

    Example code as below, test cased as attached, return result : 49.729um
    (without checking length, result will be 0um)

    def epDistance (edge, point)
        if (point - edge.p1).sprod_sign(edge.d) < 0
            return edge.p1.distance(point)
    
        elsif (point - edge.p2).sprod_sign(edge.d) > 0
            return edge.p2.distance(point)
    
        else
            return edge.distance(point).abs
    
        end
    end
    
    def eeDistance(edge1, edge2)
        if edge1.length == 0 or edge2.length == 0
            return edge1.p1.distance(edge2.p1)
    
        elsif !(edge1.intersect(edge2))
            return [   
                epDistance(edge2, edge1.p1),
                epDistance(edge2, edge1.p2),
                epDistance(edge1, edge2.p1),
                epDistance(edge1, edge2.p2),
            ].min
        end
        return 0
    end
    
    def disMin(eps)
        dist = []
        eps.each{|ep| dist << eeDistance(ep.first, ep.second)}
        puts "dist : #{dist.min.round(3)}" 
    end
    
    ubm    = input(94,0)
    ruleE  = 50
    ubm_sp = ubm.space(ruleE.micron) 
    disMin(ubm_sp)
    ubm_sp.output("RuleE")
    

    Regarding to the DRC display accuracy issue you've mentioned, this only affects the final display showed on screen
    following example runs a space check on a 4um space object, the result is accurate, but the display result have an offset of 1 dbu on each side.

    DRC EdgePair printed out by function : (6.5,-5;6.5,5) (2.5,2.5;2.5,-2.5)
    DRC result from property window (6.501, -5.001) (2.499, -2.501) (2.499, 2.501) (6.501, 5.001)

    This I think this offset might be implemented on purpose to make display result of small/slim object have better visibility.

    The way to avoid this is actually simple:

    ubm_sp.output("RuleE")
    ubm_sp.polygons.output("RuleE_polygon")  #use this can avoid small offset 
    
  • Hi @RawrRanger
    Thanks very much for your help , I will base on that code to check and building mind.
    Thanks.

  • @jiunnweiyeh and @RawRanger

    Version 0.28.14 now has the EdgePair#distance attribute. Maybe you would like to try this.

    Best regards,

    Matthias

  • @Matthias,
    OK, got it , I will try 0.28.14 version and the function of EdgePair .
    Thanks.

Sign In or Register to comment.