Qucs / ADMS

ADMS is a code generator for the Verilog-AMS language
GNU General Public License v3.0
94 stars 32 forks source link

possible analogfunction bug #69

Open felix-salfelder opened 7 years ago

felix-salfelder commented 7 years ago

need to access analogfunction calls on right hand sides of assignments and contributions. this patch pushes these calls into rhs/function.

indeed, that seems to be working in

but i get "admst transform unexpected" in
tvrusso commented 4 years ago

Since I sparked interest in addressing this issue, I should comment at this point that it would be a very bad thing to add analog function calls to the "function" list of an expression as this pull request would do.

This is because almost every code generating back-end in the wild is derived from Laurent Lemaitre's original design, and they all use the "function" list to generate function precomputation for assignments --- that is, if an assignment has a function call on the RHS, all the function calls are done in precomputation steps, and their derivatives also generated in a precomputation step. The variables used for precomputation are then used instead of the function call when the assignment itself is generated.

In these back-ends, it is ASSUMED that analog functions are not in that list, and those are handled in a completely different manner. If ADMS started putting analog function calls into "function" all of these back-ends would break. Any back-end without Lemaitre pedigree that needs this sort of thing should do so by having their own adms.implicit.xml and using it instead of ADMS's default version (by using "-x -e /path/to/my/adms.implicit.xml").

That said, for Xyce/ADMS's purposes I found that the method that Lemaitre's analog function code uses to generate analog function derivatives is extremely inefficient for models that make extensive use of analog functions, especially analog functions that call other analog functions. For our needs, we will want to do analog function precomputation in manner similar to that used for builtin functions. I was successful at getting these calls into the "function" list without the failures you observed. I did this by putting the push of the call in a different place. Just in case anyone reading this is developing their own ADMS back-end without relying on Laurent Lemaitre's original analog function implementation, I'm providing the explanation. As noted, it should NOT be done in ADMS's implicit templates, because it would be harmful to existing back-ends.

Farther down in adms.implicit.xml than where you have done this, there is the following code:

    <!-- Table 4-14 - Standard Functions -->
    <!-- Table 4-15 - Trigonometric and Hyperbolic Functions-->
    <admst:when
      test="[name='analysis' or name='\$analysis' or name='\$simparam' or name='simparam' or                                                               
      name='\$shrinka' or name='\$shrinkl' or name='\$limexp' or name='limexp' or name='\$limit' or                                                        
      name='ln' or                                                          
      name='log' or                                                         
      name='exp' or                                                         
      name='sqrt' or                                                        
      name='min' or                                                         
      name='max' or                                                         
      name='abs' or                                                         
      name='pow' or                                                         
      name='floor' or                                                       
      name='ceil' or                                                        
      name='sin' or                                                         
      name='cos' or                                                         
      name='tan' or                                                         
      name='asin' or                                                        
      name='acos' or                                                        
      name='atan' or                                                        
      name='atan2' or                                                       
      name='hypot' or                                                       
      name='sinh' or                                                        
      name='cosh' or                                                        
      name='tanh' or                                                        
      name='asinh' or                                                       
      name='acosh' or                                                       
      name='atanh'                                                          
      ]">
      <admst:push into="$globalexpression/function" select="."/>
      <admst:value-to select="class" string="builtin"/>
    </admst:when>
    <admst:when test="[name='transition']">
      <admst:push into="$globalexpression/function" select="."/>
    </admst:when>
    <admst:otherwise>
      <admst:assert test="[exists(definition)]" format="%(lexval/(f|':'|l|':'|c)): analog function '%(name)' is undefined\n"/>
    </admst:otherwise>
  </admst:choose>
</admst:when>

This is where the builtin functions are already being pushed into the function list. The final "otherwise" only gets executed for analog function calls, and in its existing version just tests that the function being called is defined. This can easily be modified:

    <!-- Table 4-14 - Standard Functions -->
    <!-- Table 4-15 - Trigonometric and Hyperbolic Functions-->
    <admst:when
      test="[name='analysis' or name='\$analysis' or name='\$simparam' or name='simparam' or                                                               
      name='\$shrinka' or name='\$shrinkl' or name='\$limexp' or name='limexp' or name='\$limit' or                                                        
      name='ln' or                                                          
      name='log' or                                                         
      name='exp' or                                                         
      name='sqrt' or                                                        
      name='min' or                                                         
      name='max' or                                                         
      name='abs' or                                                         
      name='pow' or                                                         
      name='floor' or                                                       
      name='ceil' or                                                        
      name='sin' or                                                         
      name='cos' or                                                         
      name='tan' or                                                         
      name='asin' or                                                        
      name='acos' or                                                        
      name='atan' or                                                        
      name='atan2' or                                                       
      name='hypot' or                                                       
      name='sinh' or                                                        
      name='cosh' or                                                        
      name='tanh' or                                                        
      name='asinh' or                                                       
      name='acosh' or                                                       
      name='atanh'                                                          
      ]">
      <admst:push into="$globalexpression/function" select="."/>
      <admst:value-to select="class" string="builtin"/>
    </admst:when>
    <admst:when test="[name='transition']">
      <admst:push into="$globalexpression/function" select="."/>
    </admst:when>
    <admst:otherwise>
      <admst:assert test="[exists(definition)]" format="%(lexval/(f|':'|l|':'|c)): analog function '%(name)' is undefined\n"/>
      <admst:push into="$globalexpression/function" select="."/>
    </admst:otherwise>
  </admst:choose>
</admst:when>

That is, just push the function into "function" right after the assert.

As soon as one does this, one will cause all the precomputation code of existing back ends to be broken, because they are counting on no analog function calls being in the list. So this technique should be reserved for ADMS users who have rewritten their code generators and are overriding ADMS's implicit rules with their own.