• 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 8051
  • 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
  • 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
  • AurelBuche
    AurelBuche over 1 year ago in reply to ItsMichael

    If you really want your function to work wherever the list to sweep over is:
    it is cleaner to use `exists' to find it and then `remq' to remove it from the arguments
    (`remove' works also but in this case `remq' is more adapted)

    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
      letseq( ( (sweepValues car( exists( val args listp(val) )))
                (otherArgs  remq( sweepValues args))
                )
        foreach( currentValue sweepValues
         printf("%L\n" apply( func_name currentValue otherArgs))
        )
      )
    )
    
    sweepFunc('plus 1 '(2 4 6 7) 3)

    Using `letseq' I define sweepValues and I can use it in otherArgs definition (this does not work with a classic `let')

    sweepValues contains the first list found in args

    otherArgs contains all values in args except sweepValues

    The final trick is that `apply' can contain any number of middle arguments that are added to the final one using `cons'

    apply( fun arg0 arg1 args)

    is equivalent to 

    apply( fun (cons arg0 (cons arg1 args)))

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • 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
>

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