Error while building BSIM4.7-based verilog model

125 views
Skip to first unread message

Albert Kumar

unread,
Dec 2, 2022, 3:26:43 PM12/2/22
to xyce-users
I'm using Xyce 7.5 and the buildxyceplugin script to build a verilog-A model called BSIM4SIC that is a modified version of BSIM4.7.

In buildxyceplugin.log, I get the error:
[fatal..] /home/akumar/veriloga_test_delete/BSIM4SiC_V61.va: during lexical analysis syntax error at line 2368 -- see 'if'

Line 2368 of the *.va code  is part of the block below:
2361   analog function real get_nuintd;
2362        //
2363        input nf;
2364        integer nf;
2365        input minsd;
2366        real minsd;
2367
2368        if (nf % 2 != 0)
2369            //odd
2370            get_nuintd = 2.0 * max((nf - 1.0) / 2.0, 0.0);
2371        else
2372        begin
2373            //even
2374            if (minsd == 1)
2375                // minimize # of source
2376                get_nuintd = 2.0 * max(nf / 2.0 - 1.0, 0.0);
2377            else
2378                get_nuintd = nf;
2379        end
2380    endfunction

Any idea what could be the issue?

xyce-users

unread,
Dec 2, 2022, 5:12:08 PM12/2/22
to xyce-users
Before I get too deep into this, let me warn you that the Xyce team has *never* been able to import any BSIM device (bsim6, bsim-cmg, bsim-soi) into Xyce using Xyce/ADMS without making at least some small modifications to the Verilog-A source.  This one is clearly going to need some.  I cannot begin to guess what other problems you may find after addressing this one.  You can see some of the changes that we had to make to BSIM verilog-a in the various "README_Xyce" files that accompany their sources in Xyce/utils/ADMS/examples (which are not really just examples, they're the actual Verilog-A source for the built in devices in Xyce that were compiled with Xyce/ADMS).  I would not get your hopes up for a quick import, you may have your work cut out for you.

That said,  there are multiple problems with this analog function.

ADMS considers it a syntax error for the body of an analog function not to be wrapped in a begin/end pair.  Since presumably this Verilog-A source imports into other simulators just fine, it is probably the case that this is just a limitation of ADMS's parser and probably represents yet another way that the ADMS program was not kept up to date  with the Verilog-A standard.  Wrapping the body with begin/end:

   analog function real get_nuintd;
      //                                                                        
      input nf;
      integer nf;
      input   minsd;
      real    minsd;
      begin

         if (nf % 2 != 0)
           //odd                                                                

           get_nuintd = 2.0 * max((nf - 1.0) / 2.0, 0.0);
         else
           begin
              //even                                                            
              if (minsd == 1)

                // minimize # of source                                        
                get_nuintd = 2.0 * max(nf / 2.0 - 1.0, 0.0);
              else
                get_nuintd = nf;
           end
      end
   endfunction

will get rid of that "lexical analysis syntax error" problem.

But all that will get you is the next failure, which is that Xyce/ADMS (the part that takes what ADMS has parsed and turns it into C++ that Xyce can link to) was not written to allow the arguments of an analog function to be of different type than the return value.  Analog function support in Xyce/ADMS really was never completely finished, and this particular limitation is a holdover from one particular quirky way that it generates analog function code so that other analog functions can call them.  It might be possible to remove that limitation, but staff availability to work on Xyce/ADMS is low.

However, I can guess at a possible workaround:  declare the input parameter to be "input nfr; real nfr;" instead of integer, and then set a local integer variable "nf" from that.  It should be the case that calling the analog function with integer arguments should upcast the integer value to a real number appropriately and this may not have any terrible impact on the device performance.  But I have not tried it, and this is just a guess:

  analog function real get_nuintd;
      //                                                                        
      input nfr;
      real nfr;
      input   minsd;
      real    minsd;
      begin
          integer nf;
          nf=nfr;     // this should truncate any real argument back down to an integer
          if (nf % 2 != 0)    // Xyce/ADMS also does not work correctly if you try to take the modulus of a real variable, so it is necessary to use the integer here.
           //odd                                                                

           get_nuintd = 2.0 * max((nf - 1.0) / 2.0, 0.0);
         else
           begin
              //even                                                            
              if (minsd == 1)

                // minimize # of source                                        
                get_nuintd = 2.0 * max(nf / 2.0 - 1.0, 0.0);
              else
                get_nuintd = nf;
           end
      end
   endfunction

I cannot say whether the function so modified will *work* in the BSIM-SiC, but I can say for sure that making these changes to the analog function will get you past both of these specific processing failures.  The first addresses the ADMS parser limitation, the second Xyce/ADMS's other limitation.

Albert Kumar

unread,
Dec 3, 2022, 1:24:11 AM12/3/22
to xyce-users
Thank you. Yes, the model works with Spectre, but not with Xyce.

You were right fixing these two issues opened up a bunch of other issues, but I think I fixed most of them.Just a note that buildxyceplugin doesn't seem to like the non-input argument "integer nf" inside the conditional statements. It seems like only input argument variables are allowed inside the conditionals. Am I incorrect?

Now, I'm stuck with the fatal message listed below. I checked line 1100 in the xml file, but I don't understand the issue. Any suggestions?

[info...] -x: skipping any implicit xml scripts
[info...] admsXml-2.3.7 (unknown) Dec  1 2022 21:58:57
[info...] Top-level analog/code assigns to 241 variables.
[fatal..] Template not found:
[fatal..]   <admst:template match="xyceAnalogFunctions:callfunction"/>
[fatal..]   see:  [/opt/Xyce-7.5/Serial/share/xml/xyceAnalogFunction_nosac.xml:1100]

xyce-users

unread,
Dec 3, 2022, 9:43:46 AM12/3/22
to xyce-users
Quick bit of pedantry:  "buildxyceplugin" is just a script that invokes ADMS with the Xyce/ADMS backend code generator and runs the C++ compiler on it.  The problem here is with Xyce/ADMS (and the very first issue of begin/end missing was an issue with ADMS).

I'm not sure what you mean by "does not seem to like the non-input argument "integer nf" inside the conditional statements."  The modified function I posted needed to use the integer "nf" because of how Xyce/ADMS generates code for the modulus operator %.  While the Verilog-A standard gives clear guidance for what A%B should mean when A and B are not both integers, Xyce/ADMS simply emits "A%B" and lets the C++ compiler handle it.  C++ does not like it when A and B are not both integers, and you'd get a C++ compile time error if you used the real variable there.  That's why you have to use the integer nf in this particular conditional.  Were the conditional statement not using the modulus operator, using the real variable would be fine.

I cannot  give a quick answer to your question about the last error you're stuck with without access to the verilog-A source, and even then probably can't spend the time hunting down the fix.  However, what happening here is that something in some analog function is using a "callfunction" and callfunctions from within analog functions are not yet implemented (and may never be in Xyce/ADMS).  "callfunctions" are things like "$strobe","$bound_step", "$write", "$warning", that sort of thing.  So if you've got an analog function that is using one of those, that's the problem.

Albert Kumar

unread,
Dec 3, 2022, 5:40:41 PM12/3/22
to xyce-users
Below is a simplified function. I get a "not declared in this scope" error due to the "integer nf" variable on line 5.
If I use "real nf" then there is no error, but of course there is no purpose.
Is this expected?
1  analog function real get_nuintd;                                                                      
2      input nfr;
3      real nfr;
4      begin
5          integer nf;
6          nf = nfr;   // "nf=1.3" still gives an error.                                
7          get_nuintd = 1.0*nf;
8      end
9   endfunction

I thought it was because inside a conditional "if" and there was perhaps a scope issue, but it's not related - sorry for the confusion. Below is the log file.

N_DEV_ADMSbsim470sic.C: In function 'double Xyce::Device::ADMSbsim470sic::AnalogFunctions::d_get_nuintd(double, double)':
N_DEV_ADMSbsim470sic.C:89:25: error: 'd_nf_d_nfr' was not declared in this scope
 d_get_nuintd_d_nfr=(1.0*d_nf_d_nfr);
                         ^~~~~~~~~~
N_DEV_ADMSbsim470sic.C:89:25: note: suggested alternative: 'd_nfr'
 d_get_nuintd_d_nfr=(1.0*d_nf_d_nfr);
                         ^~~~~~~~~~
                         d_nfr
N_DEV_ADMSbsim470sic.C: In member function 'Xyce::Device::ADMSbsim470sic::AnalogFunctions::get_nuintdEvaluator::returnType Xyce::Device::ADMSbsim470sic::AnalogFunctions::get_nuintdEvaluator::evaluator_(double)':
N_DEV_ADMSbsim470sic.C:135:25: error: 'd_nf_d_nfr' was not declared in this scope
 d_get_nuintd_d_nfr=(1.0*d_nf_d_nfr);
                         ^~~~~~~~~~

Any case I try to get around it by creating a custom analog modulo function that accepts real numbers, and then replacing '%'.

xyce-users

unread,
Dec 3, 2022, 5:59:12 PM12/3/22
to xyce-users
Ugh.  Yes, this is annoying.  Xyce/ADMS is generating code that expects certain variables to be declared, holding derivative information, but in this case not generating code that declares them and sets them to something.

There are serious problems in Xyce/ADMS regarding use of integer variables and I forgot about those.  The problem here is that we're expecting derivative computations, and none are performed on integer variables.  This is a bug --- we should not be expecting them when the variable is an integer.  Here, we're detecting that "nf" depends on "nfr" which is used downstream to expect that the derivative of "nf" with respect to "nfr" (d_nf_d_nfr) exists, but we don't actually ever create that.  It's a hole in the automatic differentiation implementation. 

It's puzzling, though, that assigning "nf=1.3" still gives an error, since there should be no differentiation problem there.

For now, your best bet is to work around somehow to avoid taking the modulus of a real number using the modulus operator.  The Verilog-A standard says that a%b when either a or b are real should be performed as if it were
     ((a/b) < 0) ? (a - ceil(a/b)*b) : (a - floor(a/b)*b)
This is what Xyce/ADMS *should* be doing when it encounters such a thing, but it is easier said than done --- one would have to evaluate the left and right arguments of the modulus operator and figure out whether or not they evaluate to a real or integer expression.

So to get past this you could try using a real "nf" as the function input argument and replacing "nf % 2" with "(nf/2)<0 ? (nf-ceil(nf/2)*2):(nf-floor(nf/2)*2)" and see if that does any better.

I have filed an issue in our internal issue tracker regarding the mishandling of analog functions with arguments that don't have the same type as the return value (this may be trivial to fix, because almost all the pieces needed to make it work are already there and being used in other ways), but the issue of handling derivatives of  real variables that depend on integer variables properly was filed a long time ago and there has been almost no internal driver (read that as "funding") to make it bubble up to the top of the priorities list to happen in finite time.  There is also a long-standing issue in the tracker regarding the mishandling of modulus operators, and it, too, has languished at the bottom of the priority heap.

xyce-users

unread,
Dec 3, 2022, 6:18:03 PM12/3/22
to xyce-users
Oh, and for best results, you might want to avoid issues of integer/double upconversion in C++ with possibly attendant incorrect computation or problems with finding matching function prototypes by writing the substitute for "nf%2" with explicitly real constants:

     ((nf/2.0)<0) ? (nf-ceil(nf/2.0)*2.0):(nf-floor(nf/2.0)*2.0)

So:

   analog function real get_nuintd;
      //                                                                        
      input nf;
      real nf;

      input   minsd;
      real    minsd;
      begin

         if ((((nf/2.0)<0) ? (nf-ceil(nf/2.0)*2.0):(nf-floor(nf/2.0)*2.0))  != 0)
           //odd                                                                

           get_nuintd = 2.0 * max((nf - 1.0) / 2.0, 0.0);
         else
           begin
              //even                                                            
              if (minsd == 1)
                // minimize # of source                                        
                get_nuintd = 2.0 * max(nf / 2.0 - 1.0, 0.0);
              else
                get_nuintd = nf;
           end
      end
   endfunction

Maybe that will work.    It should, as ceil and floor are both properly implemented in Xyce/ADMS, whereas support for integer variables that even remotely depend on real variables is really sketchy.

Albert Kumar

unread,
Dec 3, 2022, 9:28:59 PM12/3/22
to xyce-users
Thank you. Appreciate your help. I did fix all the callfunction issues as well (it was due to $strobe).

ceil and floor don't work for me (Xyce-7.5). Would these be available in Xyce-7.6? I don't see them mentioned in the release notes.

[fatal..] Unable to handle function floor of class builtin at this time.  Bye.[fatal..] see [/opt/Xyce-7.5/Serial/share/xml/xyceAnalogFunction_nosac.xml:808]

Thomas Russo

unread,
Dec 3, 2022, 9:41:45 PM12/3/22
to xyce-users
Ding it.  Looks like ceil and floor didn't get implemented in the analog function code.  They work fine outside of analog functions.  No, it won't be in 7.6, either.

Will have to see if I can slam them in so that they can be in 7.7 or by users of github source.  The problem is in one spot I've already identified, and it should be easy to fix.

xyce-users

unread,
Dec 3, 2022, 9:52:01 PM12/3/22
to xyce-users
It is indeed simple to fix this "ceil and floor break in analog functions", and it was just an oversight.

The attached patch, applied to xyceAnalogFunction_nosac.xml should take care of it.  If it fixes things for you then I'll be sure to make this change in the official source so that it should be in the next github push if I get that done.

The other things maybe not so much.  But I think I can at least get the thing not to crash when an analog function uses a callfunction, but what I'll probably do is just make it ignore that call function, as if you'd commented out the line. 
afdiff.patch

xyce-users

unread,
Dec 3, 2022, 9:57:46 PM12/3/22
to xyce-users
Yeah, fixing the "crash on use of callfunctions in analog functions" was easy, too.

This patch makes Xyce/ADMS ignore callfunctions when they're used in analog functions.  It'll throw a warning when processing that it is ignoring the callfunction, but that's better than bombing.

Apply this patch after applying the other, and both the $strobe and ceil/floor issues should just work. 

Can't say that the *rest* of the model will work, but this should get you past the issues we identified so far.
afdiff2.patch

Albert Kumar

unread,
Dec 4, 2022, 2:41:16 AM12/4/22
to xyce-users
Wow, thank you so much! I applied the patches and checked that ceil and floor work. The $strobe seems to still flag as an error, but let me double check it tmrw. And this is no big deal for me to fix manually. Thank you again.

xyce-users

unread,
Dec 4, 2022, 11:47:16 AM12/4/22
to xyce-users
Yeah, that's because the patch set was incomplete.  I was not actually testing this by compiling all the way down to a plugin, but by looking at the generated C++ code and fixing what I spotted as the problem.  There was a second spot I needed to fix, and that's fixed by this attached patch.  With this patch, I was able to compile a toy model with your analog function (including a $strobe) all the way to a plugin, so now I know I've got these two bits nailed.
afdiff3.patch

xyce-users

unread,
Dec 5, 2022, 11:08:53 AM12/5/22
to xyce-users
While I was *sure* I had gotten this working over the weekend, when I went to get it into the main Xyce source tree and get it tested, I found another issue.    I can't for the life of me figure out how I convinced myself that it worked before, but there you go.  This is what comes from trying to dash off quick fixes over the weekend instead of waiting to get back to the office and doing it carefully.

I do have it fixed for real and being tested properly.  But rather than post the patch (which is much larger than before), let's just leave it as "comment out your $strobe in analog functions" for now, and when Xyce 7.7 comes out (or you pull from GitHub after the next batch of code gets published in 3 weeks or so) the "ignore callfunctions like $strobe" thing will be there and completely debugged.

Albert Kumar

unread,
Dec 7, 2022, 2:10:49 PM12/7/22
to xyce-users
I'll try the patch shortly, but first I'm stuck on some "scope" issues.  I see many, many cases like below in the .va file I received.

The parameter "coxe" is defined by variables in the prior begin/end section and are "not declared in this scope".

This type of code has to be fixed, correct? For example, getting rid of the begin/end prior to the "coxe" definition.

    `define EPSSI 1.03594e-010
    parameter real toxe = 50e-9;
    parameter real epsrox = 1.0;
    real param_epsrox, param_toxe, epssub;
   
    analog begin
    ...
        @(initial_step)
        begin
            real param_epsrox, param_toxe, epssub, toxe;
            param_epsrox = epsrox;
            param_toxe = toxe;
            epssub = `EPSSI;
        end
        real coxe;   
        coxe = param_epsrox * `P_EPS0 / param_toxe;
   ... 
   end

xyce-users

unread,
Dec 7, 2022, 2:54:37 PM12/7/22
to xyce-users
Any variable declared inside a begin/end block is scoped local to that block and therefore cannot be accessed outside of that block, the same as it would be if you had done it inside a "{}" pair in C++ (which, of course, is exactly how it is translated into C++ by Xyce/ADMS). 

Also, if you declare a variable inside a block that is already declared *outside* that block, it declares what is basically an overriding temporary variable that only lives inside that block and which exists only  the duration of execution of the block.  You wanna be careful about that.  (again, this is because of C++ scoping rules and how Xyce/ADMS effectively translates Verilog-A begin/end pairs into C++ block-delimiting "{}")

Remove the declaration of param_epsrox, param_toxe, epssub, and toxe from inside the @(initial_step) and declare them outside the block (in the snippet you posted, they were already declared outside the block).  The "@(initial_step) begin /end" thing marks a complete block that should only happen at the initial step, and it will have access already to any variables you've got there.

Xyce/ADMS takes the contents of @(initial_step) and simply creates a small block of code inside its "updateIntermediateVars" function that is conditionalized so that it only gets executed during the DCOP phase (the "initial step" of a transient).  Thus, any variable declared at module scope or inside the "analog begin/end" block will be accessible to it without declaring it again. 

But you have other problems here as far as Xyce/ADMS is concerned.  In Xyce/ADMS all variables defined at module scope that are not also used in the special, highly non-standard, ADMS-specific "@(initial_instance)" or "@(initial_model)" blocks (which are run only when the model is first instantiated or if any model or instance parameters are modified by a .step analysis) are considered local to Xyce's "updateIntermediateVars" function and *do not retain values from step to step*.

Therefore,  trying to initialize param_epsrox, param_toxe, and epssub variables inside the @(initial_step) block only initializes block-local variables as you've written it, but even if you removed the duplicate declarations it would be initializing those function-scoped variables only when the DCOP is being performed.  After the DCOP is done and you've entered transient, this block is no longer getting executed, but the prior values stored in the variables were basically discarded when the function exited last.  Thus, any calculations outside of the DCOP are using uninitialized variables.  This is a bad thing.

In this particular case you should just remove the @(initial_step)begin/end here and set param_epsrox, param_toxe, and epssub directly to assure that they always have the correct values every time the code is executed.  Since nothing about the code you've shown makes it strictly applicable only to the DCOP, there's no reason to put it in such a block.

In this regard, Xyce's handling of variables as local variables to the updateIntermediateVars function is not really in compliance with the Verilog-A standard, which says that variables should retain value across calls.  But carefully assuring that they are always initialized by the module assures that it doesn't matter.

Albert Kumar

unread,
Dec 8, 2022, 2:00:14 PM12/8/22
to xyce-users
Great explanation. I've actually got the model working thanks to all your help and simulation results appear to match expectations. But, I'm not sure if my understanding is correct or if I'm doing things correctly.

In this Verilog-A model, at the module level, parameters are defined and variables are declared. Then, there is an analog/begin end block. Inside this block there are a series of @(initial_step) blocks, which are finally followed by some additional lines of code. Many of the variables are defined inside the @(initial_step) blocks, but are not used in the final lines of code.

A simplified model is shown below.

When I simulate, it appears that variables defined and/or modified inside the @(initial_step) blocks are preserved across all simulation steps, i.e. the strobe outputs the values of epssub 1, 2 sequentially for DC sims and just 2 for the transient sim after the DCOP.

Couple questions:
1) Can you confirm that values of variables defined/modified inside the @(initial_step) are preserved for variables downstream and outside the @(initial_step) blocks?
2) The variable "epssub" must be used after the @(initial_step) blocks. Otherwise, the error: 'epssub' was not declared in this scope' results. Is this expected?
3) To get the original full verilog model working, I added lines just like line 27 (epssub=epsub) for all the variables declared at module level (~1000 variables) after the @(intial_step) blocks. Do you see any issue with this? Another option is to only add these lines for variables used inside @(initial_step) blocks, but not in the final section outside the @(initial_step). This will require some effort and I'm not sure it will work.

 1 `include "disciplines.vams"
 2 `include "constants.vams"
 3
 4 `define EPSSI 1.0;
 5
 6 module test_me(p, n);
 7     inout p, n;
 8     electrical p, n;
 9     parameter real var1 = 7.3; //Xyce requires this for simulation?
10    
11     real epssub;
12
13     analog
14     begin
15         @(initial_step)
16         begin
17             epssub = `EPSSI;
18             $strobe ("1st epssub", epssub);
19         end
20        
21         @(initial_step)
22         begin
23              epssub = 2.0 * `EPSSI;
24              $strobe("2nd epssub", epssub);
25         end
26        
27         epssub = epssub; // This line is required if lines 28 and 29 are missing.
28         $strobe ("Outer epssub", epssub);
29         I(p, n) <+ epssub * V(p, n);
30      
31     end
32 endmodule

With lines 28 -29 commented out:
N_DEV_ADMStest_me.C:569:1: error: 'epssub' was not declared in this scope
 epssub = 1.0;

N_DEV_ADMStest_me.C:575:1: error: 'epssub' was not declared in this scope
 epssub = (2.0*1.0);

N_DEV_ADMStest_me.C:935:1: error: 'epssub' was not declared in this scope
 epssub = (2.0*1.0);

xyce-users

unread,
Dec 8, 2022, 2:44:45 PM12/8/22
to xyce-users
For starters, recall that @(initial_step) means (to Xyce/ADMS at least) a block of code that should only be executed during the DCOP, and will never be executed in transient.  It is therefore *wholly* inappropriate to use this block to do things like bias independent initializations or copying model/instance parameters into local variables.  So what you're doing here is actually dangerous and may result in transient simulations trying to use uninitialized variables (because you're only initializing them when it's a DCOP, and I assure you the values are NOT guaranteed to be preserved between calls as they are all local variables to updateIntermediateVars and are thrown away every time this function returns).

Other simulators and Verilog-A compilers may do the right thing here, but this is not right in Xyce/ADMS.

If you want to isolate initializations of variables that should only take place once, Xyce/ADMS wants you to put those into "@(initial_instance)" or "@(initial_model)" blocks (the former is performed once for each instance of the device in a circuit, the latter for each model card).  To do this in a good, portable way, you could do this:

    module foobar(a,b);
    inout a,b;
    electrical a,b;
    parameter real model_parameter = 1 from (0:inf);
    parameter real instance_parameter = 2 from (-inf:inf) (*type='instance' *);

    real module_scoped_variable1;
    real module_scoped_variable2;
    analog begin
      `ifdef  insideADMS
       @(initial_model)
       `else
       @(initial_step)
       `endif
       begin
               module_scoped_variable1 = model_parameter;
       end
       `ifdef insideADMS
       @(initial_instance)
       `else
       @(initial_step)
       `endif
        begin
            module_scoped_variable2 = instance_parameter;
        end

That'll take care of initializing the two module scoped variables, and Xyce/ADMS will assure that they are elevated to a level where they retain values across calls (they are added to the "instance" or "model" classes precisely because they appear in these @(initial_instance) and @(initial_model) blocks, which are placed into functions called "processParams" in the Xyce Instance and Model classes, respectively).

By putting this ADMS-specific stuff into an ifdef like this, you make the module something that can be processed in Xyce/ADMS *and* whatever other simulator was already happy with it unmodified.  "insideADMS" is defined by the ADMS Verilog-A processor itself, so this makes sure only ADMS ever sees these nonstandard uses.

You really, really must not put this "initialize once" stuff into @(initial_step) in Xyce, because it'll be wrong, will only initialize the variables during DCOP, and will be forgotten when DCOP is over.  Trust me on that one.


Now, as for the issue where a variable is declared outside an @(initial_step) block but which is never used except *inside the block*, this appears to be a bug in Xyce/ADMS.  Details (probably too many of them) follow.

Just because a variable is declared in the Verilog-A doesn't mean that Xyce/ADMS actually emits the declaration --- it only emits a declaration for variables that are *assigned to*.  But it ignores assignments inside @(initial_step) for reasons I cannot recall.  I might be able to fix that, but I because I can't recall *why* it's doing that I can't be sure that a quick fix will be the right fix.  If you look at Xyce/utils/xyceImplementationFile_nosac.xml, you'll see these lines:

  <!-- Now let's collect into analog/code/ @assignedVars only those          -->
  <!-- variables that are assigned to in code EXCLUDING the special blocks  -->
  <admst:for-each select="analog/code/item">
    <!-- if a block and not special -->
    <admst:if test="adms[datatypename='block']/..[name!='initial_instance' and name!='initial_model' and name != 'initial_step']">
      <admst:apply-templates select="." match="collectAssignedVariables"/>
    </admst:if>
    <!-- if not a block -->
    <admst:if test="adms[datatypename!='block']">
      <admst:apply-templates select="." match="collectAssignedVariables"/>
    </admst:if>
  </admst:for-each>

This is where we collect up all the variables in any block that are assigned to in that block and are not in the scope of some higher block.  You see it's ignoring @(initial_instance), @(initial_model), and @(initial_step) here.  It is *correct* for it to be doing that for @(initial_model) and @(initial_instance)  (which are not part of the Verilog-A standard and are recognized only by ADMS, and denote bias-independent calculations at the instance or model level that Xyce/ADMS will put into a different function than all the other stuff), but I believe it is (and always has been) wrong for it to be treating @(initial_step) that way.   Once it has collected up variables that are assigned to, it uses this to determine which variables should be declared at which scope in the emitted C++.

@(initial_step) code isn't really as special as those other (ADMS-specific) events --- what it causes Xyce/ADMS to emit this sort of C++:

  if (getSolverState().dcopFlag)
  {
      ... the contents of the initial_step block...
  }

right into the same "updateIntermediateVars" function that almost all of the rest of the code goes into (excepting anything in @(initial_model) or @(initial_instance)).  So in fact, all the variables assigned to there and NOT declared there should be considered in the same scope as the main body of the module, but at the moment are NOT considered "assigned to" by this block because of this bug.

But when you also assign to the same variable *outside* the block the "collectAssignedVariables" thing picks it up and makes sure it is declared.

Now, if it were not completely wrong to be using initial_step here (as far as Xyce/ADMS is concerned, despite what good, modern, standard-compliant Verilog-A compilers do), I'd say "simple, just remove the "and name!='initial_step'" bit of that line in xyceImplementationFile_nosac.xml" and we'd be done.  but if you did that you'd just be inviting all the problems I just enumerated in the first part of this very long note.

Inserting thousands of lines of code that simply assign to variables just to work around this bug is not the thing you really want to do here.  If you switch to using @(initial_instance) and @(initial_model) as appropriate with those ifdefs I described, you'll have better results.  Fixing this little bug *might* be possible before I retire (which is happening in a few weeks), but even if I got it fixed using @(initial_step) in this way is *still* the wrong thing to be doing and it'd just be pushing off the problems to some other place.

Finally, I'm not sure what you're asking by this line:


                parameter real var1 = 7.3; //Xyce requires this for simulation?


Perhaps you're referring to this little nit buried in the Xyce/ADMS users' guide (https://xyce.sandia.gov/documentation-tutorials/xyce-adms-users-guide/)
---

Unless module attributes are added to the Verilog-A input, devices added in this manner will all be "Y" devices, and therefore must be accessed from a netlist using the Y syntax:

   Y<module name> <unique instance name>  <node>* <model name> <instance parameter list>

Module attributes may be added to the Verilog-A input to plug the device into Xyce as a new MOSFET (M), BJT (Q), JFET (J), Resistor (R), Capacitor (C), or Diode (D) model levels instead of as Y devices.

If the model has model parameters, then a model name is required, and the model card will be of the same type as the module name:

   .model <unique model name> <module name>  <model parameter list>
----


Here , while it's explicitly stated that "if the model has model parameters then a model name is required" it is NOT explicitly stated that if the model has NO model parameters then a model name must be OMITTED:

    Y<module name> <unique instance name> <node>* <instance parameter list>

(I.e. no model name specified) This applies when the model has no parameters at all, or when all the parameters have the (* type="instance" *) attribute (and therefore all parameters are instance parameters and the model still has no model parameters).

If you were specifying a model name in your instance line and Xyce was rejecting it when your module didn't have any parameters at all, then this is why.

I could have sworn that the Xyce/ADMS users guide had that little extra detail added at one point, but it appears to be the case either that I was imagining things or that it got accidentally deleted when our site got migrated from one content management system to another (there was a problem with that, the IT folks who did the migration migrated old data).




On Thursday, December 8, 2022 at 12:00:14 PM UTC-7 Albert Kumar wrote:
Great explanation. I've actually got the model working thanks to all your help and simulation results appear to match expectations. But, I'm not sure if my understanding is correct or if I'm doing things correctly

 [quoted message elided to reduce size]

Neal Wood

unread,
Dec 22, 2022, 2:15:48 PM12/22/22
to xyce-users
All the best for your retirement, Tom. Thank you for all of your contributions to Xyce.
Reply all
Reply to author
Forward
0 new messages