B sources (common examples)

From LTwiki-Wiki for LTspice

For a listing of all b-source functions see B sources (complete reference).

When, why and how to use tripdt and tripdv with explanation

Help states, "If the voltage across a source changes by more than tripdv volts in tripdt seconds, that simulation time step is rejected."

This is rather terse, but it seems to mean, when attempting a solution for the next transient simulation step, in addition to comparing the non linear error of the next step to reltol and other step size acceptance limits, the simulation engine will perform the following test:

IF (the voltage change *across* the b-source > tripdv AND the time change across the simulation step > tripdt) THEN reject the simulation step size, i.e. make it smaller and try again.  (This is quite different than just testing if the magnitude of slope of the voltage across the source gets too large.)

By experimentation, it seems Mike's description is not exactly correct, at least for the last time step while leaving the state change constraint where it seems the allowable tripdt limit is increased by two. Here is a netlist that illustrates LTspice invoking the time step state change constraint.

V1 1 0 SINE(0 1 1k) ; input test source
B1 2 0 V=buf(V(1)) tripdv=0.1 tripdt=10n ; b-source equivalent to a BUF a-device (with state transition time accuracy control)
.opt plotwinsize=0
.tran 5m

The u() function is digital in nature so changing tripdv over a wide range has little effect just so long as it is a bit smaller than the state change of the unit step function. However, tripdt directly controls the maximum allowable time change through the state change except the last of these time changes may be twice the limit (no doubt due to the standard way LTspice increases time steps by doubling).

The problem with the tripdv/tripdt constraint arises when the b-source voltage is analog rather than digital in nature because it is very possible to choose these limits such that normal voltage changes exceed them. That causes an effect much like a very small maximum time step and the simulation may run very slowly.

In order to differentiate the effects on time step size and accuracy that stem from b-source tripdv and tripdt from the normal step size control algorithms, it is probably a good idea to disable or slacken the normal step controls. If maximum step size is not specified, it seems LTspice may select a max step size as a fraction of the end time and perhaps reltol and some other parameters.

For b-source step size control experimentation, I would recommend loosening up reltol and setting the step size to the same as the stop time. Also waveform compression should be disabled.

.opt reltol=10m plotwinsize=0

If you put yourself in the "shoes" of the simulator solver, you can see that the shape of a signal can effect how easy it is to locate activity (pin down state changes or high dv/dt) in a b-source signal. For example, first consider a square wave for which you want the simulator to locate and reproduce the transitions very precisely, yet run as fast as possible between transitions. After an edge event the solver keeps increasing the step size because nothing is changing. At some point the voltage changes and the solver has to reject steps to back up in time in order to locate the latest edge event (otherwise the points across the edge would remain widely spaced and when the plot engine connects the dots, the square wave may end up looking more like a triangle wave).

When you make a square wave with the standard voltage source, the solver knows ahead of time that it is a square wave and where the edges should be in time, but if you make a square wave with a b-source, I don't think it has any way to interpret whatever arbitrary function you have assigned it, so it must rely on tripdv and tripdt to recognize an event. However, a square wave change is easy to see because even if the edge is missed, the level has changed and remains changed for at least several time steps, so the solvers knows an event occurred that it should back up and locate.

Now consider how much more difficult it is for the solver to "see" a narrow pulse type waveform. If the solver doesn't know ahead of time where the edges should be, it could easily blow right by the pulse, never knowing it should have occurred at all. The points on either side of the pulse will be correct so the plot engine will just connect the dots as a flat line. Unlike the case for the square wave, tightening up tripdv and triptdt will not help unless a time point chances to fall within the narrow active region of the pulse (then the edges of that pulse may be sharpened by tightening tripdv and tripdt, but most pulses will still be overlooked).

So what to do if you need to make narrow b-source pulses? First make a rectangular or saw tooth wave and key its edge from whatever will also drive the pulse.

