• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Custom IC SKILL
  3. Creating rectangles with rounded corners in Layout using...

Stats

  • Locked Locked
  • Replies 2
  • Subscribers 143
  • Views 16945
  • Members are here 0
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Creating rectangles with rounded corners in Layout using SKILL (PCell)

Sheppy
Sheppy over 9 years ago

HI,

I have written some code to generate the layout of a p-cell high-voltage transistor. All shapes created are rectangles (like the active, contact, metal layers).

While measuring the devices it was found that the break-down voltage was really low, much lower than anticipated. One of the solutions that has already been tested, and has been proven to be very effective, is to create rounded corners for some of the layers involved. The layouts to test this were all generated manually. No it is my task to put that (rounding the corners) in the p-cell.

Is there an easy solution to convert a rectangle in a rounded-corner rectangle? Something like this already exists in Cadence as a SKILL procedure? If not I just have to come-up with something clever myself...

With kind regards,

Sjoerd

  • Cancel
  • Andrew Beckett
    Andrew Beckett over 9 years ago

    Hi Sjoerd,

    There's no built-in function to do this within a PCell (there's leModifyCorner, but that can't be used in a PCell). So you'd have to write something to do this yourself.

    However, this is easily done using Virtuoso PCell Designer. I added one command to my existing transistor pcell (this is a silly example - just did it because I didn't have anything more relevant to had):

    Virtuoso PCellDesigner is a Cadence services product - we have quite a few customers for it world wide now. You can find a little more about it from these CDN Live presentations:

    • PCell Class Library for Use Over Different Process Nodes by Using PCell Designer and Object Oriented Programming
    • Hierarchical Module Design With Cadence PCell Designer
    • Jumping analog layout productivity and accuracy with PCell

    I'd be happy to give you a demo (the tool is developed by my team) if you're interested (send me a friend request on the forum - note, this is not an invitation to everyone to send me a friend request; I get too many people sending me friend requests with questions in which they could have posted in the forums; I only accept friend requests when I have asked people to contact me this way so that we can get in touch directly).

    Regards,

    Andrew.

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Sheppy
    Sheppy over 9 years ago

    Hi Andrew,

    As it turned out (using 3-d field simulations and measurement results) the best solution for our case is to have elliptical ends, so not just simple rounded corners.

    The solution I came up with is to write a procedure (added below) which creates a polygon of a quarter of an ellipse (the top-left section). In the pcell code, this polygon gets copied and then transformed to create the top-half or bottom-half of an ellipse. All this is DRC clean in the process we-re using,

    The code of the procedure:

    procedure( smtCreateQuarterEllipsePointList( a b dbGrid @optional ( internal nil ) ( diagonal nil ) ( addSpike nil ) )
        let( (    ( A 0.0 ) ( B 0.0 )
            ( ellipseFormula "" )
            ( x 0 ) ( y 0 )
            ( coordX 0 ) ( coordY 0 ) ( xOffset 0.0 )
            ( interPoint nil ) ( newPoint nil ) ( pointList nil )
            ( finished nil )
            ( nrOfPoints 0 )
            ( prevPoint nil ) ( curPoint nil ) ( extraPoint nil ) ( diagonalPointList nil )
            ( deltaY 0.0 )
            ( listToReturn nil ) )

            ;;; Make sure the values for a and b are on the database grid...
            a = abs( spdkPutOnGrid( a dbGrid "auto" ) )
            b = abs( spdkPutOnGrid( b dbGrid "auto" ) )
            
            ;;; By default, the "digitized" ellipse is outside of the perfect ellipse.
            ;;; By setting the switch "internal" to TRUE, it (the digitized ellipse) will
            ;;; be drawn on the inside of the perfect ellipse.
            when( internal
                a = spdkPutOnGrid( ( a - dbGrid ) dbGrid "auto" )
                b = spdkPutOnGrid( ( b - dbGrid ) dbGrid "auto" )
                xOffset = dbGrid
            ) ;;; end of when

            ;;; This is a re-write of the general ellipse formula: x2/a2 + y2/b2 = 1
            ;;; Formula now hard-coded into this procedure, but will eventually be stored in a
            ;;; global disembodied property list so it can be changed at any time without changing
            ;;; the code of this procedure. Use this (womething similar) to retrieve:
            ;;; ellipseFormula = get( globVar 'ellipseFormula )
            ellipseFormula = "abs(sqrt(B*(1-((x*x)/A))))"
            A = a * a
            B = b * b
            
            ;;; The formula of an ellipse has negative values for x and y.
            ;;; That is not wanted, but to get correct results, the starting point has to be negative.
            x = spdkPutOnGrid( minus( a ) dbGrid "auto" )

            while( !finished
                ;;; Calculate the value of y...
                y = evalstring( ellipseFormula )
                ;;; In order to have only positive values for x and y, the x value has to be shifted.
                coordX = spdkPutOnGrid( ( x + a + xOffset ) dbGrid "auto" )
                coordY = spdkPutOnGrid( y dbGrid "up" )
                ;;; Constructing this new point...
                newPoint = coordX:coordY
                ;;; In order to create a "staircase" ellipse, points inbetween
                ;;; the calculated points have to be added. That interPoint is constructed here.
                interPoint = list( spdkPutOnGrid( ( coordX - dbGrid ) dbGrid "auto" ) coordY )

                ;;; Only in some cases, this interpoint should be added to the list op points.
                ;;; If it is a piont that is already in the list, don't add it.
                ;;; Do not add it at the start: the first point can't have an interPoint added...
                ;;; If an ellipse with diagonals is required, only the calculated points must be stored.
                ;;; In the next section the extra points for the diagonals will be generated.
                unless( member( interPoint pointList ) || zerop( coordX ) || diagonal
                    pointList = append( pointList list( interPoint ) )
                ) ;;; end of unless

                ;;; Adding the newPoint to the list of points.
                pointList = append( pointList list( newPoint ) )

                x = spdkPutOnGrid( ( x + dbGrid ) dbGrid "auto" )

                ;;; Check when to leave the while() loop...
                when( x > 0.0
                    finished = t
                ) ;;; end of when
            ) ;;; end of while
            ;;; Adding the last point to close the polygon...
            pointList = append( pointList list( ( a + xOffset ):0.0 ) )

            ;;; If no diagonals are needed, this is where the correct output list is stored in the return value.
            listToReturn = pointList

            ;;; Diagonals needed...
            when( diagonal
                ;;; In order to get a DRC clean ellipse, little spikes have to be added in some cases.
                ;;; This is where the spike at the lower-left corner of the ellipse polygon is added
                ;;; to the list of points.
                if( !addSpike
                then
                    diagonalPointList = append( diagonalPointList list( car( pointList ) ) )
                else
                    coordX = spdkPutOnGrid( ( xCoord( car( pointList ) ) - dbGrid ) dbGrid "auto" )
                    coordY = yCoord( car( pointList ) )
                    diagonalPointList = append( diagonalPointList list( coordX:coordY ) )
                    coordX = xCoord( car( pointList ) )
                    coordY = spdkPutOnGrid( ( yCoord( car( pointList ) ) + dbGrid ) dbGrid "auto" )
                    diagonalPointList = append( diagonalPointList list( coordX:coordY ) )
                ) ;;; end of if

                ;;; Determine the number of points in the list of points...
                nrOfPoints = length( pointList )

                ;;; Start the list at position 2, since the first point is already processed by if( !addSpike ... )
                ;;; Stop a few points before the end of the list, last points will be processed when adding
                ;;; (or not adding) a spike at the end.
                for( point 2 nrOfPoints - 2
                    prevPoint = nthelem( point - 1 pointList )
                    curPoint = nthelem( point pointList )
                    deltaY = spdkPutOnGrid( ( yCoord( curPoint ) - yCoord( prevPoint ) ) dbGrid "auto" )
                    ;;; Diagonals should be created at every point of the list, however:
                    ;;; - if delta-x and delta-y are both the same as dbGrid, not needed to add
                    ;;;   since the line between those points is already a diagonal
                    ;;; - if delta-y is zero, than it is a horizontal line, so no diagonal can be added
                    ;;; This means that only if delta-y is larger than dbGrid, a diagonal can/must be added.
                    ;;; Delta-x doesn't need to be checked since this will always be dbGrid.
                    when( deltaY > dbGrid
                        coordX = xCoord( prevPoint )
                        coordY = spdkPutOnGrid( ( yCoord( curPoint ) - dbGrid ) dbGrid "auto" )
                        extraPoint = coordX:coordY
                        diagonalPointList = append( diagonalPointList list( extraPoint ) )
                    ) ;;; end of when
                    diagonalPointList = append( diagonalPointList list( curPoint ) )
                ) ;;; end of for point

                curPoint = nthelem( nrOfPoints - 1 pointList )

                ;;; Adding (or not) a spike to the top-right corner of the polygon...
                if( !addSpike
                then
                    diagonalPointList = append( diagonalPointList list( curPoint ) )
                else
                    coordX = xCoord( curPoint )
                    coordY = spdkPutOnGrid( ( yCoord( curPoint ) + dbGrid ) dbGrid "auto" )
                    diagonalPointList = append( diagonalPointList list( coordX:coordY ) )
                ) ;;; end of if

                diagonalPointList = append( diagonalPointList last( pointList ) )
                listToReturn = diagonalPointList
            ) ;;; end of when

            listToReturn

        ) ;;; end of let
    ) ;;; end of procedure smtCreateQuarterEllipsePointList

    The code for spdkPutOnGrid, which is used in the other procedure:

    procedure( spdkPutOnGrid( value grid direction @optional ( accuracy 1e-6 ) )
        let( (  ( onGrid nil )
            ( theTest 0.0 ) )
        
            unless( fixp( value ) || floatp( value )
                error( "spdkPutOnGrid: argument #1 should be a integer or float - %L" value )
            ) ;;; end of unless
            unless( fixp( grid ) || floatp( grid )
                error( "spdkPutOnGrid: argument #2 should be a integer or float - %L" grid )
            ) ;;; end of unless
            unless( equal( lowerCase( direction ) "up" ) || equal( lowerCase( direction ) "down" ) || equal( lowerCase( direction ) "auto" )
                error( "spdkPutOnGrid: argument #3 should be a string, possible input: \"up\" or \"down\" or \"auto\" - %L" direction )
            ) ;;; end of unless
            unless( accuracy < 1.0
                error( "spdkPutOnGrid: argument #4 should be smaller then 1 (argument 4 sets the accuracy) - %L" accuracy )
            ) ;;; end of unless
            
            if( !zerop( value )
            then
                case( lowerCase( direction )
                    ( "up"
                        onGrid = atoi( sprintf( nil "%f" float( value / grid ) ) ) * grid
                        unless( abs( ( value - onGrid ) / value ) < ( grid * accuracy )
                            if( negativep( value )
                            then
                                onGrid = onGrid
                            else
                                onGrid = onGrid + grid
                            ) ;;; end of if
                        ) ;;; end of unless
                    )
                    ( "down"
                        onGrid = atoi( sprintf( nil "%f" float( value / grid ) ) ) * grid
                        unless( abs( ( value - onGrid ) / value ) < ( grid * accuracy )
                            if( negativep( value )
                            then
                                onGrid = onGrid - grid
                            else
                                onGrid = onGrid
                            ) ;;; end of if
                        ) ;;; end of unless
                    )
                    ( "auto"
                        ;;; If value=12.75 and grid=0.1, the output should be 12.8
                        ;;; However, the output is 12.7. The calculation in the "theTest" statement
                        ;;; results in something slightly less than 0.5, so it would round-down...
                        ;;; Therefor, the third condition is added: if the difference is small relative to the grid,
                        ;;; the remainder is regarded as a half (0.5), thus round-up.
                        onGrid = atoi( sprintf( nil "%f" float( value / grid ) ) ) * grid
                        theTest = ( float( value ) - onGrid ) / grid
                        cond(
                            ( theTest == 0.0
                                onGrid = onGrid
                            )
                            ( theTest > 0.5
                                onGrid = onGrid + grid
                            )
                            ( abs( ( theTest - 0.5 ) / theTest ) < ( grid * accuracy )
                                onGrid = onGrid + grid
                            )
                            ( t
                                onGrid = onGrid
                            )
                        ) ;;; end of cond
                    )
                ) ;;; end of case
            else
                onGrid = 0
            ) ;;; end of if
            
            when( fixp( grid )
                onGrid = fix( onGrid )
            ) ;;; end of when
            when( floatp( grid )
                onGrid = float( onGrid )
            ) ;;; end of when
            
            ;;; Return the value...
            onGrid
            
        ) ;;; end let
    ) ;;; end of procedure spdkPutOnGrid

    How to use this code:

    1. Load the code
    2. Create a new layout window or open an existing one and make it active
    3. cv=geGetWindowCellView()

    4. points = smtCreateQuarterEllipsePointList( 3.0 4.0 0.01 )
      3.0 = the width of the total ellipse if a full ellipse was drawn
      4.0 = the height of the total ellipse if a full ellipse was drawn
      0.01 = the database grid

    5. lpp = list("MET1" "drawing") (specify a valid layer from your PDK)
    6. dbCreatePolygon( cv lpp points )
    7. Check layout window for the result

    Play with some of the optional arguments to see what it is doing. To get 45-degrees angles do something like points = smtCreateQuarterEllipsePointList( 3 4 0.01 nil t ).

    With kind regards,

    Sjoerd

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information