• 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. How to generically wrap a function to be called in a loop...

Stats

  • Locked Locked
  • Replies 12
  • Subscribers 143
  • Views 8056
  • 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

How to generically wrap a function to be called in a loop with for a parametric sweep

ItsMichael
ItsMichael over 1 year ago

Hello,

I would like to create a function which can wrap any function and execute it multiple times with a "sweep" of one of the arguments.

I guess I can do this with funcall() , but how can I parse the arguments in a generic way ?
Will that just involve string manipulation and some sort of evalstring() usage ?

To clarify what I want, let's say my wrapper function is called run_for_list:

run_for_list( plus 3 (1 2 3 4))

So basically I want loop through the plus() function for the list argument.
This is obviously just an example of the usage, my aim is to run a sweep of an argument for a calculator function and return a family of plots.

Any suggestion on how to implement such a thing ?

Thanks in advance,
Michael

  • Cancel
Parents
  • AurelBuche
    AurelBuche over 1 year ago

    Hi Michael,

    I never used `famMap' so I can't really tell if it is the right solution or not for your case

    Anyway, you can call any function by its name using `funcall' or `apply'.

    They almost do the same job except in the way they receive the arguments : 

    1. funcall takes them as if the function was called directly:
      (printf "%d %d" 12 27) ~ (funcall 'printf  "%d %d" 12 27)
    2. apply takes them as a list:
      (printf "%d %d" 12 27) ~ (apply 'printf '("%d %d" 12 27))

    For your example, you can do:

    procedure( sweepCalc(func_name parameters parameter_name_to_sweep start_value step_size stop_value)
      ;; Make sure FUNC_NAME is callable
      (assert (isCallable func_name) "Unable to call %A, it is not a valid function name" func_name)
      ;; Sweep over given range
      (for x start_value stop_value step_size
        (funcall func_name x)
        )
    )

    Your call will become

    sweepCalc('valueAt VT("signal") "n_xValue" 0u 1u 10u)

    Do not forget the quote " ' ", otherwise SKILL interpreter will complain that valueAt is an unbound variable

    You can construct functions name using `concat' but I advise you to avoid it as much as possible...

    (it becomes very hard to debug when you have to deduce built function names that are called in fancy ways)

    sweepCalc(concat( "value" 'At) ...)

    Also,you don't need to pass "n_xValue" as you can define the variable used in `for' directly

    procedure( sweepCalc(func_name parameters start_value step_size stop_value)
      ;; Make sure FUNC_NAME is callable
      (assert (isCallable func_name) "Unable to call %A, it is not a valid function name" func_name)
      ;; Sweep over given range
      (for x start_value stop_value step_size
        (funcall func_name x)
        )
    )
    
    sweepCalc(valueAt VT("signal") 0u 1u 10u)

    And you inverted the order of step_size and stop_value, it is quite confusing compared to the other SKILL calls you can make using those arguments (`for', `linRg', ...). You should stay consistent as much as possible

    Hope this helps

    Aurélien

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • ItsMichael
    ItsMichael over 1 year ago in reply to AurelBuche

    Thank you so much for the reply.
    My own research this morning lead me to funcall(), but from your example I think apply() is best suited if I get the input arguments as a list.
    I have just edited the question to be slightly more generic (right before reading your reply).

    I agree it's better to just use a list for the sweep which can be easily generated by linRg.

    For a generic implementation for such a sweep, I'm currently thinking about getting a parameter list.
    I could check which one of the parameters given is a list it self (which would mean that I would like to sweep that parameter)
    and then, according to it's position in the list, generate the function calls with apply.

    I'll post my solution here if it works.

    Thanks,
    Michael

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • AurelBuche
    AurelBuche over 1 year ago in reply to ItsMichael

    Ok,

    I looked at your updated post

    In this case what you can do is Curry (Wikipedia - Currying) your function and then map it to your list

    run_for_list( 'plus 3 (1 2 3 4))

    procedure( run_for_list( fun val args)
      "Apply FUN with VAL to ARGS"
      assert( isCallable(fun) "Unable to call %A, it is not a valid function name" fun)
      mapcar( lambda( (x) funcall(fun val x)) args )
    )
    
    ;; Is equivalent to
    
    procedure( run_for_list( fun val args)
      "Apply FUN with VAL to ARGS"
      assert( isCallable(fun) "Unable to call %A, it is not a valid function name" fun)
      foreach( x args
        funcall( fun val x)
      )
    )
    

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • ItsMichael
    ItsMichael over 1 year ago in reply to ItsMichael

    Ok, this is what I came up with, sorry for the formatting, for some reason the indentations were not copied [how did you put this cool looking block of code ?]

    procedure( sweepFunc(func_name @rest args)
    ; Make sure func_name is callable
    assert(isCallable(func_name) "Unable to call %A, it is not a valid function name" func_name)
    ; Find where the list in the args is
    list_idx = 0
    while(type(nth(list_idx args)) != type(list()) list_idx++)
    ; Iterate over the list's values
    sweepValues = nth(list_idx args)
    foreach( currentValue sweepValues
    ; substitute the list in the args by it's current value
    new_args = copy(args)
    setf_nthelem(currentValue list_idx+1 new_args)
    ; call the function with the arguments
    res = apply( func_name new_args)
    printf("%L\n" res)
    )
    )

    sweepFunc('plus 1 '(2 4 6 7) 3)

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • AurelBuche
    AurelBuche over 1 year ago in reply to ItsMichael

    Unfortunately my previous answer was considered as SPAM due to a wikipedia link

    It might be released one day...

    Why do you assume the list argument (to sweep over) can be passed at any position?

    This is very messy and makes your code quite complicated

    It should be simpler to force its position (let's assume its the last value)

    procedure( run_for_list( fun val args)
      "Apply FUN with VAL to ARGS"
      assert( isCallable(fun) "Unable to call %A, it is not a valid function name" fun)
      foreach( x args
        funcall( fun val x)
      )
    )

    About my code snippets, I created an emacs-lisp script to extract my code as HTML using my colors

    In this forum when you create your reply you can use : Tools -> Source Code and insert HTML directly

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Reply
  • AurelBuche
    AurelBuche over 1 year ago in reply to ItsMichael

    Unfortunately my previous answer was considered as SPAM due to a wikipedia link

    It might be released one day...

    Why do you assume the list argument (to sweep over) can be passed at any position?

    This is very messy and makes your code quite complicated

    It should be simpler to force its position (let's assume its the last value)

    procedure( run_for_list( fun val args)
      "Apply FUN with VAL to ARGS"
      assert( isCallable(fun) "Unable to call %A, it is not a valid function name" fun)
      foreach( x args
        funcall( fun val x)
      )
    )

    About my code snippets, I created an emacs-lisp script to extract my code as HTML using my colors

    In this forum when you create your reply you can use : Tools -> Source Code and insert HTML directly

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
Children
  • ItsMichael
    ItsMichael over 1 year ago in reply to AurelBuche
    AurelBuche said:
    Why do you assume the list argument (to sweep over) can be passed at any position?

    My use case for this is to run a sweep on one of the custom or built-in calculator functions and display the results as a family.
    So for example if I would like to sweep the value of the clip() function, it takes two arguments so I would like to keep the option to sweep the start or the end of the clip.
    ie. clip(signal '(1u 2u 3u) 10u) or clip(signal 1u '(8u 9u 10u))

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • AurelBuche
    AurelBuche over 1 year ago in reply to ItsMichael

    Ok, I get it but it is very tricky.

    Anyway your code should work but I am afraid it might become really confusing and hard to debug if you use it extensively.

    I did not find a clean and safe solution yet, maybe creating a macro...

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • ItsMichael
    ItsMichael over 1 year ago in reply to AurelBuche

    Thanks for all the suggestions and inputs.
    This will rarely be used, it's mostly to used to sweep calculator functions.
    The current use-case is to sweep a linearity calculation function with different voltage ranges to analyze the dynamic range of the circuit.

    I didn't know about the listp(), was looking for a "isList" function but couldn't find. The naming convention here is not the most consistent.

    I will look into using letseq, exists and remq for finding the first list in the list.
    Thanks again.

    Best regards,
    Michael

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 1 year ago in reply to ItsMichael

    Michael,

    How about this?

    defmacro(abSweepCalc (loopVar loopValues expression)
      ; the local variable has an underscore to try to avoid accidental
      ; capture with a variable in the expression (in the absence of 
      ; hygenic macros)
      `let(((_family famCreateFamily(',loopVar 'double)))
        foreach(,loopVar ,loopValues
          famAddValue(_family ,loopVar ,expression)
        )
        _family
      )
    )

    You can then use:

    abSweepCalc(start '(20n 40n 50n) clip(VT("/OUTP") start 200n))

    In other words, you specify the loop variable, the list of values to iterate over and an expression where you use the loop variable somewhere in the expression. 

    I used a macro (since that's the easiest way of having something code-like in the arguments without messing around with quotes, and avoids needing to use eval() etc.

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
  • ItsMichael
    ItsMichael over 1 year ago in reply to Andrew Beckett

    Thanks Andrew,
    I just noticed your reply.
    Even though I have a working solution I think I will rewrite it to use the macro, it's much more elegant.

    Just wondering about the apostrophes, could you briefly explain the use of the apostrophe before the let statement and inside the first argument of famCreateFamily ? is this related to the syntax of the macro definition ?
    also, is the first argument of the foreach loop empty ?
    I also find the mixing of commas and spaces to separate arguments confusing, do they serve the same purpose ?

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 1 year ago in reply to ItsMichael
    ItsMichael said:
    Just wondering about the apostrophes, could you briefly explain the use of the apostrophe before the let statement and inside the first argument of famCreateFamily ? is this related to the syntax of the macro definition ?
    also, is the first argument of the foreach loop empty ?
    I also find the mixing of commas and spaces to separate arguments confusing, do they serve the same purpose ?

    The commas are not separators. There is an (undocumented) syntax in SKILL where commas can be used as an alternative separator to white space, but that's not what is going on here. What I'm using is the backquote operator (before the let) which is a macro that allows selective evaluation within a quoted list. If you use a normal quote (') rather than backquote (`) then everything inside the list is treated as unevaluated. If however you use backquote, then you can selectively evaluate things inside the list. The way it works is that anything preceded by a comma is evaluated, or if preceded by ,@ (not used in this case) then it's a list that is spliced in at that point.

    Put simply, this is a convenient shorthand for building complex lists with lots of symbol in - for example - I can build:

    `(a b (c d ,someVar) x y)

    rather more concisely than:

    liist('a 'b list('c 'd someVar) 'x 'y)

    In the case of macros it's particularly convenient because the resulting code structure looks like the code I'm trying to generate. Another way of looking at this is that with the surrounding backquote, everywhere that there's a comma-something expression, then the evaluated value of something is stuffed into the resulting code structure. Macros don't evaluate their arguments before they are called, and instead return a list which is the code to use instead - so can programmatically transform a set of arguments into some code which is used instead.

    The specific example I gave of using the macro ends up transforming into this code:

    let(((_family famCreateFamily('start 'double))
        )
        foreach(start '(2e-08 4e-08 5e-08)
            famAddValue(_family start
                clip(VT("/OUTP") start 2e-07))
        )
        _family
    )

    Hope that helps!

    Andrew

    • 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