Note that a transient simulation is not continuous with time, but is actually a collection of consecutive solutions to the circuit at a series of discrete time steps.  In order to make the simulation run as quickly as possible, the simulation engine constantly monitors the nonlinear error between adjacent steps and will adjust step size dynamically for a step size that doesn't lead to an unacceptable error.  If the error is too small, the step size is still used, but the *next* time step is made larger.  If the error is too large, the step size is rejected, i.e., the solution results are discarded and another solution is attempted with a smaller step size.

It seems that even if tripdt and tripdv are not specified, LTspice still applies an internal default test to accept or reject a step based on whether or not the behavioral source changes too much in one time step.  Perhaps this internal default function is separate from the tripdv/tripdt test, or perhaps a default value for tripdt is estimated based on something like the the simulation end time.

Replicating a-devices with b-sources

It is a trivial exercise to replicate most digital functions by directly applying the logical operators available to b-sources in LTspice. One need only take the initiative to read their descriptions in the Help file and write the obvious expressions. This applies to buffers, inverters, and-gates, or-gates and xor-gates. With a little thought, one can easily build most more complex digital functions with b-sources as well. However, digital functions that are based on temporal states are more problematic. This class of functions includes flip-flops, edge triggered flip-flops, sample-and-hold devices and the like.

For example, b-sources do not directly provide a sample-and-hold function, yet a very efficient implementation is possible with a roundabout application of the Verilog integration function, sdt(x,ic,r), which provides integration of a variable with optional initial condition and optional reset to that initial condition (assert).

With sdt(0,ic,r), when the integrand is set to zero, the initial condition (which may vary during the simulation) will be continuously sampled as long as the reset is high and then held as long as reset is low, thus producing an ideal sample-and-hold with zero acquisition time.

Building an edge triggered digital device also requires a similar indirect application of the Verilog differentiation function, ddt(x). To detect the positive going edge of an input clock pulse stream, it must first be squared up (ideally buffered to a logical one or zero), then differentiated and buffered again to produce a pulse only on the positive edge. Here is the SPICE code:

* Edge triggered b-source logic and integrated averaging in LTspice

These following functions are intended to be used in b-sources. The first two replicate edge triggered logic such as is used in the a-device DFLOP clock input. The last function is more complicated as it includes three behavioral integrators to provide a running integrated average of x (starting on the falling edge of sampling pulse s) that is then held until the next rising edge of sampling pulse s. In normal use, x would be an analog input and the sampling pulse s would be a series of very narrow 1 volt positive digital pulse.

.func up(s) buf( ddt(s)) ; generates pulse on rising edge of s
.func dn(s) buf(-ddt(s)) ; generates pulse on falling edge of s

* The following averages x between sample pulses s
.func ave(x,s) sdt(0,sdt(x,0,dn(s))/sdt(1,0,dn(s)),up(s))
B1 0 1 I= ave(V(x),V(s)) Rpar=1 Cpar=1n ; source for x and s not shown

* D Flip-Flop (with Q-not feedback to create divide by two counter)

* Divide by two counter (D flip-flop with Q-not data feedback)
Vclk clk 0 PULSE(0 1 1u 1u 1u 1u 10u) ; input pulse source
Bdflop 0 Q I=sdt(0, inv(V(Q)), buf(ddt(buf(V(clk))))) Rpar=1 Cpar=1nF ; D Flip-Flop
.tran 50u

Note that the b-source has been Nortonized into a one amp current source driving a 1nF capacitor in parallel with a one ohm resistor. This provides a picosecond time constant delay to output changes. This small delay is a convergence aid that is necessary so that the solver is able to resolve state changes of the b-source that depend directly on its own output.

Although this b-source divide-by-two counter is robust and efficient, it is no match in speed to the equivalent LTspice native a-device (either the DFLOP or the even more versatile COUNTER device). LTspice's native a-device will always outperform the best b-source equivalent implementation, so this entire exercise is somewhat of an academic study.

Because they are general purpose devices, b-sources are computationally burdened with carrying a full Jacobian (although this can be turned off - often with disastrous results - with the NoJacob parameter). Also, since b-sources are largely analog devices, they do not have a built-in provision to recognize state changes like the digital a-devices do (yes, this can be less effectively approximated with b-sources by specifying the tripdv and tripdt parameters). The bottom line is that in LTspice it is always better to use a-devices whenever they fit the requirement.

