• 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. Mixed-Signal Design
  3. VerilogA blind to DC sweep analysis

Stats

  • Locked Locked
  • Replies 11
  • Subscribers 65
  • Views 8177
  • 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

VerilogA blind to DC sweep analysis

NewScreenName
NewScreenName over 1 year ago

I am trying to use a verilogA model of a continuous time comparator, so I could see the output comparison even for DC analysis. However while it works fine for basic DC, when I enable a sweep variable, even when plotting the output of the comparator as a DC sweep VS("/outp"), in facts for each point of the sweep just the DC output (without sweeping) is shown, as if it was displaying VDC("/outp") rather than VS("/outp").

Just to double check that the issue is not in my setup I tried to replace the comparator verilogA model with a vcvs from abnalogLib, and that works properly for the DC sweep, therefore I guess there is some limitation in verilogA from supporting DC sweep, or should I just write down differently the VerilogA code (which you can find below)?

module ct_comp(inp, inm, outp, outm, vdd, vss);
input inp, inm;
output outp, outm;
inout vdd, vss;
electrical inp, inm, outp, outm, vdd, vss;

real Vop, Vom;

analog begin

@ ( initial_step ) begin

Vop = V(inp) > V(inm) ? V(vdd) : V(vss);
Vom = V(inp) > V(inm) ? V(vss) : V(vdd);

end

@ (cross( V(inp) - V(inm))) begin
if(V(inp) > V(inm)) begin
Vop = V(vdd);
Vom = V(vss);
end
else begin
Vop = V(vss);
Vom = V(vdd);
end
end

