;; This shell should be available only for hcmos9a

when(piGetInfo(getShellEnvVar("DKITROOT") "process")==list("hcmos9lp_HV_M4_5V0")
  hiSetBindKey("Layout" "<Key>F7" "DKmisc_slottedShield()")
  hiSetBindKey("Layout" "<Key>F8" "DKmisc_reportDensities()")
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; $Id: shield.il,v 1.4 2005/01/20 08:48:58 teppo Exp $
;
; Description:  Procedures to create slotted shield on metal layer.
;
; $Author: teppo $
; $Date: 2005/01/20 08:48:58 $
; Language:     Skill
; Package:      nil
; MainFun:      N/A
; Status:       Experimental (Do Not Distribute)
;
; (C) Copyright 2004, ST Microelectronics, all rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Revision 1.5  2005/01/26 10:48:58  jerome
; Addition of report form after slot generation. 
; Also proposing selected layer as the default one in the metal layers list.
;
; Revision 1.4  2005/01/20 08:48:58  teppo
; Addition of enclosure parameter. Also refining the hole exclusion procedure.
;
; Revision 1.3  2005/01/17 11:08:46  teppo
; Changed how the temporary cellview is created. Now it is a scratch cv and will disappear after use
;
; Revision 1.2  2005/01/05 08:30:31  teppo
; Cleanup
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The following procedure computes ;
; the ratio area/area_ref for each ;
; metal layer, where area_ref is   ;
; the area of the selected object  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_reportDensities()

  prog( ( check_layers work_cw curr_cw area LP1 LP2 from_layer orig_ref_obj percentage
         polygon ref_area ref_obj result shapes_clean to_layer layer )

    curr_cw=geGetEditCellView()

    orig_ref_obj=car( geGetSelectedSet() )

    unless( orig_ref_obj
      hiDisplayAppDBox( 
        ?name 'wserror
        ?dboxBanner "Metal Density Calculator"
        ?dboxText "Nothing selected in the active window.\nYou must select a seed object for calculation."
        ?dialogType hicErrorDialog
        ?buttonLayout 'Close
      )
      return(0)
    )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; This is the only technology dependent part! ;
    ; List of layer porposes that are counted in  ;
    ; density calculation.                        ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    check_layers='(
       ("poly"    "drawing" "dummy" "fsizing" "nosizing" "dm" "hd")
       ("active"  "drawing" "dummy" "fsizing" "nosizing" "dm" "hd")
       ("metal1"  "drawing" "dummy" "fsizing" "nosizing" "dm" "hd")
       ("metal2"  "drawing" "dummy" "fsizing" "nosizing" "dm" "hd")
       ("metal3"  "drawing" "dummy" "fsizing" "nosizing" "dm" "hd")
       ("metal4B" "drawing" "dummy" "fsizing" "nosizing" "dm" "hd")
       ("alucap"  "drawing" "dummy" "fsizing" "nosizing" "mim" "pin" )
    )
       
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Selected shape is copied as a polygon in ;
    ; layer_y1 in a temporary cellView         ; 
    ; Its area is considered as the reference  ;
    ; area                                     ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    work_cw = dbOpenCellViewByType( curr_cw->libName curr_cw->cellName "DKmisc_temporary"  curr_cw->cellViewType "s")

    if( orig_ref_obj->isShape then
      ref_obj=dbCopyFig( orig_ref_obj work_cw )
      ref_obj->lpp=list("y1" "drawing")
    else
      ref_obj=dbCreateRect(work_cw list("y1" "drawing") orig_ref_obj->bBox)
    )

    ref_obj=leConvertShapeToPolygon(ref_obj)
    ref_area=DKmisc_polygonArea(ref_obj)
    result=""

    foreach( lppairs check_layers

      area = 0 ; area for current lppairs ;

      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ; Temporary shapes in layers layer_y2 and ;
      ; layer_y3 are first purged               ;
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      LP1=car(exists(lpp work_cw~>layerPurposePairs (lpp~>layerName=="y2" && lpp~>purpose=="drawing")))
      LP2=car(exists(lpp work_cw~>layerPurposePairs (lpp~>layerName=="y3" && lpp~>purpose=="drawing")))
      foreach(shape LP1~>shapes
        dbDeleteObject(shape)
      )
      foreach(shape LP2~>shapes 
        dbDeleteObject(shape)
      )

      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ; All shapes in current layer to ;
      ; check are copied in layer_y2   ;
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      layer=car(lppairs)
      foreach( purpose cdr(lppairs)
        from_layer=list(layer purpose)
        to_layer=list("y2" "drawing")
        DKmisc_copyShapes(curr_cw work_cw from_layer to_layer to_layer nil)
      )

      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ; layer_y3 = AND ( layer_y1 layer_y2 ) ;
      ; LP2 = mergeShapes( layer_y3 )        ;
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      leLayerAnd( work_cw  list("y1" "drawing") list("y2" "drawing") list("y3" "drawing") )
      LP2=car(exists(lpp work_cw~>layerPurposePairs (lpp~>layerName=="y3" && lpp~>purpose=="drawing")))
      shapes_clean=leMergeShapes(LP2~>shapes)
      foreach( shape shapes_clean
        polygon=leConvertShapeToPolygon(shape)
        area = area + DKmisc_polygonArea(polygon)
      )
      percentage = 100 * area / ref_area
      sprintf(result "%s%s == %.1f%%\n" result layer percentage)
    ) ; end of lppairs foreach

    dbClose(work_cw)

    hiDisplayAppDBox( 
           ?name 'dada
           ?dboxBanner "Metal Densities Report"
           ?dboxText result
           ?dialogType hicInformationDialog
           ?buttonLayout 'Close
    )
  ) ; end of prog
) ; end of DKmisc_reportDensities procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The following procedure computes ;
; the area of a polygon            ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_polygonArea(object)

  prog( (points area first previous)

    when( object~>type != "geomInst" && object~>shape != "polygon"
      printf("Slot Generator Error: Illegal object type, object must be a polygon!\n")
      return()
    )
    
    points = object~>points
    area = 0
    first = previous = car(points)
    points = cdr(points)

    foreach( point points
      area = area + car(previous)*cadr(point) - car(point)*cadr(previous)
      previous = point
    )
    area = area + car(previous)*cadr(first) - car(first)*cadr(previous)
    area = 0.5 * abs(area)
    return(area)
  ) ; end of prog
) ; end of DKmisc_polygonArea procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The following procedure calls    ;
; slot generator when at least one ;
; object is selected.              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_slottedShield()
  let( (curr_cw ref_obj)

    curr_cw=geGetEditCellView()
    ref_obj=car( geGetSelectedSet() ) 

    if( ref_obj then
      DKmisc_shieldForm(curr_cw ref_obj)
    else
      hiDisplayAppDBox( 
        ?name 'wserror
        ?dboxBanner "Slotted shield generator"
        ?dboxText "Nothing selected in the active window.\nYou must select a seed object for the shield."
        ?dialogType hicErrorDialog
        ?buttonLayout 'Close
      )
    )
  ) ; end of let
) ; end of DKmisc_slottedShield procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The following procedure checks if the selected object ;
; is in the same layer as the destination one and calls ;
; slot generator procedure.                             ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_makeShield( curr_cw orig_ref_obj form_id )
  if( orig_ref_obj->isShape && orig_ref_obj->layerName == form_id->layer->value 
  then
    hiDisplayAppDBox( 
      ?name 'wserror
      ?dboxBanner "Slotted shield generator"
      ?dboxText "The seed object is on the same layer as the resulting shield.\nThe seed object will be removed."
      ?dialogType hicWarningDialog
      ?callback "DKmisc_makeShield2(curr_cw orig_ref_obj form_id 1)"
      ?buttonLayout 'OKCancel
    )
  else
    DKmisc_makeShield2(curr_cw orig_ref_obj form_id nil)
  )
) ; end of DKmisc_makeShield procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Slot Generator Procedure ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_makeShield2( curr_cw orig_ref_obj form_id delete_ref )
  let( ( to_layer spacing layerName purpose ex_purpose excludes connect_net 
         ref_net ref_obj kopio dbIdList final work_cw work_pur special_layer result
         enclosure mfg_grid holebox ref_area areaHole areaShape polygon )

    result=""
    purpose="drawing"
    work_pur="drawing"
    layerName=form_id->layer->value
    to_layer=list("y1" "drawing")
    special_layer=list("y7" "drawing")
    spacing=form_id->keepout->value
    connect_net=form_id->netname->value     
    enclosure=form_id->holeenc->value

    mfg_grid=techGetLayerMfgResolution( techGetTechFile(curr_cw) layerName )

    ;mexclude purposes for different metal layers ;
    excludes= '( ("metal1" "m1")
                 ("metal2" "m2")
                 ("metal3" "m3")
                 ("metal4B" "m4B")
                 ("metal4" "m4")
                 ("metal5" "m5")
                 ("metal6" "m6")
                 ("alucap" "alucap")                                     
               )

    ; purpose of exclude layer that corresponds to layerName ;
    ex_purpose=cadr( assoc( layerName excludes ) ) ;

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; A temporary cellView is open and selected ;
    ; shape is saved in this view in layer_y2   ;
    ; as a polygon.                             ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    work_cw=dbOpenCellViewByType( curr_cw->libName curr_cw->cellName "DKmisc_temporary" curr_cw->cellViewType "s")
    if( orig_ref_obj->isShape then
      ref_obj=dbCopyFig( orig_ref_obj work_cw )
      ref_obj->lpp=list( "y2" work_pur )
      when( delete_ref ; do we remove original shape ? ;
        dbDeleteObject( orig_ref_obj )
      )
    else
      ref_obj=dbCreateRect(work_cw list("y2" work_pur) orig_ref_obj->bBox)
    )
    ref_obj=leConvertShapeToPolygon(ref_obj)
    ref_area=DKmisc_polygonArea(ref_obj)

    ; Original net ;
    if( connect_net != "" then
      ref_net=dbFindNetByName(curr_cw connect_net)
    else
      ref_net=nil
    )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Shapes from target layer are copied into destination layer y1 ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    DKmisc_copyShapes( curr_cw work_cw list(layerName purpose) to_layer special_layer connect_net )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; building layer_y4 as an exclusion layer ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Enlarged shapes by 'spacing' in layer_y1 are added in layer layer_y1 ;
    leLayerSize( work_cw list("y1" work_pur) spacing list("y1" work_pur) )

    ; Enlarged shapes by 'enclosure-mfg_grid' in layer_y1 are created in layer layer_y4 ;
    leLayerSize( work_cw list("y1" work_pur) enclosure-mfg_grid list("y4" work_pur) )

    ; Shapes in layer_y7 (special layer) are added in layer layer_y4 ;
    leLayerSize( work_cw list("y7" work_pur) 0 list("y4" work_pur) )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; mexclude layers are copied in layer_y1 ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    DKmisc_copyShapes( curr_cw work_cw list("mexclude" ex_purpose) to_layer special_layer connect_net)

    ; We construct the ring of the original shape (width = enclosure - mfg_grid) in layer_y4 ;
    ; Shape in layer_y2 (original shape) is reduced in layer layer_y3 by (- enclosure + mfg_grid) ;
    leLayerSize( work_cw list("y2" work_pur) -enclosure+mfg_grid list("y3" work_pur) )
    ; layer_y4 = XOR( layer_y2 layer_y3 ) => resulting ring added to previous layer_y4 shapes ;
    leLayerXor( work_cw  list("y2" work_pur) list("y3" work_pur) list("y4" work_pur) )

    holebox=ref_obj->bBox
    holebox=list( list( caar(holebox) + enclosure  cadar(holebox) + enclosure) 
                  list( caadr(holebox) - enclosure cadadr(holebox) - enclosure) )

    dbIdList = rodFillBBoxWithRects(    
                  ?cvId               work_cw
                  ?layer              list("y3" work_pur)
                  ?fillBBox           holebox
                  ?width              form_id->holew->value
                  ?length             form_id->holew->value
                  ?spaceX             form_id->holes->value
                  ?spaceY             form_id->holes->value
                  ?returnBoolean      nil
               ) ; end rodFillBBoxWithRects

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Holes are created in layer layer_y1 ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    areaHole = 0
    foreach( hole dbIdList
      ; Does hole overlap with exclude layers ? ;
      unless( dbGetTrueOverlaps( work_cw hole~>bBox list("y4" work_pur) ) 
        hole->lpp=list("y1" work_pur)
        polygon=leConvertShapeToPolygon(hole)
        areaHole = areaHole + DKmisc_polygonArea(polygon)
      )
    )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; final_layer = AND_NOT( layer_y2 layer_y1 ) ; 
    ;   where:                                   ;
    ;   - layer_y1 = holes + exclude layers      ;
    ;   - layer_y2 = original object             ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    final=leLayerAndNot( work_cw list("y2" work_pur) list("y1" work_pur) list("y6" work_pur) )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Resulting shapes are copied into target layer ;
    ; Connectivity is added if needed.              ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    areaShape=0
    foreach( shape final
      areaShape = areaShape + DKmisc_polygonArea(shape)
      kopio=dbCopyFig( shape curr_cw )
      kopio->lpp=list( layerName "drawing" )
      when( ref_net
        dbAddFigToNet(kopio ref_net)
      )
    )
    dbClose(work_cw)

    result = strcat( sprintf(nil "Metal density: %.1f%%\n" 100 * areaShape / ref_area) "\n\n" )
    result = strcat( result  sprintf(nil "Hole density: %.1f%%\n" 100*(1-areaShape/ref_area) ) )
    hiDisplayAppDBox(
           ?name 'report
           ?dboxBanner "Metal Density Report"
           ?dboxText result
           ?dialogType hicInformationDialog
           ?buttonLayout 'Close
    )
  ) ; end of let
) ; end of DKmisc_makeShield2 procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Main Slot Generator Form ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_shieldForm(cwid ref_obj)
  let( ( wsForm layerlist lay_purposes defLayer defNet )

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; we get layers of type: metal ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    layerlist=nil
    lay_purposes = techGetLayerFunctions( techGetTechFile(cwid) )
    foreach(lay lay_purposes
      when( cadr(lay)=="metal"
        layerlist=cons( car(lay) layerlist )
      )
    )

    if( member( car(ref_obj~>lpp) layerlist )  then
      defLayer = car(ref_obj~>lpp)
    else
      defLayer = car(layerlist)
    )
   
    defNet="" 
    if( ref_obj~>net then
      defNet = ref_obj~>net~>name
    )
 
    ;;;;;;;;;;;;;;;;;;
    ; we create Form ;
    ;;;;;;;;;;;;;;;;;;
    hiCreateAppForm(
      ?name 'wsForm
      ?fields list(
        hiCreateCyclicField(
          ?name 'layer
          ?choices layerlist
          ?prompt "Select layer for shield"
          ?defValue defLayer 
        )
        hiCreateFloatField(
          ?name 'holew
          ?defValue 3.0
          ?prompt "Hole size"
          ?callback "DKmisc_updateRatio(wsForm)"
        )
        hiCreateFloatField(
          ?name 'holes
          ?defValue 5.0
          ?prompt "Hole spacing"
          ?callback "DKmisc_updateRatio(wsForm)"
        )
        hiCreateFloatField(
          ?name 'holeenc
          ?defValue 1.0
          ?prompt "Hole enclosure by shield"
        ;  ?callback "DKmisc_updateRatio(wsForm)"
        )
        hiCreateFloatField(
          ?name 'keepout
          ?defValue 3.0
          ?prompt "Keepout spacing"
        )
        hiCreateOutputStringField(
          ?name 'ratio
          ?defValue "86%"
          ?prompt "Rough estimated metal coverage"
        )
        hiCreateStringField(
          ?name 'netname
          ?defValue defNet
          ?prompt "Connect to Net"
        )
      )
      ?formTitle "Slotted Shield Metal Generator"
      ?buttonLayout 'OKCancelDef
      ?callback "DKmisc_makeShield( cwid ref_obj wsForm )"
    )
    hiDisplayForm(wsForm)
  ) ; end of let
) ; end of DKmisc_shieldForm procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The following function refreshes the   ;
; ratio value in slot generator form     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_updateRatio(form_id)
  let( ( w s ratio)
    w=form_id->holew->value
    s=form_id->holes->value
    cond(
      (w <= 0 
         println("Hole size must be positive!")
         form_id->holew->value=0.001 )
      (s <= 0 
         println("Hole spacing must be positive!")
         form_id->holew->value=0.001 )
    )

    w=form_id->holew->value
    s=form_id->holes->value
    ratio=100-100*(w/(s+w))*(w/(s+w))
    sprintf(ratio "%.0f%%" ratio)
    form_id->ratio->value=ratio
    t
  ) ; end of let
) ; end of DKmisc_updateRatio procedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The following procedure copies shapes  ;
; into new view and flatten instances    ;
; before copying inside shapes           ;
; Connectivity is preserved upon request ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
procedure( DKmisc_copyShapes( from_cw to_cw from_layer to_layer special_layer @optional (ignore_net "") ( transform list(list(0 0) "R0" 1.0) ) )
  let( ( kopio LP layerName purpose trans2 )

  layerName=car(from_layer)
  purpose=cadr(from_layer)
  LP=car(exists(lpp from_cw~>layerPurposePairs (lpp~>layerName==layerName && lpp~>purpose==purpose)))

    foreach(shape LP~>shapes
      if(ignore_net != "" && shape->net->name==ignore_net then
        kopio=dbCopyFig(shape to_cw transform)
        kopio->lpp=special_layer
      else
        kopio=dbCopyFig(shape to_cw transform)
        kopio->lpp=to_layer
      )
    )

    ; Now visit all the instances
    foreach(inst from_cw~>instances
      if(leIsContact(inst)  ; Is instance a contact ? ;
      then
        ; Instance is a contact ;
        if(ignore_net != "" && car(inst->instTerms)->net->name == ignore_net 
        then
          trans2=dbConcatTransform(geGetInstTransform(inst) transform) ; Flatten process ;
          DKmisc_copyShapes(inst~>master to_cw from_layer special_layer special_layer ignore_net trans2)
        else                    
          trans2=dbConcatTransform(geGetInstTransform(inst) transform) ; Flatten process ;
          DKmisc_copyShapes(inst~>master to_cw from_layer to_layer special_layer ignore_net trans2)
        )
      else
        trans2=dbConcatTransform(geGetInstTransform(inst) transform) ; Flatten process ;
        DKmisc_copyShapes(inst~>master to_cw from_layer to_layer special_layer ignore_net trans2)
      )
    ) ; end of inst foreach ;
  ) ; end let
) ; end procedure DKmisc_copyShapes
