Basic Usage Part3: Performing a simple BTI simulation Welcome to the third part of the tutorial "Basic Usage". In this part you will learn how to perform a simple BTI simulation for the device created previously. First, you will learn how to extract basic device characteristics to get familiar with the framework. Then, we will calculate the semiconductor potential, the potential profile across the stack and the threshold voltage shift \(\Delta V_\mathrm{th} \) for a selected stress condition.
Step 1: Print basic device properties For the beginning we want to print some basic properties of our device, to get familiar with the framework. We want to print the flatband voltage, the threshold voltage, the equivalent oxide thickness, the doping concentrations and the Fermi level in the channel. To retrieve the respective quantities from our device object we use the methods my_device.get_Vfb0, my_device.get_Vth0, my_device.EOT, my_device.Nd, my_device.Na and my_device.get_Fermilevel: # print basic device properties

print("Vfb0 = {:.3f} V".format(my_device.get_Vfb0(T=Temperature)))

print("Vth0 = {:.3f} V".format(my_device.get_Vth0(T=Temperature)))

print("EOT = {:.3f} nm".format(my_device.EOT * 1.0E9))

print("Nd = {:.3e} cm⁻³".format( * 1.0E-6))

print("Na = {:.3e} cm⁻³".format( * 1.0E-6))

print("Ef = {:.3f} eV".format(

for i, layer in enumerate(my_device.insulating_layers):
print("layer {}:".format(i), layer)
Note that there is a difference between my_device.get_Vth0 and my_device.Vth. The method my_device.get_Vth0 always returns the threshold voltage without trapped charge. Contrary, my_device.Vth returns the threshold voltage including the effect of whatever charge is trapped at the moment. If you followed the tutorial carefully, you should see the following output when running the Python snippet above: Vfb0 = 0.521 V
Vth0 = -0.405 V
EOT = 1.390 nm
Nd = 1.400e+17 cm⁻³
Na = 1.000e+10 cm⁻³
Ef = -4.189 eV
layer 0: 1.00nm SiO2
layer 1: 2.00nm HfO2
So far everything seems fine and we can continue with our simulation.

Step 2: Plot semiconductor potential Now that we checked our device we want to plot the semiconductor potential, i.e. the potential difference between the surface and the bulk of the semiconductor. Since most TCAD tools are capable of calculating the semiconductor potential, it is a good metric to compare other TCAD tools with Comphy. We want to calculate the semiconductor potential for gate voltages between -2.0 V and 2.0 V. To retreive the semiconductor potential from our device object we use the method my_device.get_semiconductor_potential. For plotting the semiconductor potential against the gate voltage we will use the Python package matplotlib: # plot semiconductor potential

Vg = np.linspace(-2.0, 2.0)
semiconductor_potential = \
[my_device.get_semiconductor_potential(Vg=Vappl, T=Temperature) for Vappl in Vg]

figure = plt.figure(figsize=(8.0, 6.0))
plt.plot(Vg, semiconductor_potential)
plt.title("Semiconductor potential")
plt.xlabel("Vg / V")
plt.ylabel("Semiconductor potential / eV")

The above Python snippet should produce the following image:
The plot shows the semiconductor potential, i.e. the potential difference between the surface and the bulk of the semiconductor, of our tutorial device.

Step 3: Calculate potential profile across the stack In our next step we want to retrieve the potential profile across the stack for a gate voltage of 1.0 V. For this purpose we use the method my_device.get_VB_and_CB, which returns the valence band edge, the conduction band edge, and the potential for an arbitrary array of x coordinates. # plot potential profile across the stack

x = np.linspace(-1.0E-9, my_device.oxide_thickness + 1.0E-9, 1000)
VB, CB, potential = my_device.get_VB_and_CB(x=x, T=Temperature, applyVg=True, Vg=1.0)

figure = plt.figure(figsize=(8.0, 6.0))
plt.title('Potential profile for Vg = 1V')

ax1 = plt.gca()
ax1.plot(x / 1.0e-9, VB, color='red', label='VB')
ax1.plot(x / 1.0e-9, CB, color='blue', label='CB')
ax1.legend(loc="lower right")
ax1.set_xlabel('Position / nm')
ax1.set_ylabel('Energy / eV')
ax2 = ax1.twinx()
ax2.plot(x / 1.0e-9, potential, color='green', label="potential")
ax2.set_ylabel('potential / V', color='green')
ax2.tick_params(axis='y', labelcolor='green')

Note that Comphy uses a surface-potential-based compact model and hence does not calculate the full potential profile in the semiconductor. The method my_device.get_VB_and_CB returns the potential at the semiconductor surface instead of the actual potential for x-values smaller than zero! As a result, the code snippet above leads to following image with a constant potential profile in the semiconductor bulk:
The plot shows the potential profile across the stack for our tutorial device. Note that the constant potential profile in the semiconductor does not correspond to reality, but indicates the level of the surface potential.

Step 4: Calculate shift of threshold voltage Finally we want to stress the device and calculate the shift of the threshold voltage for a selected stress pattern. For this purpose we start with the definition of the stress pattern. For demonstration we want to stress our device with a square signal with a frequency of (1.0 / 40.0) s for a total time of 250 seconds. During the application of the stress we will keep the device at a constant temperature of 300.0 K. # define stress pattern

# define time steps
time_per_step = 1
number_steps = 250
time = np.linspace(0.0, time_per_step * number_steps, number_steps)

# define constant temperature profile
temperature = np.full_like(time, 300.0)

# define gate voltage profile
min = -2.95
max = 0.5
frequency = 1.0 / 40.0
Vg = min + (max - min)*(-signal.square(2 * np.pi * frequency * time)/2.0 + 0.5)

# plot gate voltage profile
figure = plt.figure(figsize=(8.0, 6.0))
plt.plot(time, Vg)
plt.plot(time, Vg, 'o')
plt.title("stress waveform")
plt.xlabel("time / s")
plt.ylabel("gate_voltage / V")
The plot shows the stress waveform for the BTI simulation

Now we are ready to apply the stress pattern to our device: # apply stress pattern to device

# let's initialize the device with the initial voltage and temperature
my_device.initialize(Vg=Vg[0], T=temperature[0], savebands=False)

# Now, we create a list for the threshold voltage shifts
threshold_voltage_shifts = [0.0]

# Now, we loop over the scenario we have set out.
# We skip the first point as that is our initialization point.
for idx in range(1, len(time)):

# here we actually apply the voltage for a given timestep
dVth, dVth_contributions = my_device.apply_gate_voltage( time=time[idx] - time[idx-1],

# the method apply_gate_voltage() returns two objects:
# dVth: the total threshold voltage shift caused by all trapped charges
# dVth_contributions: a dict containing the different defect band contributions
# finally, we store the total threshold voltage shift, so we can plot it later

# Now that we have the data, let's plot it
figure = plt.figure(figsize=(8.0, 6.0))
plt.plot(time, threshold_voltage_shifts)
plt.title("BTI sequence")
plt.xlabel("time / s")
plt.ylabel("dVth / V")

The above Python snippet yields the following image:
The plot shows the shift of the threshold voltage of our device when stressed with the waveform displayed in Figure 3.0

Congratulations, you performed your first BTI simulation and calculated the threshold voltage shift \(\Delta V_\mathrm{th} \) with the Comphy framework. In the same way, you can calculate the threshold voltage shift for arbitrary stress profiles. At this point we recommend playing with the parameters of the trap bands to get a feeling for the effect of each parameter.