V(outp) <+ transition(Vop, 10p, 10p, 10p);
V(outm) <+ transition(Vom, 10p, 10p, 10p);
end
endmodule

  • Cancel
  • Andrew Beckett
    Andrew Beckett over 1 year ago

    Use @(above()) rather than @(cross()) - this works even with DC, whereas @(cross) requires at least one timestep. A fairly small rewrite of your model - this works (I think):

    `include "disciplines.vams"
    
    module ct_comp(inp, inm, outp, outm, vdd, vss);
    input inp, inm;
    output outp, outm;
    inout vdd, vss;
    electrical inp, inm, outp, outm, vdd, vss;
    
    real Vop, Vom;
    
    analog begin
    
      @ (above( V(inp) - V(inm)));
      @ (cross( V(inp) - V(inm)));
    
      if(V(inp) > V(inm)) begin
        Vop = V(vdd);
        Vom = V(vss);
      end
      else begin
        Vop = V(vss);
        Vom = V(vdd);
      end
    
      V(outp) <+ transition(Vop, 10p, 10p, 10p);
      V(outm) <+ transition(Vom, 10p, 10p, 10p);
    end
    endmodule
    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
  • NewScreenName
    NewScreenName over 1 year ago in reply to Andrew Beckett

    I can confirm it works, however I am surprised, shouldn't the IF statements be contained within the above and cross statements to be executed when the related event triggers?

    I tried further to use this approach in a verilogA block which should convert a binary input to its decimal representation. Below is the code, where I added the above condition on update signal on both directions to ensure regardless its state the code will be evaluated at any DC analysis, but in this case it does not seem to work:

    `include "constants.vams"
    `include "disciplines.vams"

    module bin2dec_conv(vss, vdd, vref, update, in, out);

    inout vss, vdd;
    input vref, update;
    input [19:0] in;
    output out;

    electrical vss, vdd;
    electrical vref, update;
    electrical [19:0] in;
    electrical out;

    parameter integer Nbits_used = 10;
    parameter integer is_signed = 1;

    real vtrans, vref_r;
    real out_r = 0;
    real weight[19:0];
    genvar i;

    analog begin

    vth = V(vdd,vss)/2;
    vref_r = V(vref);

    @(above(V(update)-vth) or above(-V(update)+vth));

    if(!is_signed) begin

    @(initial_step or above(V(update)-vth) or above(-V(update)+vth)) begin

    out_r=0;
    for(i=Nbits_used-1; i>=0; i=i-1) begin

    weight[i] = V(in[i])>vth? 1.0/2.0**(Nbits_used-i) : 0.0;
    out_r = out_r+weight[i]*vref_r;

    end

    end

    end else begin

    @(initial_step or above(V(update)-vth) or above(-V(update)+vth)) begin

    out_r=0;
    for(i=Nbits_used-1; i>=0; i=i-1) begin

    if(i==Nbits_used) begin

    weight[i] = V(in[i])>vth ? -1.0/2.0**(Nbits_used-i) : 0.0;

    end else begin

    weight[i] = V(in[i])>vth? 1.0/2.0**(Nbits_used-i) : 0.0;

    end
    out_r = out_r+weight[i]*vref_r;

    end

    end

    end

    V(out)<+transition(out_r, 0, 10p, 10p);

    end

    endmodule

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 1 year ago in reply to NewScreenName
    NewScreenName said:
    I can confirm it works, however I am surprised, shouldn't the IF statements be contained within the above and cross statements to be executed when the related event triggers?

    In the case of the comparator, it's fine. That's because the @cross/@above without any body simply ensures there is a timepoint close to the threshold crossing; the output is determined directly by the state of the input at all times, so the if can be evaluated at every time step.

    With your bin2dec Verilog-A code above, the update pin seems to be used as a clock to determine when the input should be sampled - so it doesn't make sense to use the same approach. I'm not sure what your bold line is supposed to be doing (it has a different threshold than the other above statements).

    I'd have to put together an example netlist to try this out and debug it - no time for that today, sorry (will be a few days most likely before I have some spare time to experiment). The lack of indentation makes it hard to reason about too, but I can fix that when I test it.

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • NewScreenName
    NewScreenName over 1 year ago in reply to Andrew Beckett
    Andrew Beckett said:
    With your bin2dec Verilog-A code above, the update pin seems to be used as a clock to determine when the input should be sampled - so it doesn't make sense to use the same approach. I'm not sure what your bold line is supposed to be doing (it has a different threshold than the other above statements).

    Sorry the different threshold was a typo leftover from a modification, I have now fixed it, it is consistently the same threshold and the  goal of it was to force a time point in DC analysis as you suggested for the comparator case. The above statement, in my intent, should always trigger as it checks for V(update) to be above or below vth, one of these two conditions will always be satisfied in DC.

    I have also put some indentation, however how can I get automatic indentation for pieces of code in this forum? For this I had to manually indent what I copied from my verilogA model, as the copy&paste doesn't seem to retain indentation from the original code. The format->formats->inline->code formatting option just gives a different font, rather than taking care of identation.

    Andrew Beckett said:
    will be a few days most likely before I have some spare time to experiment

    Sure, even later in time I would greatly appreciate your help here

    Best regards

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

    Dear NewScreenName,

    NewScreenName said:
    I have also put some indentation, however how can I get automatic indentation for pieces of code in this forum? For this I had to manually indent what I copied from my verilogA model, as the copy&paste doesn't seem to retain indentation from the original code. The format->formats->inline->code formatting option just gives a different font, rather than taking care of identation.

    Please allow me provide a comment or two (or try to!) to address the easy part of your question quoted above. The degree to which the Forum response dialog box copy and paste functionality preserves tabs or spaces is browser dependent. If I use the Firefox browser, copy and pasting text between a document containing tabs or spaces is preserved when past into the browser Forum reply post window. If I use the Safari browser, the pasting operation does not preserve any tabs or multiple spaces:

    However, if you save the content with multiple spaces or tabs into a text files and then upload the text file, the tabs or multiple spaces should be preserved. In this case, I am writing into the Safari browser and uploaded the text file contaning the example code snippet shown above.

    Shawn

    Fullscreen function.txt Download
    
    function(x,y)
    {
    if (x > y)
    	printf("X is greater than Y\n");
    else
    	printf("X is not greater than y...sorry!\n");
    }

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

    I'm not sure what's supposed to be actually wrong with the code. It works as far as I can see (other than a couple of small mistakes I corrected, in red below):

    `include "constants.vams"
    `include "disciplines.vams"
    
    module bin2dec_conv(vss, vdd, vref, update, in, out);
    
    inout vss, vdd;
    input vref, update;
    input [19:0] in;
    output out;
    
    electrical vss, vdd;
    electrical vref, update;
    electrical [19:0] in;
    electrical out;
    
    parameter integer Nbits_used = 20;
    parameter integer is_signed = 1;
    
    real vth, vref_r;
    real out_r = 0;
    real weight[19:0];
    genvar i;
    
    analog begin
      vth = V(vdd,vss)/2;
      vref_r = V(vref);
    
    //  @(above(V(update)-vth) or above(-V(update)+vth));
    
      if(!is_signed) begin
        @(initial_step or above(V(update)-vth) or above(-V(update)+vth)) begin
          out_r=0;
          for(i=Nbits_used-1; i>=0; i=i-1) begin
            weight[i] = V(in[i])>vth? 1.0/2.0**(Nbits_used-i) : 0.0;
            out_r = out_r+weight[i]*vref_r;
          end
        end
      end else begin
        @(initial_step or above(V(update)-vth) or above(-V(update)+vth)) begin
          out_r=0;
          for(i=Nbits_used-1; i>=0; i=i-1) begin
            if(i==Nbits_used) begin
              weight[i] = V(in[i])>vth ? -1.0/2.0**(Nbits_used-i) : 0.0;
            end else begin
              weight[i] = V(in[i])>vth? 1.0/2.0**(Nbits_used-i) : 0.0;
            end
            out_r = out_r+weight[i]*vref_r;
          end
        end
      end
      V(out)<+transition(out_r, 0, 10p, 10p);
    
    end
    
    endmodule

    The fixes were:

    1. The value of Nbits_used was 10 (so it only took notice of the least significant 10 bits out of the 20 bit input). I could have changed this on the instance line, but changed it in the code for simplicity
    2. The variable here was called vtrans still rather than vth (you'd changed it to vth everywhere else)
    3. This wasn't really a mistake - just that the event statement without a body wasn't doing anything, so can be commented out

    BTW, I also realised that in my original response, the @above line is unnecessary - the event isn't needed in the dc analysis (since the conditional statement is executed on every step), and in the transient you are already having a cross event on every transition, so the above was redundant.

    Anyway, back to this case. I tested it with a vector file:

    radix 44444
    io i
    vname BUS[[19:0]]
    tunit ns
    trise 0.1
    tfall 0.1
    vih 1.2
    vol 0.0
    0 12345
    100 FABB2
    200 98765
    300 DEAD1

    and this netlist:

    //
    
    #ifndef DCSWEEP
    vec_include "forumtest.vec"
    #else
    //vb19 (BUS\[19\] 0) vsource dc=1.2
    // set the 19th bit to be higher than the clk so that
    // it crosses the threshold before the clock does
    vb19 (BUS\[19\] 0 clk 0) vcvs gain=1.5
    vb18 (BUS\[18\] 0) vsource dc=1.2
    vb17 (BUS\[17\] 0) vsource dc=0
    vb16 (BUS\[16\] 0) vsource dc=1.2
    vb15 (BUS\[15\] 0) vsource dc=0
    vb14 (BUS\[14\] 0) vsource dc=0
    vb13 (BUS\[13\] 0) vsource dc=1.2
    vb12 (BUS\[12\] 0) vsource dc=0
    vb11 (BUS\[11\] 0) vsource dc=0
    vb10 (BUS\[10\] 0) vsource dc=1.2
    vb9 (BUS\[9\] 0) vsource dc=1.2
    vb8 (BUS\[8\] 0) vsource dc=1.2
    vb7 (BUS\[7\] 0) vsource dc=0
    vb6 (BUS\[6\] 0) vsource dc=1.2
    vb5 (BUS\[5\] 0) vsource dc=0
    vb4 (BUS\[4\] 0) vsource dc=0
    vb3 (BUS\[3\] 0) vsource dc=1.2
    vb2 (BUS\[2\] 0) vsource dc=0
    vb1 (BUS\[1\] 0) vsource dc=0
    vb0 (BUS\[0\] 0) vsource dc=1.2
    #endif
    
    vclk (clk 0) vsource type=pulse val0=0 val1=1.2 rise=0.2n delay=5n width=50n period=100n
    vdd (vdd 0) vsource dc=1.2
    vref (ref 0) vsource dc=1.0
    
    b2dec (0 vdd ref clk BUS\[19\] BUS\[18\] BUS\[17\] BUS\[16\] BUS\[15\] BUS\[14\] BUS\[13\] \
    BUS\[12\] BUS\[11\] BUS\[10\] BUS\[9\] BUS\[8\] BUS\[7\] BUS\[6\] BUS\[5\] BUS\[4\] BUS\[3\] BUS\[2\] \
    BUS\[1\] BUS\[0\] out) bin2dec_conv
    
    ahdl_include "bin2dec_conv.va"
    tran tran stop=400n
    #ifdef DCSWEEP
    dc dc dev=vclk param=dc start=0 stop=1.2 step=0.1
    #endif

    If you run with:

    spectre -E testcase.scs

    then the output will be due to the changes in the vector file - as you can see, it works fine in transient:

    If you run with:

    spectre -DDCSWEEP testcase.scs

    then it instead uses a fixed set of voltages for each bit, except for BUS[19] which is set to be a scaled version of the clk signal. The clk voltage source is then swept in the DC sweep - and the reason for the scaling of BUS[19] is to ensure it crosses the threshold before the clock does, otherwise you wouldn't see any change in the DC sweep. Because of that, you get this:

    So my question is, what doesn't work (in your eyes) in this model?

    To answer the other question (and to augment Shawn's response), I do actually use Safari (I use a Mac) and the mechanism I use is to put anything I want to keep properly indented (such as the code above) into a text file (I call it "solution.txt") and then open that in a tab in the browser (I actually have a bookmark for the fixed file I use) and then I copy and paste from that Safari browser tab into the post. 

    Andrew

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

    Regarding the transient analysis, yes, that works as I expected.

    It is the DC sweep which does not, or at least now I get an idea why from your answe. In the DC sweep you are clocking the block when the net BUS[19] is going to have a transition, or actually just after BUS[19] has crossed its threshold (but this is a transition in sweep, not in time!). So I assume it would be necessary to give a sort of "clock" for the DC sweep as well when I want the DC step to change my output? 

    Is my understanding correct?

    I thought that the update signal would only be relevant in a transient simulation, while my intent is to have the DC sweep to calculate for each different DC point what the output should be automatically (that was what I hoped the @initial_step would actually do: triggering the block in DC, but probably that works only on the first step of the DC). This way I must get the update signal to cross the threshold in between each DC sweep point, and it looks a bit cumbersome when you have several sweep points and you don't even know at which step which bus net is going to change. Or maybe I am blind and missing an easy way to achieve this?

    EDIT: what I noticed is that as soon as I put the DC sweep variable "myVar" in one of the parameters of the vpulse driving the "update" pin of the block, actually the DC sweep works fine. This is even if that variable in on a parameter which has nothing to do with the dc analysis. Actually I tried to give the vpulse an AC amplitude of myVar (no ac analysis is defined) and then the dc sweep gives the expected output. 

    Why is this then? The "update" pin is not really crossing the threshold in this case.

    Also for this to work I must keep the @(initial_step or above(V(update)-vth) or above(-V(update)+vth)) which means in a transient simulation the update signal will clock the block on both edges and it is not really what was intended. Also it is not clear to me why if I left just @(initial_step or above(V(update)-vth)) the output of the block in the DC sweep never changes?

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

    I'd already worked on this before I saw your edit, so here's a proposed change which I think does what you want - a DC sweep will effectively run the code within the events at each DC sweep point, and transient will be triggered only on rising edges of update (I highlighted the changes):

    `include "constants.vams"
    `include "disciplines.vams"
    
    module bin2dec_conv(vss, vdd, vref, update, in, out);
    
    inout vss, vdd;
    input vref, update;
    input [19:0] in;
    output out;
    
    electrical vss, vdd;
    electrical vref, update;
    electrical [19:0] in;
    electrical out;
    
    parameter integer Nbits_used = 20;
    parameter integer is_signed = 1;
    
    real vth, vref_r;
    real out_r = 0;
    real weight[19:0];
    integer toggle=-1;
    genvar i;
    
    
    analog begin
      vth = V(vdd,vss)/2;
      vref_r = V(vref);
    
      // create an artificial event when in a dc analysis.
      if (analysis("dc")) toggle=1-toggle;
    
      if(!is_signed) begin
        @(initial_step or above(V(update)-vth) or
            above(toggle) or above(-toggle)) begin
          out_r=0;
          for(i=Nbits_used-1; i>=0; i=i-1) begin
            weight[i] = V(in[i])>vth? 1.0/2.0**(Nbits_used-i) : 0.0;
            out_r = out_r+weight[i]*vref_r;
          end
        end
      end else begin
        @(initial_step or above(V(update)-vth) or 
            above(toggle) or above(-toggle)) begin
          out_r=0;
          for(i=Nbits_used-1; i>=0; i=i-1) begin
            if(i==Nbits_used) begin
              weight[i] = V(in[i])>vth ? -1.0/2.0**(Nbits_used-i) : 0.0;
            end else begin
              weight[i] = V(in[i])>vth? 1.0/2.0**(Nbits_used-i) : 0.0;
            end
            out_r = out_r+weight[i]*vref_r;
          end
        end
      end
      V(out)<+transition(out_r, 0, 10p, 10p);
    
    end
    
    endmodule

    It does this by introducing an artificial event (toggle) which is toggled at each DC point. The alternative was to replicate all the logic computing the out value within the condition for analysis("dc") but that would involve more copy and pasting...

    Not sure about your point about sweeping of myVar on an unrelated parameter on the vsource driving update. I'll have to try that (later today, probably).

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett over 1 year ago in reply to Andrew Beckett
    NewScreenName said:

    EDIT: what I noticed is that as soon as I put the DC sweep variable "myVar" in one of the parameters of the vpulse driving the "update" pin of the block, actually the DC sweep works fine. This is even if that variable in on a parameter which has nothing to do with the dc analysis. Actually I tried to give the vpulse an AC amplitude of myVar (no ac analysis is defined) and then the dc sweep gives the expected output. 

    Why is this then? The "update" pin is not really crossing the threshold in this case.

    I just checked, and I see this behaviour. I think it's a side-effect, and I wouldn't rely on it. I suspect what is happening is that any change to any parameter on the vsource triggers it to re-evaluate the vsource during the DC, and that causes the above to fire - even though in reality it didn't actually change value. It's probably a cautious approach to avoid accidentally missing things, or it might be a mistake - but either way I don't think it''s the right approach to rely on that. However, hopefully with my artificial toggle approach above, you have a better solution now!

    Andrew

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

    The solution proposed by you works, thanks a lot!

    Then just for my understanding, is it correct that the DC sweep points should be considered as steps as well as time points are steps in a transient simulation, such that for a verilog block to "re-trigger", it is necessary to have a sort of "clocking" signal from one sweep point to the other? In your example you did change the vclk voltage (which if I understood correctly is the dc voltage of the generator connected to the "update" pin).

    While I had assumed initial_step would be re-computed at the beginning of each DC sweep point (assumed it was considered a time step), which is obviously wrong.

    • 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