• 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. Using sweep names and values with famMap()

Stats

  • Locked Locked
  • Replies 5
  • Subscribers 144
  • Views 16286
  • 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

Using sweep names and values with famMap()

sgreenwo
sgreenwo over 10 years ago

The famMap() function can be used to extend a waveform-processing function to families of waveforms. I've found various examples on Support, Community, comp.cad.cadence, etc. on how to use this function to perform an operation on a family of waveforms, such as Andrew's nice explanation here. Rather than merely operating on data from each element in a family, I want to associate the sweep name and sweep value with data from each member of the family. Because the sweep depth can vary (i.e., length(sweepNames()) will vary), I tried to apply recursion with famMap().

The idea was that if the procedure argument was a waveform, I could strip off the sweep values from the waveform's XVec; if the argument was a family, I would use famGetSweepValues() and pass them into the function using famMap(). For simplicity, the code below is a stripped-down version of what I tried. This version attempts to print the "vds" parameter of FET instance "/Mn1" for each member of the family, while also printing the sweep values for each member.

-------------------------------------------------------------

;;; procedure to print vds

procedure( famVdsPrint( fileName cnrStr famVds)

cond(

  ; Waveform case - print the results
  (drIsWaveform(famVds)
  Xvec = drGetWaveformXVec(famVds)
  Xlen = drVectorLength(Xvec)
  Yvec_vds = drGetWaveformYVec(famVds)

  for(i 0 Xlen-1

    paramVal = strcat(cnrStr "_" sprintf(nil "%g" drGetElem(Xvec i)))
    vds = 1e3 * drGetElem(Yvec_vds i)
    fprintf(fileName "%s,%.1f\n" paramVal vds)
  )
)

; Waveform family case - apply recursion
(famIsFamily(famVds)
  foreach(sweepVal famGetSweepValues(famVds)
    famMap('famVdsPrint fileName strcat(sprintf(nil "%g" sweepVal) cnrStr) famValue(localFamVds sweepVal))
  ) 
)

; Error case
(t
  error("Error: procedure famVdsPrint can't handle %L\n" famVds)
)

)

)

-------------------------------------------------------------

[run a simulation with 3 nested parametric analyses, and open the results]

-------------------------------------------------------------

selectResult('dcOpInfo)

fam_vds = pv("/Mn1" "vds") ;

fileName = outfile("/myPath/myFileName.csv" "w")

famVdsPrint( fileName "" fam_vds)

-------------------------------------------------------------

That did not work. The waveform case works as intended, so the inner-most sweep is fine. The procedure correctly prints the "vds" values for each family member, but incorrectly prints only the outer-most sweep values as it loops through all the family members. Thinking that this might be a variable-scoping problem, I tried to make the variables local by modifying the family case as follows:

-------------------------------------------------------------

; Waveform family case - apply recursion

(famIsFamily(famVds)
  let( (localFamVds)
    localFamVds = famVds
    foreach(sweepVal famGetSweepValues(localFamVds)
      famMap('famVdsPrint fileName strcat(sprintf(nil "%g" sweepVal) cnrStr) famValue(localFamVds sweepVal))
    )
  )
)

-------------------------------------------------------------

Same failed result.

I am not set on any particular implementation. Another idea that occurred to me was to use famToList(fam_vds), but I could not figure out how to print the list elements recursively.

I would appreciate any help in associating family data with its sweep values. I did a fair amount of searching, but my apologies if this has been answered elsewhere. 

Best Regards,

Stephen Greenwood

  • Cancel
  • sgreenwo
    sgreenwo over 10 years ago

    I should also have mentioned that while ocnPrint() can print the data in this test case, I need a different print format, and  in the future I may also want to do something that ocnPrint() can't handle (such as assigning a sweep-based custom plot label to a plotted waveform).

    Best Regards,

    Stephen Greenwood

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Ivars
    Ivars over 10 years ago
    Stephen,

    I've attached a function that you might be able to modify to do what you want. The function igfDescendAndSortFamMeasurements() takes a family of measurements, such as OP("da.M1" "vds") when evaluated on a parametric analysis (ADEXL), and returns a list of values sorted based on a string constructed from the sweep names and values.

    I originally wrote this so that I could create a table of mixed measurements (dcOpInfo and tran) where each type of measurement had the same order relative to the sweep parameters.

    The example below shows the dc operating point Vds extracted from a 3 parameter sweep. I believe the infomation you need for your purpose is available in data table (in x->swpList) and you just need to reformat it.

    Hope this helps.

    (I'll be out of email contact for the next two weeks, so any further replies will need to wait until early April).

    Ivars

    Example of output (function definition below):

    x = igfDescendAndSortFamMeasurement(OP("da.M1" "vds"))

    x->??
    (sortList
    (("VDD12 1.08 temperature -40.0 vincm 0.5" 0.5417283)
    ("VDD12 1.08 temperature -40.0 vincm 0.6" 0.4519738)
    ("VDD12 1.08 temperature -40.0 vincm 0.7" 0.36213)
    ("VDD12 1.08 temperature -40.0 vincm 0.8" 0.2721419)
    ("VDD12 1.08 temperature 125.0 vincm 0.5" 0.5636597)
    ("VDD12 1.08 temperature 125.0 vincm 0.6" 0.4758717)
    ("VDD12 1.08 temperature 125.0 vincm 0.7" 0.3882314)
    ("VDD12 1.08 temperature 125.0 vincm 0.8" 0.3005837)
    ("VDD12 1.32 temperature -40.0 vincm 0.5" 0.7779688)
    ("VDD12 1.32 temperature -40.0 vincm 0.6" 0.6880795)
    ("VDD12 1.32 temperature -40.0 vincm 0.7" 0.5980505)
    ("VDD12 1.32 temperature -40.0 vincm 0.8" 0.5078215)
    ("VDD12 1.32 temperature 125.0 vincm 0.5" 0.7994591)
    ("VDD12 1.32 temperature 125.0 vincm 0.6" 0.7115096)
    ("VDD12 1.32 temperature 125.0 vincm 0.7" 0.6236678)
    ("VDD12 1.32 temperature 125.0 vincm 0.8" 0.5357573)
    ) swpList
    (((("VDD12" 1.08)
    ("temperature" -40.0)
    ("vincm" 0.5)
    ) 0.5417283
    )
    ((("VDD12" 1.08)
    ("temperature" 125.0)
    ("vincm" 0.5)
    ) 0.5636597
    )
    ((("VDD12" 1.32)
    ("temperature" -40.0)
    ("vincm" 0.5)
    ) 0.7779688
    )
    ((("VDD12" 1.32)
    ("temperature" 125.0)
    ("vincm" 0.5)
    ) 0.7994591
    )
    ((("VDD12" 1.08)
    ("temperature" -40.0)
    ("vincm" 0.6)
    ) 0.4519738
    )
    ((("VDD12" 1.08)
    ("temperature" 125.0)
    ("vincm" 0.6)
    ) 0.4758717
    )
    ((("VDD12" 1.32)
    ("temperature" -40.0)
    ("vincm" 0.6)
    ) 0.6880795
    )
    ((("VDD12" 1.32)
    ("temperature" 125.0)
    ("vincm" 0.6)
    ) 0.7115096
    )
    ((("VDD12" 1.08)
    ("temperature" -40.0)
    ("vincm" 0.7)
    ) 0.36213
    )
    ((("VDD12" 1.08)
    ("temperature" 125.0)
    ("vincm" 0.7)
    ) 0.3882314
    )
    ((("VDD12" 1.32)
    ("temperature" -40.0)
    ("vincm" 0.7)
    ) 0.5980505
    )
    ((("VDD12" 1.32)
    ("temperature" 125.0)
    ("vincm" 0.7)
    ) 0.6236678
    )
    ((("VDD12" 1.08)
    ("temperature" -40.0)
    ("vincm" 0.8)
    ) 0.2721419
    )
    ((("VDD12" 1.08)
    ("temperature" 125.0)
    ("vincm" 0.8)
    ) 0.3005837
    )
    ((("VDD12" 1.32)
    ("temperature" -40.0)
    ("vincm" 0.8)
    ) 0.5078215
    )
    ((("VDD12" 1.32)
    ("temperature" 125.0)
    ("vincm" 0.8)
    ) 0.5357573
    )
    ) meas
    (0.5417283 0.4519738 0.36213 0.2721419 0.5636597
    0.4758717 0.3882314 0.3005837 0.7779688 0.6880795
    0.5980505 0.5078215 0.7994591 0.7115096 0.6236678
    0.5357573
    ) swpTbl table:igfTable
    )



    ;; When different types of measurements are made on a family of results, for
    ;; example a riseTime() vs. a pv(), the returned list/waveform of results
    ;; can be in different orders since the descent path through the family heirarchy
    ;; appears to be different for simulation results compared to simulation parameters.
    ;; To allow the different type of measurements to be correctly grouped, the sweep
    ;; parameters are recorded during the descent through the family heirarchy for each
    ;; measurement. Then the sweep parameters are sorted alphanumerical and then a sort
    ;; key consisting of concatenation of all the sweep parameter names and values is created.
    ;; This sweep key is then used to sort the measurement values. This ensures that all
    ;; measurements will be ordered in the same way (since the sweep parameters are constant
    ;; across each job).
    ;;
    ;; Usage example:
    ;;
    ;; x = igfDescendAndSortFamMeasurement(OP("x1.x2.M1" "vds"))
    ;;
    ;; A list of the sorted measurements can be obtained using:
    ;; x->meas
    ;;
    ;; The sorting list can be seen using:
    ;; x->sortList
    ;;
    ;; A list of the sweep parameters plus measurement can be obtained:
    ;; x->swpList
    ;;
    ;; use x->?? to see the full table
    ;;

    procedure( igfDescendAndSortFamMeasurement(w @key (data ncons(nil)) )
    let( (name val_list next_wave meas smeas)
    when(!data->swpTbl
    data->swpTbl = makeTable("igfTable")
    );when
    when(!data->meas ;; list of individual measurements
    data->meas = nil
    )
    when(!data->swpList ;; list of measurements plus the sweep name/values
    data->swpList = nil
    )
    when(!data->sortList ;; sorted list of measurements
    data->sortList = nil
    )
    name = famGetSweepName(w)
    if(name
    val_list = famGetSweepValues(w)
    val_list = nil
    )
    if(val_list
    then
    foreach(val val_list
    data->swpTbl[name] = val
    if(famIsFamily(w)
    then
    next_wave = famValue(w val)
    else
    next_wave = nil
    meas = famValue(w val)
    )
    if(next_wave
    then
    data = igfDescendAndSortFamMeasurement(next_wave ?data data )
    else
    when(meas == nil
    meas = "--"
    )
    if(drIsWaveform(meas)
    m = igfVecToList(drGetWaveformYVec(meas))
    m = meas
    )
    data->meas = append1(data->meas m)
    data->swpList = append1(data->swpList list(tableToList(data->swpTbl) m))
    strList = nil
    keys = sort(setof(k data->swpTbl t) nil ) ;; extract and alpha sort all keys
    foreach( key keys strList = append1(strList sprintf(nil "%s %L" key data->swpTbl[key])))
    data->sortList = append1(data->sortList list(buildString(strList) m) )
    );if
    );foreach
    smeas = sortcar(data->sortList nil)
    data->meas = foreach(mapcar m smeas cadr(m))
    else
    data->meas = w
    );if
    `data
    );let
    );procedure

    ;; Convert a waveform vector into a list. Based on code by Andrew Beckett
    procedure( igfVecToList( vec )
    let( (len i vecList)
    len = drVectorLength(vec)
    for( i 0 sub1(len)
    vecList = tconc(vecList drGetElem(vec i) )
    );for
    car(vecList)
    );let
    );procedure( igfVecToList
    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • sgreenwo
    sgreenwo over 10 years ago

    Thanks, Ivars! I will go through your code. Even if I can't use it directly I can try to adapt the concepts in it.

    Much obliged,

    Stephen

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 10 years ago

    Stephen,

    You might find this useful too. It takes a family, and produces a list of abFamilySignal objects, for each leaf waveform. As part of doing this, it constructs a name which contains the sweep name and value. I did this as part of some code which generates a "VCSV" (ViVA CSV) file from arbitrary results - I've not included the entire code, but the key part is here. May not be quite what you want, but it may be illustrative of an approach you could take.

    Regards,

    Andrew.

    ;------------------------------------------------------------------------
    ; Container structure to keep the cleaned signal name,
    ; length, whether it's complex etc
    ;------------------------------------------------------------------------
    (defstruct abFamilySignal wave name len isComplex)
    
    /*****************************************************************
    *                                                                *
    * (abUnwrapFamily fam @optional topName top (level 0) (name "")) *
    *                                                                *
    *    Convert a family or waveform into a flat list of single     *
    *  waveforms, wrapped by an abFamilySignal container structure   *
    *                                                                *
    *****************************************************************/
    
    (defun abUnwrapFamily (fam @optional topName top (level 0) (name ""))
      (let (dim sweepName)
        (unless top (setq top fam))
        (setq dim (if (or (famIsFamily top) (drIsWaveform top))
                    (drWaveformGetDimension top)
                    0))
        (cond
          ((and (famIsFamily top) (lessp level (sub1 dim)))
           (setq sweepName (famGetSweepName top level))
           (foreach mapcan sweepValue (famGetSweepValues fam)
                    (abUnwrapFamily 
                      (famValue fam sweepValue) topName top (add1 level) 
                      (sprintf nil "%s (%s=%Y)" name sweepName sweepValue))
                    )
           )
          (t (list (make_abFamilySignal 
                     ?wave fam 
                     ; the messing with strcat in format is to stop SCCS doing 
                     ; keyword substitution!
                     ?name (sprintf nil (strcat "%Y" "%s")
                                    (or topName (famGetExpr top)) name))))
          )
        )
      )
    
    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • sgreenwo
    sgreenwo over 10 years ago

    Hi Andrew,

    After playing with your code and reading some key sections of the sklangref, I am past the point that was hanging me up. I understand these forums are not your "day job" and I appreciate the help!

    Best Regards,

    Stephen

    • 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