• 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. SKILL program structure for 3rd parties

Stats

  • Locked Locked
  • Replies 6
  • Subscribers 143
  • Views 9927
  • 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

SKILL program structure for 3rd parties

BorisD
BorisD over 3 years ago

Greetings,

 I am supporting a SKILL based tool which has the following structure:

  • its a GUI based
  • the init file setup a global DPL data structure which is referenced often
  • each procedure is written in its own .il file

 Because of this tool is meant for 3rd party customer, I'd like to limit as much as possible the global variables and functions visible. I am splitting this task in two pieces to tackle it:

  • The source code would be implemented as context, which would effectively 'hide' my procedures from getting examined by user. All procs would be read and write protected but still usable which is fine by me.
  • The crux of the matter is the global DPL structure which is defined in the beginning and used throughout almost every step of tool execution.

 Ideally I'd like to get rid of that global data structure and make the tool 'invisible' to the toplevel. And yes, the tool is quite large in size and the solution I am searching should be non-invasive. Several approaches that I have considered so far:

  1. Refactor the entire source code to pass the DPL as argument back and forth to the numerous procedures or leverage the dynamic scoping - this wont work. The flow is non-linear, because I have a GUI. Whenever an execution of the current task completes, the DPL data is gone and I need it if another button is pressed in the GUI.
  2. Use setVarWriteProtect to forbid overriding the data in my DPL - this solves only half of the problem as the DPL would still be visible in toplevel.
  3. Instead of DPL as storage container, dump data in temporary file and read it back on demand - this can work, but the performance of the tool would greatly suffer because I quite often reference the DPL data. So it's far from optimal.
  4. Make use of envObjects and/or closures in SKILL++ framework - to my understanding these still have to be saved in a variable at some point which will be visible as well.
  5. Hide/Define my variables in SKILL++ mode and thus make them 'invisible' to SKILL toplevel - haven't tried it but my tests shows is possible solution, still not optimal, because user can switch to toplevel( 'ils) at any point.
  6. Leverage SKILL++ lexical scoping - now I admit I am not experienced user of it but from what I read in the documentation Cadence SKILL Language User Guide chapters on SKILL++ I did not find a way to really benefit from it in my scenario, because of the partitioning of my source code in different files AND the non-linear flow of execution.

NOTE: I think its worth pointing out that this DPL is basically used for caching values from the tool forms, hold setup variables and temporary storing process ids. In other words, the information in it can be considered non-sensitive in my opinion but still I'd like to be protected at some level.

Any comments and suggestions are highly appreciated. Thank you!

  • Cancel
