!-----------------------------------------------------------------!
!                                                                 !
! PLACE PADS: an ILAP routine to distribute pads around a design. !
!                                                                 !
! George A. McCaskill                  26th August    1982        !
!        ""                Revised:    10th September 1982        !
!        ""                Revisited:  22nd September 1982        !
!        ""                dropped in: 21st February  1983        !
!        ""                stopped by: 11th March     1983        !
!        ""                patched up: 13th June      1983        !
!-----------------------------------------------------------------!

%include "nmos.inc"
%external %string (63) %fn %spec ItoS (%integer A, B)

{#########################################################################}
{#                                                                       #}
{#      This program is part of the ILAP library, and was written in     #}
{#   The Department of Computer Science at the University of Edinburgh   #}
{#       (James Clerk Maxwell Building, Kings Buildings, Edinburgh)      #}
{#                                                                       #}
{#   This software is available free to other educational establisments  #}
{#   but the University of Edinburgh, retains all commercial rights.     #}
{#   It is a condition of having this software is that the sources are   #}
{#   not passed on to any other site, and that Edinburgh University is   #}
{#   given credit in any re-implementations of any of the algorithms     #}
{#   used, or articles published which refer to the software.            #}
{#                                                                       #}
{#   There is no formal support for this software, but any bugs should   #}
{#   be reported to Gordon Hughes or David Rees at the above address,    #}
{#   and these are likely to be fixed in a future release.               #}
{#                                                                       #}
{#########################################################################}

!  The following convention is adopted for               3
!  indicating position :-
!                                               4    directions  2
!
!                                                        1
!
!  For example , the ILAP symbol 'place side3' is positioned 'on top'
! of the given cell.
!
%const %string (7) %array side name (1:4) = "BOTTOM", "RIGHT", "TOP", "LEFT"
%constant %integer %array opposite (1:4) = 3,4,1,2 
%constant %integer %array next     (1:4) = 2,3,4,1
%constant %integer %array prev     (1:4) = 4,1,2,3

%external %routine place pads %alias "ILAP_PLACE_PADS" ( %string(31) chipname,
   cellname, %record(connf)%array %name ports, %integer n )
  !
  ! the cell's ports are ordered into 4 sides, with portf pointing into the
  ! sorted PORTS parameter.
  !
  %record %format portf  ( %integer start, finish )

  %record %format cellf  ( %integer %array offset (1:4),
                           %record(portf)%array ports (1:4),
                           %integer %array w (1:4),
                           %integer dx , dy )

  %record(cellf) cell                  { user's cell        }

  !  
  ! The four pad blocks are represented as linked lists, the free list
  ! is assembled from a pool of exactly n pad items.
  !
  %record %format padf ( %integer type, width, %record(padf)%name next )
  !
  %record(padf)%array free pads (1:n)
  %record(padf)%name fp                         { free pointer }
 
  ! The four pad blocks are accesed from the 'side' array
  !
  %record %format sidef ( %record(padf)%name first, { pointer to pad list    }
                          %string(4) layer,         { routing layer          }
                          %integer n,               { number of pads         }
                                   separation,      { total routing size     }
                                   ground rail,     { height of rail         }
                                   pushed,          { side pushing flag      }
                                   corner,          { height at corner       }
                                   px,              { amount pushed to side  }
                                   ed,
                                   em,              { extra diffusion, metal }
                                   drouted,
                                   mrouted,         { routing flags          }
                                   height,          { dimensions             }
                                   width,
                                   metal width,     { width of routing       }
                                   x                { displacement           })

  %record(sidef)%array side (1:4)      { four sides of pads }

  ! Record for describing position and dimension of 'slots'
  !
  ! a slot is an area between the power and ground rail available for
  ! pad placement.
  !
  %record %format boxf ( %integer x,   { length in x direction }
                                  y,   { height in y direction }
                                  d,   { direction of ground rail }
                                  g,   { position of ground tab }
                                  son, { each slot can give rise to another }
                                  ax,  { absolute x coordinate }
                                  ay   { absolute y coordinate } ) 
  !
  %record(boxf)%array slots (1:10)
  !
  %integer power                 { slot found for power  pad }
  %integer ground                { slot found for ground pad }

  %integer oddx
  %integer oddy

  %constant %integer gnd height = 96         
  %constant %integer pad width  = 96

  { port types }                       { user specified }

  %constant %integer vdd port      = 1
  %constant %integer gnd port      = 2
  %constant %integer in port       = 3
  %constant %integer out port      = 4
  %constant %integer cloutout port = 5
  %constant %integer cloutcl port  = 6
  %constant %integer inoutin port  = 7
  %constant %integer inoutout port = 8
  %constant %integer inouten port  = 9

  { now for the pad types }            { program generated }

  %constant %integer padint      = 3         
  %constant %integer padoutt     = 4
  %constant %integer padcloutt   = 5
  %constant %integer padcloutmyt = 6         { padinout mirrored in Y }
  %constant %integer padinoutt   = 7
  %constant %integer padinoutmyt = 8
  %constant %integer lvddt       = 9         { long vdd link }
  %constant %integer lgndt       = 10
  %constant %integer svddt       = 11        { short vdd link }
  %constant %integer sgndt       = 12
  %constant %integer cloutabst   = 13        { mark pads with no geometry }
  %constant %integer cloutmyabst = 14
  %constant %integer inoutabst   = 15
  %constant %integer inoutmyabst = 16

  %constant %string(16) %array  pads  (3:16) = "padin",
                                               "padout",
                                               "padclout" (2), 
                                               "padinout" (2),
                                               "place lvdd",
                                               "place lgnd",
                                               "place svdd",
                                               "place sgnd",
                                               "pseudo clout" (2),
                                               "pseudo inout" (2)
                                       
  %integer i                                 { counter variable }

  %integer vdd                               { number of vdd requests }
  %integer gnd                               { number of gnd requests }

  ! Pad dimensions
  !
  %integer padinx                            
  %integer padiny
  %integer padoutx
  %integer padouty
  %integer padcloutx
  %integer padcloutclx
  %integer padclouty
  %integer padinoutinx
  %integer padinoutoutx
  %integer padinoutenx
  %integer padinouty

  %const %integer contact height = 5
  %const %integer clearance = 7       { height of PM + 3 }

  %integer ground pad placed           
  %integer power pad placed          

  %own %byte %array diffusion on side (1:4) = false (*) { diff connection to chip ?

  !-----------------!
  ! Chip dimensions !
  !-----------------!

  %integer x1,x2,x3,x4             { size of block  n in x direction }
  %integer y1,y2,y3,y4             { size ""   ""  "" "" y   ""      }
  %integer xc1,xc2,xc3,xc4
  %integer yc1,yc2,yc3,yc4
  %integer oxc1,oxc2,oxc3,oxc4,oyc1,oyc2,oyc3,oyc4 { original dimensions }
  %integer %array d (1:4 )
  !
  !                                 side3              power rail
  !                                                     |
  !            +--xc4-------+--------x3-------+----xc3--|+ 
  !            | c4         y3                |       c3 |
  !    s       +    +-------+-----------------+-------+  |
  !    i      yc4   |                 |               | yc3       s
  !    d       |    |                 d3              |  |        i
  !    e       +----+       +-----------------+       +--+        d
  !    4       |    |-d4----|  GEORGE'S       |--d2---|  |        e
  !            y4   |       |   CELL !        |       |  y2       2
  !            +-x4-+       +-----------------+       +--+ 
  !            |    |           |                     |  |
  !            | c1 |           d1                    | yc2 
  !           yc1   +-------+---------------.-+-------+c2|
  !            +------xc1---+--------x1-----.-+----xc2---+
  !                                         .
  !                        side1            .
  !                                         ground rail
  !

  !------------------!
  ! Get pad from pool
  !
  %integer %fn new
    %record(padf)%name tp
    tp == fp
    fp == fp_next
    tp_next == nil
    %result = addr(tp)
  %end

  !--------------------------------------------!
  ! useful function
  !
  %integer %function max ( %integer a , b )
    %result = a %if a >= b
    %result = b
  %end

  %integer %function min ( %integer a , b )
    %result = a %if a <= b
    %result = b
  %end

  !-------------------------------------!
  ! error handler, terminates execution
  !
  %routine error (%integer n , %string(255) m )
    %string(255) f
    %switch e (1:13)
    -> e(n)
    e(1):f = "ports seperated by less than 3 lambda".m                                ;->ef
    e(2):f = "more than one VDD pad".m                                   ;->ef
    e(3):f = "PLACEPADS can't cope with this cell".m                     ;->ef
    e(4):f = "layer must be METAL for ground or power pad".m             ;->ef
    e(5):f = "METAL port width must be >= 8".m                           ;->ef
    e(6):f = "DIFFUSION port width must be >= 2".m                       ;->ef
    e(7):f = "missing PADCLOUT port".m                                   ;->ef
    e(8):f = "missing PADINOUT port".m                                   ;->ef
    e(9):f = "extra PADCLOUT out port".m    ;->ef
    e(10):f = "extra PADCLOUT clock port".m ;->ef
    e(11):f = "extra PADINOUT in port".m    ;->ef
    e(12):f = "extra PADINOUT out port".m   ;->ef
    e(13):f = "extra PADINOUT enable port".m
    ef:
    warning ("PLACE PADS - ".f)
  %end

  !--------------------------------!
  ! Check the user specified ports
  !
  %routine check ports ( %record(connf)%array %name ports, %integer n )
    %record (connf) %name po
    %integer i
    %switch s(1:4)

    %routine cerror ( %integer what, who )
       error(what,": on ".sidename(ports(who)_side)." side, c = ".itos(ports(i)_c,0) )
    %end

!!    %routine check unassigned ( %integer m )
!!       %string(10) who,l
!!       %record(connf) p
!!       %integer i
!!       %on 6,8 %start                        { works with checks on only }
!!         %if event_event = 8 %start
!!           disaster ("unassigned variable:
!!         ".who." field in port ".itos(m,0)." of the 'connf' array.")
!!         %else
!!           disaster ("incorrect array bounds 'N' given to 'PORTS'")
!!         %finish
!!       %finish
!!       who = ""
!!       p = ports(m)
!!       who = "type"
!!       i = p_type
!!       who = "side"
!!       i = p_side
!!       who = "c"
!!       i = p_c
!!       who = "w"
!!       i = p_w
!!       who = "layer"
!!       l = p_layer
!!    %end

    %for i = 1,1,n %cycle
!!      check unassigned(i)
      po == ports (i)
      disaster ("type field out of range on port ".itos(i,0)) %unless ( vdd port <= po_type <=  inouten port )
      disaster ("side field out of range on port ".itos(i,0)) %unless ( 1 <= po_side <= 4 )
      diffusion on side (po_side) = true %if po_layer->("D")
      vdd = vdd + 1 %if po_type = vdd port
      gnd = gnd + 1 %if po_type = gnd port
      cerror(2,i) %if vdd > 1
      cerror(4,i) %if ( po_type < in port %or po_type = inoutin port ) %and po_layer # METAL
      cerror(5,i) %if ( po_type < in port %or po_type = inoutin port ) %and po_w < 8
      cerror(6,i) %if  po_type > gnd port %and po_type # inoutin port %and po_w < 2
    %repeat
    %for i = 1,1,n %cycle
      po == ports (i)
      -> s(po_side)
      s(1):s(3):
          po_c = po_c - cell_dx;  %continue
      s(2):s(4):
          po_c = po_c - cell_dy
    %repeat
  %end

  ! sort ports in  order of displacement 'c' from start of side
  !
  %routine sort ports ( %record(connf)%array %name a, %integer n )
    %integer %function less than ( %record(connf) a,b )
      %result = true %if a_side < b_side %or ( a_side = b_side %and a_c < b_c )
      %result = false
    %end
    %routine sort ( %integer l, r)
      %integer i,j
      %record(connf) x,w
      i = l
      j = r
      x = a( (l+r)//2 )
      %cycle                              
        i = i + 1 %while less than ( a(i), x ) = true   { quicksort }
        j = j - 1 %while less than ( x, a(j) ) = true
        %if i <= j %start
          w = a(i) ; a(i) = a(j) ; a(j) = w
          i = i + 1 ; j = j - 1
        %finish
      %repeat %until i > j
      sort ( l,j ) %if l < j
      sort ( i,r ) %if i < r
    %end
    sort(1,n)
  %end

  !---------------------------------------------------------!
  ! routine which assembles the pad 'side' array element 'd'
  !
  %routine make side ( %integer d )      { d = north , east , south , west }
    %integer i,j                         { counters }
    %string(1) s                         { character of side }
    %integer dx
    ! Define some flags

    %integer output pad                  { flags are local to 'this side' }
    %integer input  pad
    %integer gnd pad  
    %integer vdd pad   
    %integer inout pad 
    %integer clout pad 

    !------------------------------------------------------!
    ! another port spotted for this side
    !
    %routine add to list ( %record(connf) port )
      %switch s ( vdd port:inouten port )
      j=j+1
      -> s ( port_type )
      s(vdd port):vdd pad = true ; %return
      s(gnd port):gnd pad = true ;  %return
      s(in port):input pad = true ; %return
      s(out port):output pad = true ; %return
      s(cloutout port):s(cloutcl port): clout pad = true; %return
      s(inoutin port) :s(inoutout port):s(inouten port): inout pad = true
    %end

    !----------------------------------------!
    ! build up pad list from this sides ports
    !
    %routine assemble pads ( %integer start, finish )
       %record(padf)%name tp
       %integer i

       %constant %integer inoutin  = 1         { flags for multiple port pads }
       %constant %integer inoutout = 2
       %constant %integer inouten  = 4
       %constant %integer cloutout = 1
       %constant %integer cloutcl  = 2

       %record(padf)%name inout padp           { remember instantiation of  }
                                               { inout pad                  }
       %integer inout
       %integer clock
       %integer clock first
       %integer inout first

       !-------------------------------------------!
       ! add appropriate pad ( if any ) to pad list
       !
       %routine make pad ( %record(padf)%name pad , %record(connf)%name port,
                           %integer %name width )

         %switch p ( vdd port:inouten port )          { switch on port type }

         error(7," on side".s ) %if clock # 0 %and %not ( cloutout port <= port_type <= cloutcl port )
         error(8," on side".s ) %if inout # 0 %and %not ( inoutin port <= port_type <= inouten port )

         -> p ( port_type )

         p(vdd port):
               %if ( output pad ! input pad ! %c
                     clout pad ! inout pad = false ) %and n # vdd + gnd %start
                                         { n = total number of ports ( global )}
                  pad_type = svddt       { short power link }
               %else
                  pad_type = lvddt       { long power link }
               %finish
               pad_width = 14
               width = width + 14
               %return

         p(gnd port):
               %if ( output pad ! input pad ! %c
                     clout pad ! inout pad = false ) %and n # vdd + gnd %start
                 pad_type = sgndt        { short ground link }
               %else
                 pad_type = lgndt
               %finish
               pad_width = 8
               width = width + 8
               %return

         p(in port):
               pad_type = padint ; pad_width = pad width
               width = width + pad width
               %return

         p(out port):
               pad_type = padoutt ; pad_width = pad width
               width = width + pad width
               %return

         p(cloutout port):
               error(9," on side".s) %if clock&cloutout # 0
               clock = clock!cloutout
               clock first = cloutout %if clock first = 0
               -> check clout

         p(cloutcl port):
               error(10, " on side".s) %if clock&cloutcl # 0
               clock first = cloutcl %if clock first = 0
               clock = clock!cloutcl

         check clout:
 
               %if clock&cloutcl # 0 %and clock&cloutout # 0 %start
                 %if clock first = cloutout %start
                    pad_type = padcloutt
                 %else
                    pad_type = padcloutmyt
                 %finish
                 pad_width = pad width
                 width = width + pad width
                 clock first = 0 ; clock = 0
               %else
                 %if clock first = cloutout %then pad_type = cloutabst %c
                                            %else pad_type = cloutmyabst
               %finish
               %return

         p(inoutin port):
               error(11," on side".s) %if inout&inoutin # 0
               inout = inout!inoutin
               -> check inout

         p(inoutout port):
               error(12," on side".s) %if inout&inoutout # 0
               inout = inout!inoutout
               inout first = inoutout %if inout first = 0
               -> check inout

         p(inouten port):
               error(13," on side".s) %if inout&inouten # 0
               inout = inout!inouten
               inout first = inouten %if inout first = 0

         check inout:

               %if port_type = inoutin port %start
                  inout padp == pad
               %else
                 %if inout first = inoutout %then pad_type = inoutabst %c
                                            %else pad_type = inoutmyabst
               %finish

               %if inout&inoutin # 0 %and inout&inoutout # 0 %and inout&inouten # 0 %start
                  %if inout first = inoutout %start
                    inout padp_type = padinoutt
                  %else
                    inout padp_type = padinoutmyt
                  %finish
                  inout padp_width = pad width
                  width = width + pad width
                  inout = 0 ; inout first = 0
               %finish
       %end

       inout = 0
       clock = 0
       clock first = 0
       inout first = 0
       side(d)_width = 0
       side(d)_first == nil

       %return %if start = 0

       side(d)_first == record(new)

       tp == side(d)_first
       make pad ( tp, ports(start), side(d)_width )

       %for i = start+1,1,finish %cycle
         tp_next == record(new)
         tp == tp_next
         make pad ( tp, ports(i), side(d)_width )
       %repeat

       error(7," on side".s) %if clock # 0           { missing clockout port }
       error(8," on side".s) %if inout # 0           { missing inout port }

    %end

    !--------------------------------------------!
    ! work out routing requirements for this side
    !
    %routine prepare routing ( %integer start, finish )
      %record(port)%array ap ( 1:n )
      %record(port)%array bp ( 1:n )
      %integer a, h, m, i, x
      %integer pad centre
      %integer cell centre
      %integer ma, size, msize
      %integer carry on
      %integer inout count
      %integer which sep
      %integer wide route

      %record(port)%array map (1:n)
      %record(port)%array mbp (1:n)

      %record(padf)%name tp

      a = 0;   h = 0;   m = -1
      pad centre = side(d)_width//2
      ma = 0;   size = 0;   msize = 0
      in out count = 0
      wide route = vdd pad ! gnd pad
      cell centre = cell_w(d)//2
      tp == side (d)_first

      !----------------------------------!
      ! Now find out the required offset !
      !----------------------------------!

      %if pad centre > cell centre %start
        side(d)_x = 0
        cell_offset(d) =  pad centre - cell centre
      %else
        side(d)_x = cell centre - pad centre
        cell_offset(d) = 0
      %finish

      !----------------------!
      ! choose routing layer !
      !----------------------!

      %if input pad ! clout pad ! inout pad = true %or diffusion on side (d) = true %start
        side(d)_layer = DIFFUSION
        which sep = 3
      %else           { i.e. only the output pad present }
        side(d)_layer = POLY
        which sep = 2
      %finish

      %if wide route = true %start
        side(d)_metal width = 8
      %else
        side(d)_metal width = 4
      %finish

      x = side(d)_x

      side(d)_mrouted = false ; side(d)_drouted = false

      %if start > 0 %start

        %for i = start,1,finish %cycle
 
           carry on = true

           ! First keep track of height of side

           %if tp_type < cloutabst %start   { do we have 'real' pad ? }
             h = SY ( pads( tp_type) )
             h = h + contact height %if padcloutt <= tp_type <= padinoutmyt
             h = h + contact height - 2 %if side(d)_layer = DIFFUSION %and tp_type = padoutt
             h = h + 1 %if side(d)_layer = POLY %and tp_type = padoutt 
             m = h %if h > m
           %else
             carry on = false
             tp_width = pad width
           %finish

           ! Now build up routing arrays for I/O ports

           %if ( in port <= ports(i)_type <= cloutcl port ) %or ( inoutout port <=ports(i)_type <= inouten port ) %start
             !
             ! first generate B array ( the user's ports )
             !
             a = a + 1
             bp(a)_x = ports(i)_c + cell_offset(d) + clearance
             bp(a)_w = 2
             !
             ! Now generate the A array ( pads )
             ! 
             %if tp_type  = padint %start
               ap(a)_x = x + padinx
             %finish %else %if tp_type = padoutt %start
               ap(a)_x = x + padoutx
             %finish %else %if tp_type = padcloutt %or tp_type = padcloutmyt %or %c
                       tp_type = cloutabst %or tp_type = cloutmyabst %start
               %if ports(i)_type = cloutout port %then ap(a)_x = x + padcloutx %c
                                                %else ap(a)_x = x + padcloutclx
               ap(a)_x = - ap(a)_x + pad width - 2 + 2*x %c
                  %if tp_type = padcloutmyt %or tp_type = cloutmyabst
               !
               ! just a bit of algebra to adjust for mirroring ^
               !
             %finish %else %if tp_type = inoutabst %or tp_type = inoutmyabst %start
               %if ports(i)_type = inoutout port %then ap(a)_x = x + padinoutoutx %c
                                                %else ap(a)_x = x + padinoutenx
               ap(a)_x = - ap(a)_x + pad width + 2*x - 2 %c
                  %if tp_type = inoutmyabst
               inout count = inout count + 1
             %finish
             ap(a)_w = 2
             side(d)_drouted = true
           %finish %else %if ports(i)_type <= gnd port %or ports(i)_type = inoutin port %start
             ma = ma + 1
             %if tp_type = svddt %or tp_type = lvddt %start
               map(ma)_x = x + 3
             %finish %else %if tp_type = sgndt %or tp_type = lgndt %start
               map(ma)_x = x
             %finish %else %if tp_type = padinoutt %start
               map(ma)_x = x + padinoutinx
               inout  count = inout count + 1
               carry on = false
             %finish %else %if tp_type = padinoutmyt %start
               map(ma)_x = x + pad width - padinoutinx - side(d)_metal width
               carry on = false
               inout count = inout count + 1
             %finish
             mbp(ma)_x = ports(i)_c + cell_offset(d) + clearance
             map(ma)_w = side(d)_metal width
             mbp(ma)_w = side(d)_metal width
             side(d)_mrouted = true
           %finish

           x = x + tp_width %if carry on = true
           x = x + tp_width %and inout count = 0 %if inout count = 3
           tp_width = 0 %if tp_type >= cloutabst
           tp == tp_next
            
        %repeat                   

      %finish

      msize = route ( chipname."_vdd route".s,
                       map,mbp,ma,METAL,side(d)_metal width,3,0 ) %c
              %if side(d)_mrouted = true 

      size  = route ( chipname."_route".s,
                       ap,bp,a,side(d)_layer,2,which sep,0 ) %c
              %if side(d)_drouted = true

      side(d)_ed = 0 ; side(d)_em = 0

      side(d)_mrouted = false %if msize <= 0
      side(d)_drouted = false %if size <= 0
      msize = 0 %if msize < 0
      size = 0  %if size < 0

      %if size < msize %start
        side(d)_ed = msize - size
      %finish %else %if msize < size %start
        side(d)_em = size - msize
      %finish

      side(d)_separation = max ( size, msize )      
      side(d)_height = m

    %end

    !--------------------------------------------------!
    ! Routine which calls the appropriate ILAP SYMBOLS
    !
    %routine draw side ( %integer start, finish )
      %integer i, x, e
      %integer extend outputs
      %integer extend clouts 
      %integer extend inputs 
      %integer extend metal
      %integer highest
      %record(padf)%name tp

      %routine draw pad ( %integer type, dx )
        %switch p (padint:sgndt)
        %return %if type >= cloutabst

        %routine do contact ( %integer x, y )

          %if side(d)_layer = POLY %start
            LAYER(POLY)
            BOX( dx + x -1, y, dx + x + 1, y+2 ) %if type = padoutt
          %else
            PDCN ( dx + x, y  )
          %finish
        %end

        -> p(type)
        p(padint):
              DRAW("padin",dx,0)
              %if extend inputs = true %start
                LAYER ( DIFFUSION )
                BOX ( dx + padinx , padiny,
                      dx + padinx + 2 , highest )
              %finish
              %return
        p(padoutt):
              DRAW("padout",dx,0)
              %if extend outputs = true %start
                LAYER ( DIFFUSION )
                BOX ( dx + padoutx , padouty + 2,
                      dx + padoutx+2, highest )
              %finish
              do contact( padoutx + 1, padouty)
              %return
        p(padcloutt):
              DRAW("padclout",dx,0)
              %if extend clouts = true %start
                LAYER ( DIFFUSION )
                BOX( dx + padcloutclx, padclouty+contact height,
                     dx + padcloutclx+2 , highest )
              %finish
              LAYER ( DIFFUSION )
              BOX ( dx + padcloutx, padclouty,
                    dx +padcloutx+2, highest )
              do contact( padcloutclx+1, padclouty+2 )
              %return
        p(padcloutmyt):
              DRAWMX("padclout",dx+pad width,0)
              %if extend clouts = true %start
                LAYER ( DIFFUSION )
                BOX ( dx + pad width - padcloutclx - 2, padclouty+contact height,
                      dx + pad width - padcloutclx, highest )
              %finish
              LAYER ( DIFFUSION )
              BOX ( dx + pad width - padcloutx -2 , padclouty, 
                    dx + pad width - padcloutx, highest )
              do contact( pad width - padcloutclx-1, padclouty+2 )
              %return
        p(padinoutt):
              DRAW("padinout",dx,0)            
                LAYER( METAL )
                BOX ( dx + padinoutinx, padinouty,
                      dx + padinoutinx + 4, highest )
              do contact( padinoutoutx+1, padinouty+2 )
              do contact( padinoutenx+1, padinouty+2 )
              %return
        p(padinoutmyt):
              DRAWMX("padinout",dx+pad width,0)
                LAYER( METAL )
                BOX ( dx + pad width - padinoutinx - 4, padinouty,
                      dx + pad width - padinoutinx, highest )
              do contact( pad width - padinoutoutx - 1, padinouty+2 )
              do contact( pad width - padinoutenx - 1, padinouty+2 )
              %return
        p(lvddt):
              DRAW("place lvdd",dx,0)     
              %return
        p(lgndt):
              DRAW("place lgnd",dx,0)     
              %return
        p(svddt):
              DRAW("place svdd",dx,0)     
              %return
        p(sgndt):
              DRAW("place sgnd",dx,0)
      %end

      x = 0
      dx = side(d)_x
      extend outputs = output pad  & ( clout pad ! inout pad )
      extend clouts  = clout pad & inout pad
      extend inputs  = input pad & ( gnd pad ! vdd pad ! %c
                                     output pad ! inout pad  ! clout pad )
      extend metal = ( vdd pad ! gnd pad ) & %c
                     ( output pad ! inout pad ! clout pad )
      highest = side(d)_height
      tp == side(d)_first          

      %for i = start,1,finish %cycle

        draw pad( tp_type,dx + x )

        %if extend metal = true %and ( lvddt <= tp_type <= sgndt ) %start
          LAYER ( METAL )
          %if tp_type = svddt %or tp_type = lvddt %then e = 3 %else e = 0
          BOX ( dx + e + x, gnd height, dx + e + 8 + x, highest )
        %finish

        x = x + tp_width
        tp == tp_next

      %repeat
    %end

    ! Initialise MAKE SIDE
    s = tostring('0'+d)       { character of side }
    output pad = false
    input  pad = false
    gnd pad    = false
    vdd pad    = false
    inout pad  = false
    clout pad  = false

    !------------------------------!
    ! pull out ports for this side !
    !------------------------------!

    j = 0
    cell_ports(d)_start = 0
    %for i = 1,1,n %cycle
      add to list ( ports(i) ) %if ports(i)_side = d
      cell_ports(d)_start = i %if j = 1 %and cell_ports(d)_start = 0
    %repeat
    cell_ports(d)_finish = cell_ports(d)_start+j-1

! { leave ports being to close to router. Watch ! Ports can conflict
! round corners ! }

!    %if j > 1 %start
!
!      %for i = 2,1,j %cycle
!        error(1," between ports ".itos(i-1,0)." and ".itos(i,0)." of side".s ) %c
!          %if cell_ports(d)_p(i)_c - cell_ports(d)_p(i-1)_c - %c
!              cell_ports(d)_p(i-1)_w < 3
!      %repeat
!    %finish

    assemble pads ( cell_ports(d)_start, cell_ports(d)_finish )

    prepare routing ( cell_ports(d)_start, cell_ports(d)_finish )

    %if j > 0 %start   { we have some ports }

      %if input pad ! output pad ! clout pad ! inout pad = false %and %c
          n # vdd + gnd %then side(d)_ground rail = 8+8+8 %c
                        %else side(d)_ground rail = gnd height

      SYMBOL (chipname."_side".s)

        draw side ( cell_ports(d)_start, cell_ports(d)_finish )

        DRAW ( chipname."_route".s,0,side(d)_height ) %if side(d)_drouted = true
        DRAW ( chipname."_vdd route".s,0,side(d)_height ) %if side(d)_mrouted = true
        
        %if side(d)_x > 0 %start   { add in power rail }
          
          LAYER( METAL )
          BOX  ( 0,0,side(d)_x, 8 )
          BOX  ( side(d)_x+side(d)_width, 0,
                 side(d)_x+side(d)_x+side(d)_width, 8 )
        %finish
        
      ENDSYMBOL

       side(d)_width = side(d)_width + 2 * side(d)_x
       side(d)_height      = side(d)_height + side(d)_separation

    %else

       SYMBOL (chipname."_side".s)

         side(d)_ground rail = 8+8+8
         side(d)_height = 8+8+8

         LAYER( METAL )
         BOX ( 0,0,cell_w(d),8 )
         BOX ( 0,8+8,cell_w(d),8+8+8 )
         side(d)_width = cell_w(d)

       ENDSYMBOL
    %finish
  %end { of make side }

  !===============================================================!
  !  Now that we have dimensions of all four blocks we can do the !
  ! final modifications, prior to assembly.                       !
  !===============================================================!

  %routine extend routing ( %integer start, finish , w )
    %integer i, dn, x, offset
    %string (1) s

    %return %if start = 0

    %if ( w=2 %and oddx#0 ) %or ( w=3 %and oddy#0 ) %start
      offset=1
    %else
      offset=0
    %finish

    dn = d(w)+offset
    s = to string (w + '0')

    SYMBOL (chipname."_X route".s)

      %for i = start,1,finish %cycle

        x = ports(i)_c + cell_offset(w)
 
        %if ports(i)_type >= in port %and ports(i)_type # inoutin port %start
          LAYER ( side(w)_layer )
          BOX ( x + clearance, -side(w)_ed, x + 2 + clearance, dn ) %c
            %if side(w)_ed > 0 %or dn > 0
        %finish %else  %start
          LAYER ( METAL )
          BOX( x+clearance, -side(w)_em, x+side(w)_metal width+clearance, dn ) %c
            %if side(w)_em > 0 %or dn > 0
        %finish
        !------------------------------!
        ! Now add contact if necessary !
        !------------------------------!
        %if ports(i)_layer = POLY %start
          %if side(w)_layer = diffusion %start
            PDCS ( x+1+clearance, dn + clearance - 2 )
            LAYER(DIFFUSION)
            BOX(x+clearance,dn,x+2+clearance, dn+clearance-5)
          %else
            LAYER(POLY)
            BOX( x + clearance, dn, x + 2 + clearance, dn + clearance )
          %finish
        %finish %else %if ports(i)_layer = DIFFUSION %start
          %if side(w)_layer = diffusion %start
            LAYER(DIFFUSION)
            BOX( x + clearance, dn, x + 2 + clearance, dn + clearance )
          %else
            PDCN( x+1+clearance, dn + clearance - 3 )
            LAYER(POLY)     
            BOX(x+clearance,dn,x+2+clearance, dn+clearance-5)
          %finish
        %finish %else %if ports(i)_layer = METAL %start
          %if ports(i)_type <= gnd port %or ports(i)_type = inout in port %start
            LAYER(METAL)
            BOX( x + clearance, dn, x + side(w)_metal width + clearance,
                 dn + clearance )
          %else
            %if side(w)_layer = diffusion %start
              DM ( x+2+clearance, dn + clearance - 2 )
            %else
              PM ( x+2+clearance, dn + clearance - 2 )
            %finish
            LAYER( side(w)_layer )
            BOX( x + clearance, dn, x + 2 + clearance, dn + clearance - 4 )
          %finish
        %finish
      %repeat
    ENDSYMBOL
  %end
!
! Now for the 'slot' routines
!                             
  !---------------------!
! Initialise slot 'a'
  !
  %routine make slot ( %integer a, b, c, d, e, f, g )
    slots(a)_x = b
    slots(a)_y = c
    slots(a)_d = d
    slots(a)_g = e
    slots(a)_ax = f             
    slots(a)_ay = g
    slots(a)_son = 0
  %end

  ! returns number of largest slot
  !
  %integer %function largest slot
    %record(boxf) s
    %integer i
    %integer which
    %integer size
    which = 0
    size = 0
    %for i = 1,1,8 %cycle
      s = slots(i)
      %if ( s_d = 1 %or s_d = 3 ) %and s_y = gnd height %and s_x >= size %start
        size = s_x
        which = i
      %finish %else %if ( s_d = 2 %or s_d = 4 ) %and s_x = gnd height %and s_y >= size %start
        size = s_y
        which = i
      %finish
    %repeat
    warning ("couldn't find slot") %if which = 0
    %result = which
  %end
         
  !---------------------------------------------------!
  ! Draw pad in slot, n = 9 => power pad, 10 => ground
  !
  %routine place pad ( %integer n )
    %record(boxf)%name s
    %switch p (1:4), g (1:4)

    %routine fit ground tab (%integer p, g, x, y)
!
! This routine attaches the special ground symbol to the ground rail 
!
      %return %if g = 0
      LAYER ( METAL )
      %if p = 3 %and g = 2 %start
        BOX( x+pad width-11, y + gnd height - 8, x + pad width - 8,
                                                y + gnd height )
      %finish %else %if p = 3 %and g = 4 %start
        BOX( x+8, y+gnd height-8, x+11, y+gnd height )
      %finish %else %if p = 4 %and g = 1 %start
        BOX( x, y+8, x+8, y+11)
      %finish %else %if p = 1 %and g = 2 %start
        BOX( x+pad width-11, y, x+pad width-8, y+8)
      %finish
    %end
!
! s_d indicates direction of ground rail
!
    s == slots (n)
    %if n = 9 %start                       { n = 9 => power pad }
      -> p (s_d)
    %finish
    fit ground tab ( s_d, s_g, s_ax, s_ay )
    -> g (s_d)                               { n = 10 => ground }

    g(3): p(1):DRAW("place padgnd", s_ax, s_ay);                  -> ep
    g(4): p(2):DRAWROT("place padgnd", s_ax+gnd height, s_ay, 1); -> ep
    g(1): p(3):DRAWMY("place padgnd", s_ax, s_ay+gnd height);     -> ep
    g(2): p(4):DRAWMYROT("place padgnd", s_ax, s_ay, 1)
    ep: 
  %end

  !----------------------------------------------!
  ! we have a slot big enuff, copy into slot 9, or 10 
  !
  %integer %function position pad ( %integer n )
    %if power pad placed = false %start
      slots(9) = slots(n)
      power = n
      power pad placed = true
      %result = 9
    %finish %else %if ground pad placed = false %start
      slots(10) = slots(n)
      ground = n
      ground pad placed = true
      %result = 10
    %finish
    %result = 0
  %end

  !---------------------------------------------------!
  ! try and fit in pad in slot 'n', if still pads left
  !
  %routine fit pads in slot ( %record(boxf)%name s , %integer n )

    %return %if power pad placed & ground pad placed = true

    %if ( s_d  = 1 %or s_d = 3 ) %and s_y = gnd height %and s_x >= pad width %start
      s_son = position pad ( n )
      s_x = s_x  - pad width                    { update slot's coords )
      s_ax = s_ax + pad width
      %return %if s_x < pad width
      slots(s_son)_son = position pad ( n )
      ! no need to update, we have placed both pads
      !
    %finish %else %if ( s_d  = 2 %or s_d = 4 ) %and s_x  = gnd height %and s_y >= pad width %start
      s_son = position pad ( n )
      s_y = s_y - pad width
      s_ay = s_ay + pad width
      %return %if s_y < pad width
      slots(s_son)_son = position pad ( n )
    %finish 
  %end

  !------------------------------------------------------------!
  ! We have been forced to widen slot 'n' in order to fit a pad
  !
  %routine update slots ( %integer n )
    %constant %integer %array sopposite (1:8) = 6,5,8,7,2,1,4,3
    %constant %integer right = 1, up = 2
    %integer w
    %integer i
    %switch e (1:8)
    %record(boxf)%name s, o

    %routine move slots ( %integer who, where, how much )
      %if where = up %start
        %while who # 0 %cycle
          slots(who)_ay = slots(who)_ay + how much
          who = slots(who)_son
        %repeat
      %else
        %while who # 0 %cycle
          slots(who)_ax = slots(who)_ax + how much
          who = slots(who)_son
        %repeat
      %finish
    %end

    s == slots (n)
    o == slots (sopposite(n))
    %if s_d = 1 %or s_d = 3 %start
      w   = pad width - s_x
      s_x = pad width
      o_x = o_x + w
    %else
      w   = pad width - s_y
      s_y = pad width
      o_y = o_y + w
    %finish
    -> e(n)
    e(1):e(6): XC1 = XC1 + w ; d(4) = d(4) + w ; XC4 = XC4 + w
               move slots(i,right,w) %for i = 2,1,5
               %return
    e(2):e(5): XC2 = XC2 + w ; d(2) = d(2) + w ; XC3 = XC3 + w
               move slots(i,right,w) %for i = 3,1,4
               %return
    e(3):e(8): YC1 = YC1 + w ; d(1) = d(1) + w ; YC2 = YC2 + w
               move slots(i,up,w) %for i = 4,1,7
               %return
    e(7):e(4): YC4 = YC4 + w ; d(3) = d(3) + w ; YC3 = YC3 + w
               move slots(i,up,w) %for i = 5,1,6
  %end

  %routine position slots
  
  ! make slot parameters :-
  ! 
  !   make slot ( number , x dimension , y dimension ,
  !              direction of ground rail, direction of ground tab,
  !              x coordinate, y coordinate )

    %if side(1)_pushed = false %start
      make slot ( 1, XC1 + side(1)_x,
                  side(1)_ground rail, 3, 2, 0, 0 )
    %else
      make slot ( 1, side(1)_x,gnd height,3,0,
                  OXC1,0 )
    %finish
    %if side(1)_pushed = false %start
      make slot ( 2, XC2 + side(1)_x,
                  side(1)_ground rail, 3, 4,
                  XC1+X1-side(1)_x, 0 )
    %else
      make slot ( 2, side(1)_x, gnd height, 3, 0,
                  XC1+X1-side(1)_x,0 )
    %finish
    %if side(2)_pushed = false %start
      make slot ( 3, side(2)_ground rail,
                  side(2)_x + YC2 - side(1)_ground rail, 4, 0,
                  XC1+X1+XC2-side(2)_ground rail, side(1)_ground rail )
    %else
      make slot ( 3, gnd height, side(2)_x, 4, 0,
                  XC1+X1+XC2-gnd height, OYC2 )
    %finish
    %if side(2)_pushed = false %start
      make slot ( 4, side(2)_ground rail,
                  YC3 + side(2)_x, 4, 1,
                  XC4+X3+XC3-side(2)_ground rail,
                  YC2+Y2-side(2)_x )
    %else
      make slot ( 4, gnd height, side(2)_x, 4, 0,
                  XC4+X3+XC3-gnd height, YC2+Y2-side(2)_x )
    %finish
    %if side(3)_pushed = false %start
      make slot ( 5, XC3 + side(3)_x - side(2)_ground rail,
                  side(3)_ground rail, 1, 0,
                  XC4+X3-side(3)_x,
                  YC2+Y2+YC3-side(3)_ground rail )
    %else
      make slot( 5, side(3)_x, gnd height, 1, 0,
                 XC4+X3-side(3)_x,
                 YC2+Y2+YC3-gnd height )
    %finish
    %if side(3)_pushed = false %start
      make slot ( 6, XC4 + side(3)_x,
                  side(3)_ground rail, 1, 2,
                  0, YC1+Y4+YC4-side(3)_ground rail )
    %else
      make slot ( 6, side(3)_x, gnd height, 1, 0,
                  OXC4, YC1+Y4+YC4-gnd height )
    %finish
    %if side(4)_pushed = false %start
      make slot ( 7, side(4)_ground rail,
                  YC4 + side(4)_x - side(3)_ground rail,
                  2, 0, 0, YC1+Y4-side(4)_x ) 
    %else
      make slot ( 7, gnd height, side(4)_x, 2, 0,
                  0, YC1+Y4-side(4)_x )
    %finish
    %if side(4)_pushed = false %start
      make slot ( 8, side(4)_ground rail,
                  YC1 - side(1)_ground rail + side(4)_x, 2, 0,
                  0, side(1)_ground rail )
    %else
      make slot ( 8, gnd height, side(4)_x, 2, 0,
                  0, OYC1 )
    %finish
  %end

  !--------------------------------------------------------------------!
  ! this routine only used if pads have not been positioned without
  ! widening.
  !
  %routine force final pads
    %integer s
    s = largest slot
    %return %if s = 0
    update slots ( s )
    fit pads in slot ( slots(s) , s )
    force final pads %if power pad placed & ground pad placed = false
  %end

  !---------------------------------------------------------------!
  !  Routine which pushes the pad blocks into their final positions
  ! relative to each other. 
  !
  %routine fit
    %integer i,j
    %integer biggest
    %integer smallest
    %integer shift
    %integer pushed

    !-----------------------!
    ! Now define dimensions ! 
    !-----------------------!

    d(1) = max(cell_offset(4),cell_offset(2))
    d(4) = max(cell_offset(1),cell_offset(3))
    d(3) = d(1)
    d(2) = d(4)

    ! Find pair of opposite sides with greatest area between the pads and
    ! the cell.
    !
    %if side(1)_width*d(1)+side(3)_width*d(3) > %c
        side(2)_width*d(2)+side(4)_width*d(4) %then biggest = 1 %c
                                              %else biggest = 2
    ! Find smaller of the other pair
    !
    %if biggest = 1 %start
      %if cell_offset(4) > cell_offset(2) %then smallest = 2 %c
                                          %else smallest = 4
    %else
      %if cell_offset(3) > cell_offset(1) %then smallest = 1 %c
                                          %else smallest = 3
    %finish

    ! Now push in the two big sides
    !
    pushed = 0
    %for i = 1,1,4 %cycle
      side(i)_pushed = false
      side(i)_corner = side(i)_ground rail
      side(i)_px = 0
      %if side(i)_ground rail # 8+8+8 %and cell_ports(i)_start > 0 %and %c
         ( i = biggest %or i = opposite( biggest ))%start
        %if d(i) >= side(i)_height -24 %start
          d(i) = d(i) - side(i)_height + 24
          d(prev(i)) = d(prev(i)) + 14 - pushed
          d(next(i)) = d(next(i)) + 14 - pushed
          side(i)_pushed = true
          side(i)_corner = 8+8+8
          side(i)_px = 14
          pushed = 14                               { width of ground link }
        %else
          shift = side(i)_height-side(i)_ground rail
          %if d(i) < shift %then d(i) = 0 %c
                           %else d(i) = d(i)-shift
          %if pushed # 14 %start
            %if cell_offset(prev(i)) > d(i)-3 %and side(prev(i))_ground rail # 8+8+8 %start
              d(prev(i))=d(prev(i))+3    { push out previous side }
              pushed = 3
            %finish
            %if cell_offset(next(i)) > d(i)-3 %and side(next(i))_ground rail # 8+8+8 %start
              d(next(i))=d(next(i))+3    { push out next side }
              pushed = 3
            %finish
          %finish
        %finish
      %finish
    %repeat

    ! Now push in smallest side, if possible
    !
    %if cell_offset(smallest) < min( d(prev(smallest)),d(next(smallest))) %and cell_ports(smallest)_start > 0 %start
      shift = side(smallest)_height-side(smallest)_ground rail
      %if d(smallest) < shift %then d(smallest) = 0 %c
                       %else d(smallest) = d(smallest)-shift
    %finish

    X1  = side(1)_width  ; Y1 = side(1)_height
    X2  = side(2)_height ; Y2 = side(2)_width
    X3  = side(3)_width  ; Y3 = side(3)_height
    X4  = side(4)_height ; Y4 = side(4)_width

    XC1 = X4 + d(4) - cell_offset(1)
    XC2 = X2 + d(2) - cell_offset(1)
    XC3 = X2 + d(2) - cell_offset(3)
    XC4 = X4 + d(4) - cell_offset(3)

    YC1 = Y1 + d(1) - cell_offset(4)
    YC2 = Y1 + d(1) - cell_offset(2)
    YC3 = Y3 + d(3) - cell_offset(2)
    YC4 = Y3 + d(3) - cell_offset(4)

    OXC1 = XC1 ; OXC2 = XC2 ; OXC3 = XC3 ; OXC4 = XC4
    OYC1 = YC1 ; OYC2 = YC2 ; OYC3 = YC3 ; OYC4 = YC4

    position slots

    fit pads in slot ( slots(j), j ) %for j = 1,1,8

    force final pads %if power pad placed & ground pad placed = false

    extend routing ( cell_ports(j)_start, cell_ports(j)_finish, j ) %for j = 1,1,4
  %end

  %routine add railing
    %integer old
    %integer dx
    old = ILAP CONTROL
    ILAP CONTROL = ILAP CONTROL ! 1  { suppress warnings }
    LAYER ( METAL )
    { corner 1 }

    BOX ( 0, 0, XC1, 8 )    { first the power rail }
    BOX ( 0, 8, 8, YC1 )
    BOX( side(4)_corner-8, side(1)_corner-8, OXC1-side(1)_px, side(1)_corner )
    BOX( side(4)_corner-8, side(1)_corner, side(4)_corner, OYC1-side(4)_px )

    %if side(1)_pushed = true %start
      DRAW( "ground link", OXC1-14, 0 )
      DRAWMX( "ground link", XC1+X1+XC2-OXC2+14, 0 )
    %else
      BOX( OXC1-side(1)_px, side(1)_ground rail-8, OXC1, side(1)_ground rail )
      BOX( XC1+X1+XC2-OXC2, side(1)_ground rail-8,
           XC1+X1+XC2-OXC2+side(1)_px, side(1)_ground rail )
    %finish
    BOX( OXC1, side(1)_ground rail-8,           { complete the ground rail }
         XC1+side(1)_x, side(1)_ground rail )
    BOX( XC1+X1-side(1)_x, side(1)_ground rail-8, XC1+X1+XC2-OXC2, side(1)_ground rail )       

    { corner2 }
    BOX ( XC1+X1, 0, XC1+X1+XC2, 8 )
    BOX ( XC1+X1+XC2-8, 8, XC1+X1+XC2, YC2 )
    BOX ( XC1+X1+XC2-OXC2+side(1)_px, side(1)_corner-8,
          XC1+X1+XC2-side(2)_corner+8, side(1)_corner )
    BOX ( XC1+X1+XC2-side(2)_corner, side(1)_corner,
          XC1+X1+XC2-side(2)_corner+8, OYC2-side(2)_px )

    dx = XC1+X1+XC2
    %if side(2)_pushed = true %start
      DRAWROT( "ground link", dx, OYC2-14, 1 )
      DRAWMXROT( "ground link", dx, YC2+Y2+YC3-OYC3+14, 1 )
    %else
      BOX( dx-side(2)_ground rail, OYC2-side(2)_px,
           dx-side(2)_ground rail+8, OYC2 )
      BOX( dx-side(2)_ground rail, YC2+Y2+YC3-OYC3,
           dx-side(2)_ground rail+8, YC2+Y2+YC3-OYC3+side(2)_px )
    %finish
    BOX( dx-side(2)_ground rail, OYC2,
         dx-side(2)_ground rail+8, YC2+side(2)_x )
    BOX( dx-side(2)_ground rail, YC2+Y2-side(2)_x,
         dx-side(2)_ground rail+8, YC2+Y2+YC3-OYC3 )

    { corner 3 }
    BOX ( XC4+X3, YC2+Y2+YC3-8, XC4+X3+XC3, YC2+Y2+YC3 )
    BOX ( XC4+X3+XC3-8, YC2+Y2, XC4+X3+XC3, YC2+Y2+YC3-8 )
    BOX ( XC4+X3+XC3-side(2)_corner, YC2+Y2+YC3-OYC3+side(2)_px,
          XC4+X3+XC3-side(2)_corner+8, YC2+Y2+YC3-side(3)_corner )
    BOX ( XC4+X3+XC3-OXC3+side(3)_px, YC2+Y2+YC3-side(3)_corner,
          XC4+X3+XC3-side(2)_corner+8, YC2+Y2+YC3-side(3)_corner+8 )

    %if side(3)_pushed = true %start
      DRAWMY( "ground link", OXC4-14, YC1+Y4+YC4 )
      DRAWROT( "ground link", XC4+X3+XC3-OXC3+14, YC1+Y4+YC4, 2 )
    %else
      BOX( OXC4-side(3)_px, YC1+Y4+YC4-side(3)_ground rail,
           OXC4, YC1+Y4+YC4-side(3)_ground rail+8 )
      BOX( XC4+X3+XC3-OXC3, YC1+Y4+YC4-side(3)_ground rail,
           XC4+X3+XC3-OXC3+side(3)_px, YC1+Y4+YC4-side(3)_ground rail+8 )
    %finish
    BOX( OXC4, YC1+Y4+YC4-side(3)_ground rail,
         XC4+side(3)_x, YC1+Y4+YC4-side(3)_ground rail+8 )
    BOX( XC4+X3-side(3)_x, YC1+Y4+YC4-side(3)_ground rail,
         XC4+X3+XC3-OXC3, YC1+Y4+YC4-side(3)_ground rail+8 )

    { corner 4 }
    BOX ( 0, YC1+Y4, 8, YC1+Y4+YC4 )
    BOX ( 8, YC1+Y4+YC4-8, XC4, YC1+Y4+YC4 )
    BOX ( side(4)_corner-8, YC1+Y4+YC4-side(3)_corner,
          OXC4-side(3)_px, YC1+Y4+YC4-side(3)_corner+8 )
    BOX ( side(4)_corner-8, YC1+Y4+YC4-OYC4+side(4)_px,
          side(4)_corner, YC1+Y4+YC4-side(3)_corner )

    %if side(4)_pushed = true %start
      DRAWMYROT( "ground link", 0, OYC1-14, 1)
      DRAWROT( "ground link", 0, YC1+Y4+YC4-OYC4+14, -1 )
    %else
      BOX( side(4)_ground rail-8, OYC1-side(4)_px, side(4)_ground rail, OYC1 )
      BOX( side(4)_ground rail-8, YC1+Y4+YC4-OYC4, side(4)_ground rail, YC1+Y4+YC4-OYC4+side(4)_px )
    %finish
    BOX( side(4)_ground rail-8, OYC1, side(4)_ground rail, YC1+side(4)_x )
    BOX( side(4)_ground rail-8, YC1+Y4-side(4)_x, side(4)_ground rail, YC1+Y4+YC4-OYC4 )    

    ILAP CONTROL = old
  %end

  %routine initialise
    %integer i
    !
    ! set up pad pool
    !
    free pads(i)_next == free pads(i+1) %for i = 1,1,n-1
    free pads(n)_next == nil
    fp == free pads(1)

    vdd = 0;   gnd = 0        { Number of requests }
    ground pad placed = false
    power pad placed = false

    ! call pre-defined pads
    !
    PADIN ( padinx ) ; PADOUT ( padoutx )
    PADINOUT( padinoutinx, padinoutoutx, padinoutenx )
    PADCLOUT( padcloutx, padcloutclx )
    padouty = SY( "padout" )
    padinouty = SY( "padinout" )
    padclouty = SY( "padclout" )
    padiny = gnd height

    !------------------------!
    ! Define special symbols !
    !------------------------!

    %unless SYMBOL EXISTS ("Place padgnd") = TRUE %start

        SYMBOL("Place padgnd")
          LAYER( METAL )
          BOX(11,88,85,96) 
          BOX(24,24,72,72)
          BOX(64,72,72,88)
          BOX(24,72,32,88)
          LAYER( GLASS )
          BOX(28,28,68,68)
          update mbb(0, 0, 96, 96 )  { make square bounding box }
        ENDSYMBOL
  
        SYMBOL ( "Place lvdd" )
          LAYER ( METAL )
          BOX ( 0, 0, 14, 8 )
          BOX ( 3, 0, 11, gnd height + 3 )
        ENDSYMBOL
  
        SYMBOL ( "Place lgnd" )
          LAYER ( METAL )
          BOX ( 0, 0, 8, 8 )
          BOX ( 0, gnd height - 8 , 8 , gnd height + 3 )
        ENDSYMBOL
  
        SYMBOL ( "Place svdd" )
          LAYER ( METAL )
          BOX ( 0, 0, 14, 8 )
          BOX ( 3, 0, 8+3, 8+8+8+3 )
        ENDSYMBOL
  
        SYMBOL ( "Place sgnd" )
          LAYER ( METAL )
          BOX (0, 0, 8, 8)
          BOX (0, 8+8, 8, 8+8+8+3 )
        ENDSYMBOL
  
        SYMBOL ( "Ground link" )
          LAYER( METAL )
          BOX(0,0,14,8)
          BOX(0,16,11,24)
          BOX(3,24,11,gnd height)
          BOX(11,gnd height-8,14,gnd height)
        ENDSYMBOL
    
    %finish

    cell_w(1) = SX ( cell name ) + 2 * clearance
    cell_w(2) = SY ( cell name ) + 2 * clearance

    %if cell_w(1)&1=1 %start   { even off cells }
      cell_w(1)=cell_w(1)+1
      oddx=1
    %else
      oddx=0
    %finish

    %if cell_w(2)&1=1 %start
      cell_w(2)=cell_w(2)+1
      oddy=1
    %else
      oddy=0
    %finish

    cell_w(3) = cell_w(1)
    cell_w(4) = cell_w(2)
    cell_dx   = LX ( cell name )
    cell_dy   = LY ( cell name )
  %end

  !----------------!
  ! Main Program   !--------------------------------------------------------!
  !----------------!

  initialise
  check ports ( ports, n )
  sort ports (  ports, n )

  make side(i) %for i = 1,1,4
  fit

  SYMBOL ( chip name )

    !---------------------------------!
    !  First draw in blocks + routing !
    !---------------------------------!

    DRAWMYROT(chipname."_side4", 0, YC1, 1 )
    DRAWMYROT( chipname."_X route4", X4, YC1, 1) %if cell_ports(4)_start > 0

    DRAW   ( chipname."_side1",XC1, 0)
    DRAW   ( chipname."_X route1", XC1, Y1 ) %if cell_ports(1)_start > 0

    DRAW   ( cell name, X4+d(4) + clearance - cell_dx,
             Y1+d(1) + clearance - cell_dy )

    DRAWMY ( chipname."_X route3", XC4, Y1+d(1)+d(3)+cell_w(2) ) %c
             %if cell_ports(3)_start > 0
    DRAWMY ( chipname."_side3", XC4, YC1+Y4+YC4 )

    DRAWROT( chipname."_X route2", X4+d(4)+d(2)+cell_w(1),YC2, 1 ) %c
             %if cell_ports(2)_start > 0
    DRAWROT( chipname."_side2", X4+d(4)+d(2)+cell_w(1)+X2,YC2, 1)

    place pad ( 9 )  %if power pad placed = true
    place pad ( 10 ) %if ground pad placed = true

    add railing

  ENDSYMBOL

%end;   { Here ends Place Pads }

%external %routine fill port %alias "ILAP_FILL_PORT" (%record (connf) %name port,
                    %integer type, side, coord, width, %string (4) layer)
   port_type = type
   port_side = side
   port_c    = coord
   port_w    = width
   warning ("Filling port _ width with ".itos(width,0)) %unless 1<width<=8
   port_layer = layer
%end

%end %of %file