* Replicating the MODULATOR a-device

In the netlist below a MODULATOR a-device is set up with 1V=100Hz and 0V=0Hz. It is then driven by V1 to sweep from 50Hz to 150Hz over 1 second. The frequency control input is V(f) and its swept outputs are V(sinA) and V(cosA).

.param fo=100
V1 f 0 PWL(0 0.5 1 1.5)
A1 f 0 0 0 0 cosA sinA 0 MODULATOR Mark={fo} Space=0

The b-sources below produce the same swept waveforms on V(sinB) and V(cosB). R1 and R2 duplicate the output impedance of the a-device and C1 and C2 are important for waveform fidelity and must be selected based on the frequency range of interest. They are needed to "trick" LTspice into producing enough waveform data points as the frequency increases. (Because b-sources are general purpose devices, it is often a problem to get them to correctly control step size.)

.param wo=2*Pi*fo
B1 0 sinB I=sin(wo*sdt(V(f))) Rpar=1 Cpar=1µ
B2 0 cosB I=cos(wo*sdt(V(f))) Rpar=1 Cpar=1µ

Building other functions with b-sources

* State Machine Helper Logic

The internal parameter “time” is available to state machine logic. It would very useful to create a new parameter called “StateTime” that would reset to zero at each state change. This would make it much more straightforward to create transitional “timer” states. Note, assuming the state value is made available as an output, it is possible using a b-source to create a "state-time" node that is based on the state value as follows:

B1 st 0 V=sdt(1,0,buf(abs(ddt(V(state))))) ; node "st" is "state time"

Another useful function might be to add a parameter called something like “StateChange” that would go high only during state transitions and be low otherwise. Note that this incorporated in the b-source function above as buf(abs(ddt(V(state)))).

* Up/Down Counter in a single b-source

B1 0 cnt I=sdt(0, round(V(cnt)+buf(ddt(V(up)))-buf(ddt(V(dn)))), buf(ddt(V(up)+V(dn))) ) Rpar=1 Cpar=10n

The b-source is a current source Nortonized into voltage source with a 1 ohm output impedance. The b-source parallel capacitor must be 2x the slowest rise time of the up/down inputs. In between up and down count pulses the sdt function holds the last count.

The up and down inputs, V(up) V(dn), use LTspice's standard 1 volt logic levels and trigger on the positive edges.

The cnt (count) output is an integer count that starts from zero. I haven't shown a reset, but it would be very easy to add.

* Other, Notes, etc.

I've got an equation in an arbitrary behavioral source that uses the variable *TIME* to plot.  What I can't figure out is how to cause this to repeat at some interval.

Time is a ramp that never resets.  What you need is a replacement for time in your equation that resets to zero at your repeat interval, i.e., a sawtooth function.

You could set up a standalone voltage source to make the sawtooth and replace "time" in your equation with the sawtooth node, "v(x)" or you could do this directly in the equation by replacing "time" with either of the following expressions:

  • time-int(time/m)*m ; where m is the modulus (repeat interval)
  • idtmod(1,0,m) ; integrates 1*dt with 0 offset at modulus m

If you want, you could define such expressions with a .function statement in order to keep your b-source equation from getting too messy.

.function a(m)= time-int(time/m)*m
.func b(m)= idtmod(1,0,m) ; .func is the short name
.param r=4 ; r is the actual reset modulus you wish to use

Now, within your b-source equation, you could replace each instance of "time" with either "a(r)" or "b(r)" (or change the names to suit what makes sense to you).


  • Explain how to make a discretized (pseudo-digital) source.
  • Demo various kinds of output limiters (tanh, limit, etc.) and explain why and where these are well worth using.
  • Show how to convert a BV source into an equivalent BI source (with parallel capacitor) and explain why this is generally a much better form to use.


blog comments powered by Disqus