Parents
  • mbracht
    mbracht over 3 years ago

    Hi Boris,

    I'am not really sure whether that helps but...instead of keeping a global dpl that is visible to everybody you could create a "fake" object in terms of SKILL++ lexical scoping. I don't know the slots of your dpl but let's assume it accommodates the information about a cell view in terms of library, cell and view name - in other words it has the slots libName, cellName and viewName. Now look at the below procedure that is defined in SKILL++ - by putting it in a inScheme() block you don't need to have it in an .ils file:

    (inScheme
       (defun newData ()
          (let (methods libName cellName viewName)
             methods = (list nil
                         'setLibName (lambda (param) libName = param)
                         'setCellName (lambda (param) cellname = param)
                         'setViewName (lambda (param) viewName = param)
                         'getLibName (lambda () libName)
                         'getCellName (lambda () cellName)
                         'getViewName (lambda () viewName)))))

    ...and then

    myData = newData()
    myData->setLibName("LIB1")
    myData->setCellName("CELL1")
    myData->getLibName()
    ...

    As you can see newData returns a dpl that has methods to read and write the data by getter and setter methods - but the data itself (libName,cellName,viewName) is invisible from outside. So the only thing that needs to be global now is the variable myData.
    The definition of the newData() procedure could be in your context file then.

    Max

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
Reply
  • mbracht
    mbracht over 3 years ago

    Hi Boris,

    I'am not really sure whether that helps but...instead of keeping a global dpl that is visible to everybody you could create a "fake" object in terms of SKILL++ lexical scoping. I don't know the slots of your dpl but let's assume it accommodates the information about a cell view in terms of library, cell and view name - in other words it has the slots libName, cellName and viewName. Now look at the below procedure that is defined in SKILL++ - by putting it in a inScheme() block you don't need to have it in an .ils file:

    (inScheme
       (defun newData ()
          (let (methods libName cellName viewName)
             methods = (list nil
                         'setLibName (lambda (param) libName = param)
                         'setCellName (lambda (param) cellname = param)
                         'setViewName (lambda (param) viewName = param)
                         'getLibName (lambda () libName)
                         'getCellName (lambda () cellName)
                         'getViewName (lambda () viewName)))))

    ...and then

    myData = newData()
    myData->setLibName("LIB1")
    myData->setCellName("CELL1")
    myData->getLibName()
    ...

    As you can see newData returns a dpl that has methods to read and write the data by getter and setter methods - but the data itself (libName,cellName,viewName) is invisible from outside. So the only thing that needs to be global now is the variable myData.
    The definition of the newData() procedure could be in your context file then.

    Max

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
Children
  • Andrew Beckett
    Andrew Beckett over 3 years ago in reply to mbracht

    Along similar lines to Max's suggestions, you could also do:

    let(((scopedDPL ncons(nil)))
      procedure(printMessage(message)
        printf(message)
      )
      globalProc(MyStoreInfo(theInfo)
        printMessage("storing\n")
        scopedDPL->data=theInfo
      )
      globalProc(MyRetrieveInfo()
        printMessage("retrieving\n")
        scopedDPL->data
      )
    )

    This would be in a .ils file (or wrapped in inScheme, but a .ils suffix would be best). The benefit of this is that MyStoreInfo and MyRetrieveInfo have the same calling syntax as a normal function, and the globalProc has the same defining semantics as procedure. There's also defglobalfun if you prefer defun semantics. The other procedure within the let is local, and the scopedDPL is only visible within the scope - so both the DPL and the printMessage are not accessible from outside. They're not hidden by convention, but truly hidden.

    This is using your point 6 approach. That's how I'd do it... (or Max's approach too  - his example is creating a new environment for each call to newData(), whereas mine has a single shared environment).

    Andrew.

    • Cancel
    • Vote Up +2 Vote Down
    • Cancel
  • AurelBuche
    AurelBuche over 3 years ago in reply to Andrew Beckett

    I really like Andrew's approach as this is how I code most of my scripts now (in SKILL++, using many closures)

    If you want your global procedures to be hidden name them with  a "_" prefix, it won't guarantee they will not appear in some CIW messages according to user log filters. But it is more of a convention saying that functions starting with an underscore are not supposed to be used by 3rd parties. (It will still prevent the user from finding the functions using listFunctions for instance)

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
  • BorisD
    BorisD over 3 years ago in reply to AurelBuche

    Thank you everyone for you inputs!

     I've tested the proposed solutions with simpler code and it appears to do exactly what I need it do. Both Max and Andrew's suggestions work fine, but I think the latter one looks cleaner to me as I do not have to 'instantiate the pseudo object'. My take on how it actually works, as I am new to lexical scope rules and it might be useful for other newbies, is:

    • inScheme() or .ils extension enforce to lexical scope rules. Simply put whatever variable value you have within certain scope (aka block of code) it stays that way.
    • That scope now is in a way 'container' and in Andrew's solution it is 'invisible' as it's not really assigned to any variable, but remains in virtual memory.
    • To refer to data in the 'container', when in it's not assigned to any particular symbol, you must define functions which are visible outside the 'container' -> globalProc. or what Max referred to as getters and setters.
    • These now globalProcs are the sole instruments to manipulate any data defined inside the 'container'. Being global they are accessible to anyone, but if put in context they will at the very least be write protected.

    Please, correct me if any of this is incorrect in any way. 

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • mbracht
    mbracht over 3 years ago in reply to BorisD

    yes that's pretty much the way it works. The context (the state of local variables) at the time of the creation of these functions gets "closed over" (which is why it's called a closure...) meaning those actual local variables are not garbage collected as long as there is still a way to access them. So Andrews functions and my getters/setters have access to variables that are invisible from the callers point of view.

    • 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